モナドは象だ
「モナドは象だ(Monads are Elephants)」日本語訳 — Japanese Translation of Monads are Elephants v1.0 documentation読み終わりました.
JavaからScalaに行った人には比較的分かりやすい説明だったと思います.
ちなみに4章でぎり頭爆発.
ほとんど抜粋ですが,自分用のまとめとして保管させて頂きます.
何か問題あれば,即削除しますのでコメントなどで連絡お願いします.
Tips
“=> A”は、call by name(名前渡し)と呼ばれる指定方法で、引数を遅延評価します。通常、foo( bar )という呼び出しではbarが評価された結果をfooの引数として渡しますが、fooメソッドの定義が”def foo( v: => String)”のように定義されていると、barをいわゆる無名関数に包んでfooメソッドに渡します。fooの内部では、barを評価することで結果を任意のタイミングで取り出せるようになります。
分からんとこ
「モナドは象だ」 Part3 — Japanese Translation of Monads are Elephants v1.0 documentation
m flatMap f ≡ flatten(m map f)
このfってmapにもflatMapにも使ってるけど何…
次にfにidentity入れてるけど結局それで,m flatMap identityってダメな気がするんだけど.
「モナドは象だ」 Part4 — Japanese Translation of Monads are Elephants v1.0 documentation
=> Aとか[_]とか分からん.
=> Aは注釈に書いてあった.
本題は以下から
さて,いきなりモナドは象だではなく,All About Monadsも少しだけ含めて.
コンピュータ科学論文とScalaの変換
Generic | scala |
---|---|
M | class M[A] or case class M[A] |
M a | M[A] |
unit v | new M(v) or M(v) |
map f m | m map f |
bind f m | m flatMap f |
join | flatten |
モナド則 : モナドが満たすべきいくつかの法則
ファンクターとは何か?
m map f ≡ m flatMap {x => unit(f(x))} //forで書くと以下のようになる for (x <- m) yield f(x) ≡ for (x <- m; y <- unit(f(x))) yield y
1,同一性(Identity)
def identity[A](x:A): A = x
2,コンポジション(Conposition)
gでmapした結果をさらにfでmapすると、「gとf」の合成関数でmapする結果と同じである
val result1 = m map (f compose g) val temp = m map g val result2 = temp map f assert result1 == result2 //forで書くと以下のようになる for (y <- (for (x <- m) yield g(x)) yield f(y) ≡ for (x <- m) yield f(g(x))
モナド則
1,同一性(Identity)
m flatMap unit ≡ m // or equivalently
flatMapはunitが行ったことを元に戻しているだけ
2,Unit
unit(x) flatMap f ≡ f(x)
f(x)を算出できるようにするために、unit(x)は何らかの手段でxを保存しておく必要がある
つまり,モナドはある意味コンテナ型である
3,コンポジション(Composition)
m flatMap g flatMap f ≡ m flatMap {x => g(x) flatMap f} // or equivalently //forで書くと以下のようになる for (a <- m;b <- g(a);result <- f(b)) yield result ≡ for(a <- m; result <- for(b < g(a); temp <- f(b)) yield temp) yield result
一連のflatMapがどのように一緒に動作するかという規則.
ゼロの法則
リストのNilやOptionのNoneはモナド的にゼロ(mzero)と呼ばれるもの
Result(: Success(value) | Failure(msg))のように複数のゼロを持つ事もある
モナドはゼロを持たない事もある
1,mzero flatMap f ≡ mzero
2,m flatMap {x => mzero} ≡ mzero
3,mzero plus m ≡ m
4,m plus mzero ≡ m
plusはリストでは:::,オプションではorElseに当たる
ScalaからHaskellへの対応表
No. | Scala | Haskell |
---|---|---|
FM1 | m map f ≡ m flatMap {x => unit(f(x))} | fmap f m ≡ m >>= x -> return (f x) |
M1 | m flatMap unit ≡ m | m >>= return ≡ m |
M2 | unit(x) flatMap f ≡ f(x) | (return x) >>= f ≡ f x |
M3 | m flatMap g flatMap f ≡ m flatMap {x => g(x) flatMap f} | (m >>= f) >>= g ≡ m >>= (x -> f x >>= g) |
MZ1 | mzero flatMap f ≡ mzero | mzero >>= f ≡ mzero |
M2Z | m flatMap {x => mzero} ≡ mzero | m >>= (x -> mzero) ≡ mzero |
MZ3 | mzero plus m ≡ m | mzero ‘mplus’ m ≡ m |
MZ4 | m plus mzero ≡ m | m ‘mplus’ mzero ≡ m |
FIL1 | m filter p ≡ m flatMap {x => if(p(x)) unit(x) else mzero} | mfilter p m ≡ m >>= (x -> if p x then return x else mzero) |
IOモナド
関数型プログラミングには,参照透過性*1という概念がある.
参照透明な関数は簡単に扱えて,デバッグがしやすくなる.
がしかし,IOは参照透過性を達成できない.(何を入力に入れるか分からない,ネットワークへの送信も同様)
1,世界の状態はIO関数の間で変化する.
getStringやputStringにWorldStateを与え,nextStateを返させる.
2,成果の状態はそのまま取り扱うしかない.
WorldStateを様々な場所で使えないようにする.
3,世界はどんな瞬間でも厳密にひとつの状態に定まる.
WorldStateにアクセス出来ないように,sealedにしたWorldStateをprivateなコンストラクタを持つクラスに継承させる.
以上のように小さい世界をモデル化して,扱うようにする.
*1:ある関数を同じ引数で呼び出した結果は,いつどこで呼び出そうとも同じ結果になる