純粋関数型Ja婆婆

「[湯婆婆 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/yubaba)」の 20 日目の記事・裏面です。

表面は[こちら](https://qiita.com/BlueRayi/items/655ff29d01efc3f8f585)。 つっても別に表面とは関係ないことやってるんですけどね。

## なにをするつもり?

アドベントカレンダー横断チャレンジということで(?)「[闇の魔術に対する防衛術 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/yaminomajutu)」の [10 日目の記事](https://qiita.com/BlueRayi/items/bff5b808c47ed4bf1596)で作ってみた「純粋関数型 Java」によって [Ja 婆婆](https://qiita.com/Nemesis/items/c7192a7c510788d2cba2)を実装しようという試みです。

いわばピュアな Ja 婆婆です。 もうピュアっピュアです。

## ネイティブな乱数

乱数の Leaves は未実装だったので簡単に実装しておきます。

```java
package aquapura.leaves;

import java.util.Random;

import aquapura.util.Supplier;

public class Rand {
    private Rand() {}

    private static final Random random = new Random();

    public static Supplier<Integer> getInt = random::nextInt;

    public static Supplier<Integer> getIntBetween(int start, int end) {
        return () -> random.nextInt(start + end) - start;
    }
}
```

スレッドセーフ? やつは死んだよ。

## 実装

ベタ書きバージョンは以下。

```java
package main;

import aquapura.leaves.IO;
import aquapura.leaves.Rand;
import aquapura.util.Supplier;

public class Main {
    private Main() {}

    public static Supplier<Void> main(String[] args) {
        return IO.printLine("契約書だよ。そこに名前を書きな。")
            .andThen(IO.scanLine)
            .flatMap(name -> IO.printLine("フン。" + name + "というのかい。贅沢な名だねぇ。").andThenOf(name))
            .flatMap(name -> Rand.getIntBetween(0, name.length()).map(newNameIndex -> name.substring(newNameIndex, newNameIndex + 1)))
            .flatMap(newName -> IO.printLine("今からお前の名前は" + newName + "だ。いいかい、" + newName + "だよ。分かったら返事をするんだ、" + newName + "!!"));
  }
}
```

湯婆婆オブジェクトに処理を委任したのが以下。

```java
package main;

import aquapura.leaves.IO;
import aquapura.leaves.Rand;
import aquapura.util.Supplier;

public class Yubaba {
	public final Supplier<String> queryName = IO.printLine("契約書だよ。そこに名前を書きな。").andThen(IO.scanLine);

	public Supplier<String> sayOldName(String name) {
		return IO.printLine("フン。" + name + "というのかい。贅沢な名だねぇ。").andThenOf(name);
	}

	public Supplier<String> pinchName(String name) {
		return Rand.getIntBetween(0, name.length()).map(newNameIndex -> name.substring(newNameIndex, newNameIndex + 1));
	}

	public Supplier<String> sayNewName(String name) {
		return IO.printLine("今からお前の名前は" + name + "だ。いいかい、" + name + "だよ。分かったら返事をするんだ、" + name + "!!").andThenOf(name);
	}
}
```

```java
package main;

import aquapura.util.Supplier;

public class Main {
    private Main() {}

    public static Supplier<Void> main(String[] args) {
        final Yubaba yubaba = new Yubaba();
        return yubaba.queryName
            .flatMap(yubaba::sayOldName)
            .flatMap(yubaba::pinchName)
            .flatMap(yubaba::sayNewName)
            .toVoid();
    }
}
```

~~自分で書いておいて何なんですが`final Yubaba yubaba = new Yubaba();`で草生え散らかしました。 別にユーティリティクラスでよくね?とも思いますがこれがやりたかった。~~

`pinchName`の定義がややこしいですね。 以前の名前と乱数によって得られる値の両方を持ち回らなければいけないので入れ子になってしまいます。 do 式ほしいえぐなぁ。

実行するとこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241027/2a80b2b6-db90-c7a3-2c92-e572c90ca4b5.png)

まあオリジナルと同じ動作しかしませんね。

空文字列を入れると当然クラッシュします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241027/0ca89e9d-8177-ff0e-87cd-c160e021bbfa.png)

成し遂げたぜ。