日本語訳 モナドはメタファーではない
原文 Monads Are Not Metaphors
原文が書かれたのはもう2年半くらい前ですが、いまだにたまにtweetされてたり、250くらいはてブがついてたり人気ありますね。で、説明中ででてくるコード例に関する補足というか、野暮なツッコミを思い出したので、書いておきます。
「野暮なツッコミ」といってる通り、「書いた本人はおそらく最初からわかっていたけれど(説明の都合上)あえて省略した部分」を説明する感じです。あと、わざとScalazを説明に使います。
firstNameとlastNameからfullNameを取得するコード例は、MonadではなくApplicative*1があれば十分
もとのコードは以下のようになってますが
def firstName(id: Int): Option[String] = ... // fetch from database def lastName(id: Int): Option[String] = ... def fullName(id: Int): Option[String] = { firstName(id) bind { fname => lastName(id) bind { lname => Some(fname + " " + lname) } } }
Scalaz*2 で書くとすれば、以下のように書けます
import scalaz._,Scalaz._ def firstName(id: Int): Option[String] = ??? def lastName(id: Int): Option[String] = ??? def fullName(id: Int): Option[String] = ^(firstName(id), lastName(id))(_ + " " + _)
もしくは
def fullName(id: Int): Option[String] = Apply[Option].apply2(firstName(id), lastName(id))(_ + " " + _)
念のため繰り返しておくと、書いた本人は上記のことわかってるだろうし、こんなことを言ってます
Don’t use a monad when an applicative will do.
ScalazにおいてのMonadのsequence函数?
まず、Scalaz7に
def sequence[M[_], A](ms: List[M[A]])(implicit tc: Monad[M]): M[List[A]]
というシグネチャの函数は存在しません。代わりにApplicativeに以下のようなものがあります
def sequence[A, G[_]: Traverse](as: G[F[A]]): F[G[A]] =
https://github.com/scalaz/scalaz/blob/v7.0.0/core/src/main/scala/scalaz/Applicative.scala#L39
結論を先に言ってしまうと、 "「モナドはメタファーではない」の中で説明されているsequence函数*3 " は、
Scalazにおいては、一般化された形でのみ存在しています。
ちなみに、Haskellの場合はApplicativeに直接該当するメソッドがあるわけではなく、TraversableにsequenceAというメソッドがあります。 http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-Traversable.html#v:sequenceA
Scalazに戻ると、「一般化された形」とはつまり、これ
def sequence[A, G[_]: Traverse](as: G[F[A]]): F[G[A]]
のGの型パラメータをListに固定すると
def sequence[A](as: List[F[A]]): F[List[A]]
となって、Scalazで定義されているsequence函数が、"「モナドはメタファーではない」の例ででてきたsequence函数" と同じになるということです。
(implicit tc: Monad[M])
の部分がなくなっているように見えるますが、「ScalazのApplicativeのメソッドとして定義」しているので、Fは自動的にApplicativeのインスタンスです。
なんだかちょっとややこしくなってきましたが、つまり
「ここで例に出てきたsequence函数も、Monadが必要ではなくApplicativeで十分」
という話です。
sequence :: Monad m => [m a] -> m [a]
というのがあったり、
Traversableに
sequenceA :: Applicative f => t (f a) -> f (t a)
と
sequence :: Monad m => t (m a) -> m (t a)
という似たようなものが2つあったりするのかは、誤解を恐れずにあえて一言で言うと「MonadがApplicativeを継承していなかった、という歴史的事情による産物」だと思います。
あと、「一般化された形」と言いましたが、それについてもうちょっと説明しておきます。"「モナドはメタファーではない」で出てきたsequence"の実装には、ListのfoldRightを使っています*4
*5 つまり「foldRight相当の機能をもつコンテナ」=> 「Traverseのtypeclassのインスタンスになるもの」ということなので、「sequence函数を一般化」した場合のシグネチャには
def sequence[A, G[_]: Traverse](as: G[F[A]]): F[G[A]]
というように、Traverse
がでてくるわけです。