up:: Programming
概要
関数型言語の考え。Monad。
関数に与えられた「ある値をもとに、別の値を返す」デザインパターン。
与えられた値は計算に絶対使う必要はない。捨ててもいい。
考えとしては単純で、その規則に基づくと関数内で呼出し関数を分けているセミコロン(;)でさえモナド的な考えということになる。
(第一関数の結果は第一関数のまま、それと関係なく第二関数の値を返している)
例えばこんな風に有ったらThingはモナド。
case class Thing[+A](value: A) {
def bind[B](f: A => Thing[B]) = f(value)
}
def foo(i: Int) = Thing(i + 1)
val a = Thing(1)
val b = a bind foo
ThingというコンテナにAという値が入る。bindを呼ぶとBの関数(にAを使用して)を呼んで戻り値を返す。
つまり、Aを使ってBの結果を返している。よってモナド。
もし何かから始めて、それを分解した後で、同じ型の別の何かを計算した時、それはモナドだ。
こんなの普通の関数もそうだろというところだが、その通り。
モナドは大きな概念で、どこにでもある。
Thingは値をラッピングしているもの。モナドではこれをUnit
と呼ぶ。
bindはThingから値を取り出し、渡された関数を用いて新たなThingを作る。ある物から始めて、その値を使って新たなものを計算している。なのでモナド。
何に使うの
なんの役に立つの? というところだが、これを拡張するといろいろ省略して書ける。
例えば同じ状態に対し同じ条件文をたくさん使うなら、その状態を取る値をUnitに入れておき、bindで条件文を書いてみる。するとbindを呼べば条件文を適用できるし、言語によっては複数の条件文を一気にbindで計算することもできる。
sealed trait Option[+A] {
def bind[B](f: A => Option[B]): Option[B]
}
case class Some[+A](value: A) extends Option[A] {
def bind[B](f: A => Option[B]) = f(value)
}
case object None extends Option[Nothing] {
def bind[B](f: Nothing => Option[B]) = None
}
def firstName(id: Int): Option[String] = ... // データベースから取得
def lastName(id: Int): Option[String] = ...
def fullName(id: Int): Option[String] = {
firstName(id) bind { fname =>
lastName(id) bind { lname =>
Some(fname + " " + lname)
}
}
}
Haskell IO
Haskell(純粋関数型言語。変数が無く副作用を許さない)のIO。
これもモナド。
副作用無しで画面などを弄りたい。すると手っ取り早いのはUnitに前の状態、先ほどのbindに前の状態と変えたい対象の関数を入れて新たな状態を返すこと。
しかしこれは標準入力だとうまくいかない。何を読んで出力するのかは人の手が入るため、いつか必ず別の何かを変化させることになる。
ならば状態を世界単位にすれば何とかなる。実際は世界の振りをしたもの。それがIO。
ただし、IOに入った値は何も取り出せない。ここをどうしてるかはHaskellの話。
役立ち部分
モナドはUnitとbindを持つ。
そして値の種類は気にしない。どんな値でもUnitに入ってれば同じ扱いが出来る。
そしてUnitはtrait(interfaceみたいなの)をつけて抽象化できる。どんな値を入れるかを継承できる。
つまり、どの特定のモナドなのか知らずともモナドを扱うことは出来る。
モナド則
モナドをモナドたらしめるものとしてモナド則という物がある。数学の交換法則とかそういうの。
知らなくてもさっきの通り説明は出来た。
最初二つの公理は、Unit関数はbind関数に対して単純なコンストラクタであるというもの。
bindがばらしてもunitが格納していた値のままだし、bindが値をモナドで囲むだけなら何もしてないのと同じ。
三つ目の公理はbindの順序が変わっても最後の値は同じだよ的なこと。
これらのおかげで手続き的になり読みやすい。