Revision 7 as of 2011-05-28 06:02:02

Clear message
Locked History Actions

scala/monad

モナド

※以下は間違いの可能性があるので信用しないように。これは単なる学習メモです。

第一印象

モナドとは、連続して計算を行うパイプラインを簡単に作成するためのものである。古今東西同様の仕組みはいろいろ考えられてきたが、その一種と言える。例えば、

  • unixのパイプ
    入力を受け取ってフィルタリングを行い出力をするコマンドをつなぎ合わせる。

ls -l | grep something |  sort -k 6,6
  • Javaのストリーム
    これも同じ。前のオブジェクトの入力を受け取り、フィルタリングを行う出力するオブジェクトを数珠つなぎにする。

 BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream("something")));

形態は異なるけれどもやりたいことはこれと同じ。「任意の計算をつなぎあわせて所望の目的が達成できればとても便利だろう」という悲願の実現であると言える。

モナド則

「モナド」となるオブジェクトは「モナド則」を満たす必要があるという。モナド則とは以下のようなものだ。

  • (1) m flatMap unit ≡ m
  • (2) unit(x) flatMap f ≡ f(x)
  • (3) m flatMap g flatMap f ≡ m flatMap {x => g(x) flatMap f}

関数脳のつくり方 Second Season ~モナドで悟りをひらく~より

Haskellの本にも同じような記述がある(上と合うように順序を並べ替えてある)。

  • 2. m >>= return == m

  • 1. (return x) >>= f == f x

  • 3. (m >>= f) >>= g == m >>= (\x -> f x >>= g)

※「ふつうのHaskellプログラミング」(Softbank Creative)より

Listがモナド則を満たすかを確認する

Listがこのとおりに動作するかを確認してみる。まず、flatMapはそもままflatMapメソッドだが、unitは単なるコンストラクタ呼び出しであることに注意。

val m = List(1, 2, 3)
def unit = (x: Int) => List(x)
def f = (x: Int) => List(x * 3)
def g = (x: Int) => List(x / 2)

※もちろんすべてのf,gについて確かめることは不可能なので、少なくともf,gの適用順序が変わると結果が変わるようにしとく。

※unitというのは、単なるコンストラクタ呼び出し。

すると、(1)は

m flatMap unit // List(1, 2, 3)になる

(2)は

unit(33) flatMap f // List(99)になる。
f(33)              // List(99)になる。

(3)は

m flatMap g flatMap f           // List(0, 3, 3)になる。
m flatMap {x => g(x) flatMap f}  // List(0, 3, 3)になる

と、とりあえず一種類の関数についてListがモナド則を満たすことは確認できた。

モナド則の意味

さて、モナド則は何を意味しているのだろうか?日本語で説明して欲しいものである。 上のListの例に即して考えてみる。

m flatMap unit ≡ m

これは簡単。Listのそれぞれの要素にコンストラクタを適用し、要素を一つだけ持つListを生成しても、それらのListは合体されて元通りになる、ということである。

unit(x) flatMap f ≡ f(x)

これも簡単。flatMapが動作するとき、fに与えられる引数はxである、ということ。

m flatMap g flatMap f ≡ m flatMap {x => g(x) flatMap f}

これは結合則のようなものと思われる。左辺はListに対してflatMap gを適用した結果にflatMap fを適用しているが、 右辺では、個別の要素にgを適用した結果にflatMap fを適用する、という処理をListに適用している。

うーむ、やっぱりわからない。

参考