Deletions are marked like this. | Additions are marked like this. |
Line 44: | Line 44: |
{{{ 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の適用順序が変わると結果が変わるようにしとく。 すると、(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)になる }}} |
モナド
※以下は間違いの可能性があるので信用しないように。これは単なる学習メモです。
第一印象
モナドとは、連続して計算を行うパイプラインを簡単に作成するためのものである。古今東西同様の仕組みはいろいろ考えられてきたが、その一種と言える。例えば、
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の適用順序が変わると結果が変わるようにしとく。
すると、(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)になる