JavaでMonadをはじめからていねいに

by Yuji Yamamoto on August 28, 2016

Tagged as: Monad, Java, Haskell.

モナドについてSwiftで説明してみた」という記事などで指摘されているように、プログラマー向けにMonadを説明した記事はサンプルがHaskellで書かれていることが多いので辛いですよね。

HaskellではMonadは「ないと文字通りプログラムが書けないぐらい」大事なもので、入出力処理や例外処理、ダイナミックスコープのシミュレーション、非決定計算など、普通の関数では難しい、非常に様々な機能の実装に使用されています。
その一方、MonadはC言語のポインターに並んでHaskellを学ぶ上での障害である、なんて言われたりもするとおり、他言語ユーザーからHaskellを敬遠させる大きな要因の一つともなっています。

そんな悲しい現状を少しでも改善するために、上記の記事を参考にしつつ、HaskellよりもSwiftよりももっともっと広まっているであろう、Java (Java 8以降の知識を前提とします)でMonadを定義して説明してみたいと思います 1
これを通して、

と言った点も説明したいと思います!私自身Javaを勉強中なんで一石二鳥ですね!

あっ、「Java 8以降の知識を前提とします」とは言いましたが、ほかの似たような言語を知っていれば大体わかるようには書いたつもりなので、わかりにくいという場合はご連絡ください。
このページの一番下までスクロールして、私にメールかIssueを送っていただけるとありがたいです。

あるいはそんなのめんどくさいよ、という場合でもご安心ください。この文章はどちらかというと「Monadの詳しい仕組みを知りたい人向け」なので、書いてあることがわからなくても、とりあえずMonadを使うことはできるはずですから。

最初にまとめ (ここ以降がよくわかんなくても何となく知ってもらいたいこと)

目次

とりあえずJavaでの定義を。

手始めに、Monadの定義をJavaに翻訳してみましょう 2。 Haskellや圏論での呼び方も、より分かりやすくなるようもっと具体的な名前に差し替えます。

/**
 * 本当は一つのインターフェースにまとめたいのですが、
 * Javaのインターフェースの都合上やむを得ず分けています。
 * 本来は2つ合わせて初めてMonadと呼べる、という点をお忘れなく。
 */
interface Monad<T1> /* (1) */{

  <T2> Monad<T2> then(Function<T1, Monad<T2>> action /* (2) */);

  interface Return {
    /* (3) */
    <T> Monad<T> doNothingReturning(T value);
  }
}

Java 8がある程度普及した現在、関数を受けとる関数を表現するのがらくちんになりましたね!

番号を振ったところを解説しましょう。

Java固有の情報を抜いて、もうちょっとオブジェクト指向な言語全般で通じそうな日本語で言うと、次のように言い換えられます。

Monad interfaceを実装したクラスは、

いろいろ書きましたが、ここで特に大事なところは「thenというメソッドを実装しなければならない」と「doNothingReturningを実装しなければならない」の二点です!

具体的な実装その1 Maybe

重要な注意事項:
次の例を含め、この記事で紹介するMonadインターフェースを実装したクラスは、残念ながら全てコンパイルが通りません。Javaのインターフェースの仕様上仕方ないのです。
あくまでも説明のためのコードだということでご了承下さい。
ダウンキャストなどを使って一応コンパイルを通したソースがigrep/monad-in-java-sampleにあります。
また、もっとちゃんとしたMonadのJavaによる実装に興味がある方はhighjをご覧下さい(結構トリッキーな実装なので、今回の説明では使用しませんでした)。
重要な注意事項終わり

さて、それではMonadについてもうちょっと具体的なイメージを持っていただくために、前節で定義したMonadインターフェース(と、対応するMonad.Returnインターフェース)を実装したクラスの例を紹介しましょう。

class Maybe<T1> implements Monad<T1> {
    private final T1 x;

    Maybe(T1 x){
        this.x = x;
    }

    public <T2> Maybe<T2> then(Function<T1, Maybe<T2>> nextAction){
        if (x != null){
            return nextAction.apply(x);
        } else {
            return new Maybe<>(null);
        }
    }

    public static class Return implements Monad.Return {
        public <T> Maybe<T> doNothingReturning(T x){
            return new Maybe<>(x);
        }
    }
}

上記はthenにおいて、持っている値(x)がnullかどうかあらかじめ確認し、nullでなければ引数として渡した関数(nextAction)を実行します。
次の関数(nextAction)を実行する前に「nullでないか確認する」のが上記のMaybeクラスのthenの役割です。
この後のMonad則の説明でも触れますが、「Monadの値を作って返すだけで、それ以外のことはしない」のがdoNothingReturningのお約束なので、Monadインターフェースを実装するクラスをより明確に特徴付けるのは、thenメソッドの方です。
そのため繰り返しになりますが、次の関数(nextAction)を実行する前に「nullでないか確認する」のが上記のMaybeクラスの役割だ、と言い換えることもできるでしょう。

ところで、最近のJavaに慣れた方はMaybeがJava 8のOptionalとそっくりなものであることにお気づきかもしれません。
Maybe.thenOptional.flatMapに相当し、Maybe.Return.doNothingReturningOptional.ofNullableに相当する、と考えると、確かに!
事実、その前提でopen-jdkのOptional.javaと見比べてみると、上記はOptional.javaを大幅に簡略化したものと同等であることがわかります 4

Optionalと同じようなものなので特に目新しい部分はないかもしれませんが、上記のMaybeの使用例を示しましょう。

Foo maybeFoo1 = someMap.get(key1);
Maybe.Return r = new Maybe.Return();
Maybe<Bar> bar =
  r.doNothingReturning(maybeFoo1).then((foo1) -> {
    Foo maybeFoo2 = someMap.get(key2);
    return r.doNothingReturning(maybeFoo2).then((foo2) -> {
      Foo maybeFoo3 = someMap.get(key3);
      return r.doNothingReturning(maybeFoo3).then((foo3) -> {
        return r.doNothingReturning(foo1.doSomething(foo2, foo3));
      });
    });
  });

上記のように、nullを返すかもしれないメソッドをたくさん使うとき、Maybeを使えば、thennullチェックをお任せすることができます。
これでぬるぽともお別れだー!やったね!(^_-)

…と、言いたいところですが、元ネタのOptional.flatMapを使った場合と同様、ネストが深くって嫌ですねー。
これでは普通にif文を書いた方がまだ読みやすそうです。r.doNothingReturningなんて書く分タイプ数も多いですし。

r.doNothingReturningMaybe.Return r = new Maybe.Return();の部分が長いのは単なる名付け方の問題だから(もっと短い名前を普及させればよいのであって)目をつぶるとして、結局ネストが深くなってしまう問題はどうにかならないのでしょうか?
実はこれから説明する「Monad則」というのを利用すると、このような問題をクールに解決することができます!

Monad則って?

冒頭の通り、MonadインターフェースはthenメソッドとMonad.ReturndoNothingReturningメソッド、合わせて2つのメソッドを実装しなければなりません。
加えて、これらのメソッドは「Monad則」というある一定の規則を満たして実装しなければならないよう決まっています。
ここではこの「Monad則」についてもJavaに置き換えて紹介しましょう。
詳細は後で解説するので、ここではひとまず重要な部分のみチラ見せします↓。

// 下記の3組の式が**常に同じ意味となる**doNothingReturningとthen
// となっていなければ、thenとdoNothingReturningを実装していても、
// 「そのクラスはMonadである」とはいえません。
/* (A ) */ r.doNothingReturning(x).then((x) -> ax.apply(x))
           /* ↑と↓の意味が同じになること。以下同様。 */
           ax.apply(x)

/* (A') */ ax.apply(x).then((y) -> r.doNothingReturning(y))
           /* ↑と↓の意味が同じになること */
           ax.apply(x)

/* (B ) */ ax.apply(x).then((y) -> ay.apply(y).then((z) -> az.apply(z)))
           /* ↑と↓の意味が同じになること */
           ax.apply(x).then((y) -> ay.apply(y)).then((z) -> az.apply(z))

なんだかややこしいですね!

なんでそんなのがあるの?

そもそも、なぜこんな面倒な規則があるのでしょう?
この法則を満たすとMonadは非常に便利に使えるようになるのですが、それについては後ほど説明します。
その代わり、ここではJavaで言うところの似たような「規則」を紹介することで、「Monad則」の「立ち位置」みたいなものをお伝えしたいと思います。

例えばJavaな方にはお馴染みComparableインターフェースを実装したクラスは、以下のように振る舞うよう定められています。

x.compareTo(y)というメソッドを呼んだとき、

これによって、ユーザーが自由に「全順序性」を持ったクラスを定義して、比較したりソートしたりできるようになるのでした。

ちなみに、RubyやPerlのスペースシップ演算子(<=>)やPythonの(__cmp__)など、そっくりな振る舞いを要求するメソッドは他の言語にもしばしばあります。
以下の話はそれらに置き換えて読んでいただいても全く問題ありません。

そんなComparableですが、上記の規則を破って、下記のように振る舞うComparableがあったら嫌ですよね?

/**
 * 引数とレシーバーを逆にしても1(より大きい)が返る!
 * どっちが本当に大きいの?
 */
x.compareTo(y) // => 1
y.compareTo(x) // => 1

/**
 * 自分自身と比較しても-1(より小さい)が返る!
 * どうやったら等しくなるんだ!?
 */
z.compareTo(z) // => -1

継承の仕方などによっては、間違って上記のようなおかしな振る舞いのComparableを産み出してしまうことはあるでしょう。もちろん意図的にそうした実装を作ることも可能です。
このように不自然なComparableを作らないためには、Comparable実装する側が意識しなければなりません。間違った実装を作ってしまっても、コンパイラーが「間違ってるよ!」と教えてくれないのです。

Monad則もこれと同じようなものです。
Monadはそもそも圏論という数学の一分野で定義されたものなので、その定義に準拠したものであるためには、単純にMonadというインターフェースを実装するだけでなく、それに沿うよう気を付けて中身(thendoNothingReturning)を実装しなければなりません。
そして、繰り返しますがこのMonad則を満たすからこそみなさんはMonadを便利に使えるのです!(詳細は後述!)

で、Monad則ってどんなのさ?

これ以降を読む上でのヒント: Java 8におけるFunction型のオブジェクトは、applyメソッドを呼ぶことで実行できます。

前置きが長くなってしまいましたが、いよいよ「Monad則」をJavaで紹介しましょう!
先ほどは省略した変数の定義も含めて、下記の通りです 5

// 任意の型X, Y, Zの値をx, y, zとします。
X x;
Y y;
Z z;

// Monad.Returnをrとします
Monad.Return r = new SomeMonad.Return();

// それから、X, Y, Zを受け取って別のMonadの値を返す関数をax, ay, azとしましょう。
Function<X, Monad<Y>> ax = (x) -> ... ;
Function<Y, Monad<Z>> ay = (y) -> ... ;
Function<Z, Monad<A>> az = (z) -> ... ;

// 下記の3組の式が**常に同じ意味となる**doNothingReturningとthen
// となっていなければ、thenとdoNothingReturningを実装していても、
// 「そのクラスはMonadである」とはいえません。
/* (A ) */ r.doNothingReturning(x).then((x) -> ax.apply(x))
           /* ↑と↓の意味が同じになること。以下同様。 */
           ax.apply(x)

/* (A') */ ax.apply(x).then((y) -> r.doNothingReturning(y))
           /* ↑と↓の意味が同じになること */
           ax.apply(x)

/* (B ) */ ax.apply(x).then((y) -> ay.apply(y).then((z) -> az.apply(z)))
           /* ↑と↓の意味が同じになること */
           ax.apply(x).then((y) -> ay.apply(y)).then((z) -> az.apply(z))

各変数の定義まで書いてしまったので長ったらしくなってしまいましたが、一つずつ解説しましょう。

(A), (A’)について

最初の2つは、どちらかというとdoNothingReturningが守るべき性質についての式です。

第一に(A)では、xに対して「なにもしないで」Monadの値を作って(doNothingReturning(x))、それから(then)、更にMonadを返す関数axを実行する(ax.apply)ということは、単にaxを1回実行するのと同じことだ — つまり、doNothingReturningをどんなaxに実行しても、axを実行する(ax.apply(x))のと同じ、なのでdoNothingReturningなにもしないのと同等だ — ということです。

(A’)についても同様です。
xを受け取ってMonadを返す関数axを実行(ax.apply(x))して、それから(then)、「なにもしないで」Monadの値を作る(doNothingReturning)ことは、単にaxを1回実行するのと同じことだ — つまり、doNothingReturningをどんなaxに実行しても、axを実行するのと同じ、なのでdoNothingReturningなにもしないのと同等だ — ということです。

いずれにおいても、doNothingReturningMonadの値を作って返すだけで、実質なにもしないという点が、ここでは重要です。 「そんなの役に立つの?」と思われるかもしれません。
とりあえずは「定義上そう決まっているのでそういうものだ」とご理解しておいてください。ここから先の例でなんとなく伝われば幸いです。

(B)について

(B)はちょっと複雑ですね。
集中するために該当の部分だけ持ってきましょう。

   ax.apply(x).then((y) -> ay.apply(y).then((z) -> az.apply(z)))
        /* ↑と↓の意味が同じになること */
   ax.apply(x).then((y) -> ay.apply(y)).then((z) -> az.apply(z))

ぱっと見どこが違うのかわかりませんね!違うのは↓に示す部分だけです!

   ax.apply(x).then((y) -> ay.apply(y).then((z) -> az.apply(z)))
                                      ^ この.then((z) -> az.apply(z))が...
   ax.apply(x).then((y) -> ay.apply(y)).then((z) -> az.apply(z))
                                       ^ カッコ()を突き破って、ここに出された!

上記の通り、二つめのthen、すなわちay.applyの後ろにあるthen((z) -> az.apply(z))が、一つめのthen、つまりax.apply(x).thenに渡したラムダ式から、括り出せるようになっていなければならない、ということです。

この規則によって、ネストを一段平たくできます。
then()のカッコの中にインデントを加えてみるとよくわかるでしょう。

ax.apply(x).then(
  (y) -> ay.apply(y).then(
    (z) -> az.apply(z)
  )
)
/* ↑と↓の意味が同じになること */
ax.apply(x).then(
  (y) -> ay.apply(y)
).then(
  (z) -> az.apply(z)
)

さて、この規則をもうちょっとくだけた日本語に言い換えると、

常に同じ意味でないといけない、と解釈できます。
この解釈だと「そんなの当たり前じゃないの?」と感じられるかもしれません。
それぐらい直感的な仕様を守ってくださいね、というのが「Monad則」の正体だ、と考えていただければしっくりくるでしょうか?
Comparablexが常にx.compareTo(x) == 0であってほしいのと同じことです。

「Monad則」(B)のMaybeへの応用

さて、Monad則の話が長くなってしまいましたが、Maybeのお話に帰りましょう。
下記のようなMaybe Monadの例においてMonad則を応用すると、「ネストが深くって嫌ですねー」という問題をクールに解決することができる、というお話でした。

Foo maybeFoo1 = someMap.get(key1);
Maybe.Return r = new Maybe.Return();
Maybe<Bar> bar =
    r.doNothingReturning(maybeFoo1).then((foo1) -> {
        Foo maybeFoo2 = someMap.get(key2);
        return r.doNothingReturning(maybeFoo2).then((foo2) -> {
            Foo maybeFoo3 = someMap.get(key3);
            return r.doNothingReturning(maybeFoo3).then((foo3) -> {
                r.doNothingReturning(foo1.doSomething(foo2, foo3));
            });
        });
    });

解決するヒントとして、「Monad則」の(B)を思い出してみましょう。

   ax.apply(x).then((y) -> ay.apply(y).then((z) -> az.apply(az)))
   ax.apply(x).then((y) -> ay.apply(y)).then((z) -> az.apply(az))

この規則は上の図のように、二つめのthen、すなわちay.applyの後ろにあるthen((z) -> az.apply(z))を、一つめのthen、つまりax.apply(x).thenに渡したラムダ式からくくり出すことで、ネストを一段平たくすることができる、というものでした。
おっ。ということは今回のケースにも適用できるかもしれませんよ!やってみましょう!

Foo maybeFoo1;
Foo foo1, Foo foo2, Foo foo3;
Maybe.Return r = new Maybe.Return();

maybeFoo1 = someMap.get(key1);
Maybe<Bar> bar =
    r.doNothingReturning(maybeFoo1).then((foo1Arg) -> {
        foo1 = foo1Arg;
        return r.doNothingReturning(someMap.get(key2));
    }).then((foo2Arg) -> { // <- このラムダ式と、
        foo2 = foo2Arg;
        return r.doNothingReturning(someMap.get(key3));
    }).then((foo3Arg) -> { // <- このラムダ式がくくりだされた。
        foo3 = foo3Arg;
        return r.doNothingReturning(foo1.doSomething(foo2, foo3));
    });

よし、これならネストが減ってちょっと見やすくなった…? と、思いきや、今度はコンパイルエラーです… (>_<)
Java 8のラムダ式は、ラムダ式の中でローカル変数を書き換えることができないのでしたorz
残念ながらこの問題は、現在のJavaではどうしようもありません。
しかもいずれにしてもいちいちfoo1 = foo1Argみたいな代入が必要だったりで、結局面倒くさいですよね。

実はHaskellであれば、上記のような書き換えを自動で行い、自然な見た目にしてくれるシンタックスシュガー(糖衣構文)があります。
それが次に示す「do記法」と呼ばれるものです。

do記法

本記事はあくまでもJavaでMonadを説明する記事ですので、ここではその「do記法」を仮にJavaに導入した場合を想像して、先程の例がどのように書き換えられるか示しましょう。

Maybe.Return r = new Maybe.Return();

Maybe<Bar> bar = do {
    Foo foo1 <- r.doNothingReturning(someMap.get(key1));
    Foo foo2 <- r.doNothingReturning(someMap.get(key2));
    Foo foo3 <- r.doNothingReturning(someMap.get(key3));
    r.doNothingReturning(foo1.doSomething(foo2, foo3));
};

おお、doNothingReturningという長ったらしい名前さえどうにかなれば、もはや普通のJavaと区別がつかないくらい自然じゃありませんか!

「一体どこがどう糖衣構文で簡略化されたんだ?」という疑問にお答えしましょう。
この場合、元のコードのthenメソッドの呼び出しが、細い矢印 <- の箇所に置き換わったと考えると分かりやすいでしょう。
thenメソッドが「引数として渡された関数」に渡す引数(上記の場合foo1, foo2, foo3)についてnullかどうか確認していたのを、do記法では細い矢印 <-foo1, foo2, foo3に代入する前にnullかどうか確認するようになったのです。
このような自動的な書き換えがあるからこそ、HaskellではMonadが思いの外便利に使えるのです。どうです?Javaにもちょっと欲しくなりませんか?

do記法とMonad則 (B)

ついでに、この「do記法」とMonad則(B)ののっぴきならない関係を示すことで、Monad則(B)を守ることの重要性についてもお話ししましょう。
復習のためにもう一度↓に持ってきました。

    ax.apply(x).then((y) -> ay.apply(y).then((z) -> az.apply(z)))
        /* ↑と↓の意味が同じになること */
    ax.apply(x).then((y) -> ay.apply(y)).then((z) -> az.apply(z))

上記の通りMonad則(B)は、二つめのthenの引数におけるthen((z) -> az.apply(z))の部分が、一つめのthen、つまりax.apply(x).thenに渡したラムダ式から、処理の意味を変えずに、括り出せるようになっていなければならない、ということでした。 これによってネストを一段平たくできるのでしたね。

この規則は、先程のdo記法を用いると、次のように表現することもできます。

do {
    y <- ax.apply(x);
    do { // このdoブロック
        z <- ay.apply(y);
        az.apply(z);
    };
};

// このdoブロック と記されたdoブロックを引き剥がして、

do {
    y <- ax.apply(x);
    z <- ay.apply(y);
    az.apply(z);
};

と必ず(処理の意味を変えずに)書き換えられなければならない、ということです。
あるいは逆に、doブロックの範囲を上にずらして、

do {
    z <- do {
        y <- ax.apply(x);
        ay.apply(y);
    };
    az.apply(z);
};

のようにも書き換えられなければならない、とも言えます。

普通こんな無意味な書き換えはしないだろ、と思われるかもしれません。
ところがリファクタリングしたくなったときなど、do記法で並べた各行を、他のメソッドとして切り出したくなったときはいかがでしょう?

先程の例で申しますと、

do {
    y <- ax.apply(x);
    z <- ay.apply(y);
    az.apply(z);
};

↑のを塗った箇所だけ切り出して

public SomeMonad<Z> extractedMethod(X x){
    return do {
        y <- ax.apply(x);
        ay.apply(y);
    };
}

do {
    z <- extractedMethod(x);
    az.apply(z);
};

と書きたくなったり、
あるいは、

do {
    y <- ax.apply(x);
    z <- ay.apply(y);
    az.apply(z);
};

から、を塗った箇所だけを切り出して

public SomeMonad<A> extractedMethod(Y y){
    return do {
        z <- ax.apply(y);
        az.apply(z);
    };
}

do {
    y <- ax.apply(x);
    extractedMethod(y);
};

と書きたくなるかもしれません。

このように、do記法のどんなところから切り出しを行っても意味が変わらないようにするには、Monad則(B)を満たして、入れ子を関係を気にしなくてもいいようにすることが、必要不可欠なのです。

ここまでのまとめ

長くなりましたがここまでのまとめです。

具体的な実装 その2 State

さて、do記法のすごさを分かっていただけたところ(まだわからない場合、このページの一番下までスクロールして、私にメールかIssueを送ってください!)で、Monadの別の例を紹介しましょう。
今度はJavaではあまり役に立たないとは思いますが、純粋な関数のみを使っているのに、あたかも命令型スタイルで書かれているかのように見せる、魔法のようなMonadです。

ちょっとMonadじゃないクラスがいくつか出てきますが、Stateモナドが依存しているので、どうかご了承下さい。
もちろん後で解説しますので…。

// 状態を書き換えた結果を表すValue Object
class MutationResult<T, S> {
    public final S newState;
    public final T value;

    MutationResult(S newState, T value){
        this.newState = newState;
        this.value = value;
    }
}

// Stateモナドの実装
class State<S, T1> implements Monad<T1> {
    // 状態の書き換えをシミュレートするための関数オブジェクト
    public final Function<S, MutationResult<T1, S>> mutator;

    State(Function<S, MutationResult<T1, S>> mutator){
        this.mutator = mutator;
    }

    public <T2> State<S, T2> then(Function<T1, State<S, T2>> nextAction){
        Function<S, MutationResult<T2, S>> composedMutator = (oldState) -> {
            MutationResult<T1, S> result = this.mutator.apply(oldState);
            State<S, T2> other = nextAction.apply(result.value);
            return other.mutator.apply(result.newState);
        };
        return new State<>(composedMutator);
    }

    public static class Return<S> implements Monad.Return {
        public <T> State<S, T> doNothingReturning(T value){
            return new State<>(
                (nonMutatedState) -> new MutationResult<>(nonMutatedState, value)
            );
        }
    }
}

うーん、肝心のStateクラス以外のものがあったり、その上結構煩雑ですね…(^-^;
これだけでは何のこっちゃと感じられる方も多いと思うので、ちょっとずつ解説しましょう。

そもそも、「純粋な関数」のみで命令型スタイルに見せる、とは?

ここで言う「命令型スタイル」というのが「状態を書き換えることで結果を作る」プログラミングスタイルだとして、いわゆる「関数型プログラミング」 — つまり純粋な関数のみを使用したスタイルでは、どうやって状態の書き換えを表現するのでしょう?

純粋な関数は、関数が返した値を使用しない限り、関数の外部へ影響を与えることができません。ということで素直に「古い状態を受け取って、新しい状態を返す」関数として表現してみましょう。
JavaのFunctionで言うと↓のような感じ。

Function<S, S> mutator;

しかしこれでは、「状態を書き換えたと同時に、別の値も返したい!」という時に不便ですよね。
例えば、JavaのMapremoveメソッドは、呼び出し元のMapの要素を取り除くことで状態を書き換えると同時に、取り除いた要素を返しますよね。
そうした振る舞いをシミュレートするためには、「古い状態を受け取って、新しい状態を返す」だけでなく、「一緒に返す別の値」も返せるようにする必要があるのです。

と、いうわけで出来たのが先ほど挙げましたMutationResultFunction<S, MutationResult<T1, S>> mutatorです。
↓にもう一度載せておきます。

// 状態を書き換えた結果を表すValue Object
class MutationResult<T, S> {
    public final S newState; // 新しい状態
    public final T value;    // 一緒に返す値

    MutationResult(S newState, T value){
        this.newState = newState;
        this.value = value;
    }
}

class State<S, T1> implements Monad<T1> {
    // 状態の書き換えをシミュレートするための関数オブジェクト
    // Sという型の新しい状態とともに、Tという型の書き換えた結果も返す
    public final Function<S, MutationResult<T1, S>> mutator;
}

「状態を書き換えたと同時に、別の値も返したい!」というニーズを満たすため、「新しい状態」と「一緒に返す別の値」とのペアを表すクラス MutationResult を作りました。Function<S, MutationResult<T1, S>>は古い状態を受け取って、MutationResultのインスタンスを返すだけです。

なお、これ以降Function<S, MutationResult<T1, S>>のような型の関数を、単純に「ST1mutator」と呼ぶことにします。Function<S, MutationResult<T1, S>>とか「古い状態を受け取って、新しい状態と一緒に別の値を返す関数」では単純に長いので。

Stateのコンストラクターがやっていること

class State<S, T1> implements Monad<T1> {
    public final Function<S, MutationResult<T1, S>> mutator;

    State(Function<S, MutationResult<T1, S>> mutator){
        this.mutator = mutator;
    }
    // ...
}

コンストラクターでやっていることは、単に引数をインスタンス変数にいれるだけのボイラープレートなコードです。
ここで注目していただきたいのは、State<S, T1>は、Function<S, MutationResult<T1, S>>、すなわち「ST1のmutator」をラップしただけのクラスであることです。
State Monadはこれにちょっと色をつけるだけで、あたかも命令型スタイルで書いているかのように錯覚させることができるのです!

State.Return.doNothingReturningがやっていること

難しいState.thenは後回しにして、先にdoNothingReturningを解説しましょう。

class State<S, T1> implements Monad<T1> {

    // ...

    class Return<S> implements Monad.Return {
      public <T> State<S, T> doNothingReturning(T value){
        return new State<>(
            (nonMutatedState) -> new MutationResult<>(nonMutatedState, value)
        );
      }
    }
}

Monad則上、doNothingReturningはMonadの値を作って返すだけで、それ以外のことはしてはいけません。
このルールに加えて、先ほど説明した、「State<S, T1>は、ST1のmutator、すなわち、書き換えた状態と、一緒に別の値を返す関数をラップしただけのクラスである」という事実を思い出してください。
それでは「書き換えた状態と、一緒に別の値を返す関数」のうち、「何もしない」ものを作るにはどうすればよいでしょう?
状態を書き換えないで、そのまま返す」というのが正解です。
State.Return.doNothingReturningは、
(nonMutatedState) -> new MutationResult<>(nonMutatedState, value) という、

の関数を作っているのです。

繰り返しになりますが、State.Return.doNothingReturningは、「受け取った状態を全く書き換えないで返す関数」を作る、ただそれだけのことをしています。

State.thenでやっていること

それでは本題、Monadインターフェースにとって最も重要なメソッド、Statethenの詳細を解説しましょう。
説明のために番号をコメントにふって載せますね。↓

class State<S, T1> implements Monad<T1> {
  public final Function<S, MutationResult<T1, S>> mutator;
  // ...
  <T2> State<S, T2> then(Function<T1, State<S, T2>> nextAction){
    // (1) 新しくmutatorを作って
    Function<S, MutationResult<T2, S>> composedMutator = (oldState) -> {
      // (2) 古い状態を書き換え、結果を受けとる
      MutationResult<T1, S> result = this.mutator.apply(oldState);
      // (3) (2)で状態を書き換えた際に一緒に返した値を、次のState Monadを作る関数へ渡す。
      State<S, T2> other = nextAction.apply(result.value);
      // (4) (2)で状態を書き換えた状態をまた処理する
      return other.mutator.apply(result.newState);
    };
    // (1) また別のState Monadとしてくるみなおす。
    return new State<>(composedMutator);
  }
  // ...
}

まずは(1)をつけた2ヶ所に注目してください。 コメントに書いた通りですが、State.thenメソッドでは新しいmutatorを作って、またStateオブジェクトとしてくるみなおしています。
後半の、Stateオブジェクトとしてくるみなおす部分は、型を合わせるためだけのものです。
なのでState Monadのthenは(そしてその糖衣構文であるdoも)、「mutatorを新しく作ることで、いい感じに組み合わせる」ためにある、言う点を覚えておいてください。

(2)は最初の状態書き換えです。結果をMutationResultとして返すことで「書き換えた後の状態」と「一緒に返す値」を、(3)(4)でそれぞれを利用できるようにします。

(3)では、(2)で手に入れた「書き換えた結果(MutationResult)」のうち、「一緒に返す値」を利用して、新しいState Monadを作ります。

(4)では、(3)で手に入れたState Monadのmutatorを利用して、(2)で書き換えた状態(MutationResult.newState)を更に書き換えます。
そうして(4)で返されたMutationResultが、State.thenメソッド1回でできる最終的な状態の書き換えの結果となります。

さて、(1)(4)まで色々やりましたが、結局のところ、State.thenは全体として何をしているのでしょうか。あるいは、mutatorをどのように「いい感じに」組み合わせているのでしょうか?
それは、最初のmutator(this.mutator)でmutateしてから、引数で渡されたmutator(other.mutator)でmutateするように組み合わせている、ただそれだけです。
ややこしいのは途中でMutationResultを「書き換えた状態」と「一緒に返す値」に分解しているところです。
上記のコードのstateを処理している箇所のみに注目してみてください。
oldStatethis.mutator.applyした結果をother.mutator.applyしているだけ、ということにお気づきでしょうか?

更に簡潔にまとめますと、State.then状態を書き換える関数(mutator)を、続けて実行するよう組み合わせるということをしています。

具体的な使い方

説明が長くなってしまいましたが、いよいよState Monadの使用例を示しましょう。
ひとまずここまで挙げたメソッドを組み合わせて、実用上ないと困る、ユーティリティメソッドを作ってみます。

class State<S, T1> implements Monad<T1> {
    /* ... */

    // 引数で与えた状態に書き換える
    public static <S> State<S, Void> put(S newState){
        Function<S, MutationResult<Void, S>> putter =
            (ignoredState) -> new MutationResult<>(newState, null);
        return new State<>(putter);
    }

    // 現在の状態を取得する
    public static <S> State<S, S> get() {
        Function<S, MutationResult<S, S>> getter =
            (currentState) -> new MutationResult<>(currentState, currentState);
        return new State<>(getter);
    }

    // 引数で与えた関数で、状態を書き換える
    public static <S> State<S, Void> modify(Function<S, S> simpleMutator){
        Function<S, MutationResult<Void, S>> modifier =
            (currentState) -> {
                S newState = simpleMutator.apply(currentState);
                return new MutationResult<>(newState, null);
            };
        return new State<>(modifier);
    }
    /* ... */
}

まずはState.putについて。
State.putは新しい状態(newState)を引数として受け取り、前の状態(ignoredState)を使わず、そのまま新しい状態(newState)で書き換えるmutatorを返します。
new MutationResult<>()の第一引数が「書き換えた新しい状態」で、第二引数が「一緒に返す値」であることを思い出してください。
State.putメソッドでは「書き換えた新しい状態」として受け取ったnewStateをそのまま渡し、「一緒に返す値」としてnull、すなわち「一緒に返す値」がないものとしています。
これは、Javaで例えるなら戻り値がvoidのメソッドのようなものです。戻り値の型がState<S, Void>になっている通りです。

続いてState.getについて。
State.getがラップして返すmutatorは、引数として受けとるcurrentStateをそっくりそのままnew MutationResult<>()の第一引数として渡して返します。
状態を一切書き換えないのです。あくまでもgetするだけですからね。
そして状態を書き換えた際に「一緒に返す値」であるnew MutationResult<>()の第二引数にも、currentStateをそのまま渡しています。
これにより、State.get()を使うと、現在の状態をそのまま取得することができます。

最後にState.modifyについて。
State.putと同様に、State.modifyも戻り値の型はState<S, Void>です。すなわち状態を書き換えるだけで、「一緒に返す値」を返しません。
State.putが新しい状態を引数から受け取っていたのに対して、State.modifyでは、前の状態に(第一引数として受け取った)関数を適用することで、新しい状態を作ります。
simpleMutatorと呼んでいる通り第一引数の関数は、単純に書き換える前の状態を受け取って、変更後の状態を返す関数なのです。

State Monadは実用上、ここまで紹介したユーティリティメソッド、State.putState.getState.modifyを中心に利用した方が、直感的で使いやすいかと思います。
と、いうわけで上記のState.getState.putを利用して、StringBuilderを真似したような例を紹介しましょう。

State<String, Void> builder =
          // 状態を取得して、次の関数に渡す。
    State.get()
          // ↓getから渡された状態に処理を加え、書き戻す。
         .then((currentString) -> State.put(currentString + "one!"))
         .then((_null /* どうせ State.put が返す(次の関数に渡す)値はnullなので無視する */) ->
             // ↓また状態を取得する。今度は前の行でputした後の状態が返ってくる。
             State.get()
         )
         // あとはその繰り返し。
         .then((currentString) -> State.put(currentString + "two!"))
         .then((_null) -> State.get())
         .then((currentString) -> State.put(currentString + "three!"));

System.out.println(
    // 初期値を渡すことで、初期値から書き換えていった結果が得られる
    // この場合 "zero! one!two!three!" と出力される
    builder.mutator.apply("zero! ").newState
);

あるいは、State.modifyを利用して次のように書くこともできます。

State<String, Void> builder =
    State.modify((currentString) -> currentString + "one!")
         .then((_null) -> State.modify((currentString) -> currentString + "two!"))
         .then((_null) -> State.modify((currentString) -> currentString + "three!"));

今回のケースではこちらの方がすっきりして分かりやすいでしょうね。

何が嬉しいの?

さて、ここまで長々とState Monadについて説明しましたが、いったいこんなものを作って何が嬉しいのでしょう?
この章の冒頭でも触れた通り正直に言って、Javaではあまり役に立ちません。
特に上記の例に限って言えば、普通にStringBuilderを使って書いた方が読みやすさの観点からも実行効率の観点からも明らかに優れています。
そもそもJavaでは変数もオブジェクトもミュータブル(変更可能)なのですから、敢えてこんなものを作る必要はないですしね。

一方、これはHaskellでは結構役に立ちます。
Haskellでは、型によって関数のできることに厳格な制約がかけられます(主にデバッグのために使われる例外はあります)。
通常の関数は「決められた型の値を受け取って、決められた型の値を返す」以外に、全く何も出来ません。
Javaではクラスの設計によって、利用者がそのクラスを使ってできることに制限を加えたりできますが、ある意味、Haskellでは言語仕様レベルでそのような厳重な制限がかけられています。
このことは一見厳しすぎるように見えるかも知れませんが、実際には極めて大きなアドバンテージでもあります。
本記事の守備範囲から離れてしまうので、お話しできないのが残念なくらいには。

しかし実際問題として、こうした制約が面倒に感じられることも多々あります。
よくあるのは、値を返しては別の関数に渡し、また値を返しては別の関数に渡し…というのを繰り返したいケースです。
こうしたケースでは、新しい変数名を考えるのもかったるいくらい、冗長な記述になってしまうでしょう。
それから、型を定義して表現したい対象について、命令型プログラミングスタイルで考えた方が相性がよいように感じられる場合、というのもあるでしょうね。

HaskellのState Monadは、そうしたケースにおいて、ちょうどよい「抜け道」を空けてくれます。
先程の例をよく振り返っていただきたいのですが、変数 builder を作るのに使うラムダ式や、State Monadの各種メソッドに至るまで、Java組み込みのSystem.out.println以外、すべて純粋な関数のみで構成されていることにお気づきでしょうか?
builder.mutator.apply("zero! ")してからnewStateを得るまでの全ての関数が、「戻り値が引数によってのみ定まり、『戻り値を返す』以外になにもしない」「純粋な関数」でできているのです。

このようにState Monadは、純粋な関数のみを使っているのにあたかも命令型スタイルで書かれているかのように見せる、魔法のようなMonadとなのです。
do記法を使って書き換えると、そのことをもっと実感できるでしょう。

ただし、先程のMaybe Monadの例から、もう少しdo記法でできることを増やしましょう。
State Monadの例において、

.then((currentString) -> State.put(currentString + "one!"))
.then((_null /* どうせ State.put が返す(次の関数に渡す)値はnullなので無視する */) ->
    // ↓また状態を取得する。
    State.get()
)

とか、

.then((_null) -> State.modify((currentString) -> currentString + "three!"));

などのthenメソッドでは、コメントにも書いた通り、ラムダ式の第1引数(_null)が無視されています。
これは、State.putメソッドやState.modifyメソッドが「状態を書き換えた際に、一緒に返す値」としてnullを返しているため、役立たずなためです。
そうしたケースをより簡単に書けるように、thenメソッドに渡す関数の、第1引数にあたる箇所を省略できるようにしましょう。
その結果がこれ↓です。

State<String, Void> builder = do {
    currentString <- State.get();
    State.put(currentString + "one!");

    currentString <- State.get();
    State.put(currentString + "two!");

    currentString <- State.get();
    State.put(currentString + "three!");
};
State<String, Void> builder = do {
    State.modify((currentString) -> currentString + "one!");
    State.modify((currentString) -> currentString + "two!");
    State.modify((currentString) -> currentString + "three!");
};

ますます普通の手続き型スタイルっぽくなりましたね!
State.putしたりState.modifyすることによって書き換えられる新しい状態が、doブロックの中で直接現れず、doブロック全体で共有されるようになります。
State.getの結果か、State.modifyに渡したラムダ式の中からでしか、現在の状態は見えません。「現在の状態」はdoブロックの中で暗黙に共有されるようになるのです。

さて、この場合どう糖衣構文で簡略化されたのでしょう?
元のコードのthenメソッドの呼び出しが、セミコロン「;」、つまり文の終端に置き換わったと考えると分かりやすいです。
State.modifyなどが返したState Monadを、セミコロン 「;」 がthenメソッドを呼び出して処理しているのです。具体的には、繰り返しになりますが、「状態を書き換える関数(mutator)を、続けて実行するよう組み合わせて」いるのです。

これはMonadを「プログラマブルセミコロン」として例える主張ともぴったり一致します。
上記の主張を借りるならば、Monadは(+などの演算子と同様に)セミコロンをオーバーロードできるようにしてくれるのです。

Stateまとめ

長くなってしまいましたのでここで一旦まとめます。もう次が「最後に」なんですけどね。

最後に

Monadはセミコロン、つまり文と文を繋ぐもの、「各文の間にあるもの」を変えることを可能にします。
Maybe Monadの例ではそのことに触れず「細い矢印 <- の箇所に置き換わった」と説明しましたが、「セミコロンの箇所でnullチェックをしている」と解釈しても今ならそんなに違和感ありませんよね?

また、Monadの説明でよく出てくる「文脈」6という概念はここで言う「各文の間にあるもの」と捉えていただくと、もっといろいろしっくり来るのではないでしょうか。
例えばMaybe Monadであれば、各文の間に「結果がNothingでないかどうかチェックし、Nothingであれば次の文の実行をやめる」という処理がありますし、State Monadであれば、「前の文で書き換えた状態を、次の文に渡す」という処理があります。 他のMonadをご存じであれば、それも考えてみてください。各Monadへの理解がいっそう深まることでしょう。

繰り返しになりますが、最後にまとめをもう一度挙げておきます。

それから、レビューにご協力いただいたみなさん、特にlotz84さんやitkrt2yさん、お忙しい中ありがとうございました!
突然のお願いにもかかわらず真摯な指摘をいただけました。おかげでもっとNon-Haskeller目線にたった記事が書けたと思います!

それではMonadでHappy Hacking!

参考


  1. 念のため補足しますと、ここで説明するMonadはあくまでもプログラミングの世界、特にHaskellで使われているあの「Monad」の話です。あまり触れられませんが、圏論の世界で言う「モナド」とは目的も定義も厳密には異なります(どちらかというと圏論の「モナド」の方が定義が広いです)。アルファベットで「Monad」と表記しているのも、Haskellで使われているあの「Monad」がソースコードの中で「Monad」と表記されているためです。

  2. Haskellに詳しい方はすぐに察することができるかと思いますが、MonadthenメソッドがHaskellでいうところの>>= (bind)に相当し、Monad.ReturndoNothingReturningメソッドがreturnに相当します。

  3. 参照型と値型の区別はこの際お見逃しください。やっぱりJavaで厳密にMonadを表現するのは難しい。

  4. ちなみに、HaskellのMaybeとはちょっと異なる振る舞いをします。 HaskellのMaybeは、returnnullに相当するNothingを返すことはないからです。 nullNothingは実際には大きく異なるものなので単純に比較できませんが。詳しくはHaskellを勉強してみて確かめましょう!

  5. Javaの関数オブジェクトやラムダ式の仕様上、本記事のMonad則で現れる(x) -> ax.apply(x)のような式は、本当は単にaxと書き換えることができます。
    それから、(y) -> r.doNothingReturning(y)も、単にr::doNothingReturningと書くだけでOKです。
    ですが説明しやすさのために敢えて冗長な記述をしております。ご容赦を。

  6. 例えば関数プログラミング実践入門の211ページの「本章で説明するモナドは、端的には『文脈を伴う計算』同士を組み合わせ可能にするしくみです」という文。


I'm a Haskeller キャッシングの返済の仕方