Hatena::ブログ(Diary)

scalaとか・・・ このページをアンテナに追加 RSSフィード Twitter

2013-05-05

「モナドはメタファーではない」に関する補足

| 17:24 | 「モナドはメタファーではない」に関する補足を含むブックマーク 「モナドはメタファーではない」に関する補足のブックマークコメント

日本語訳 モナドはメタファーではない

原文 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))(_ + " " + _)

念のため繰り返しておくと、書いた本人は上記のことわかってるだろうし、こんなことを言ってます



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で十分」

という話です。

Haskellが、なぜMonadに直接

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がでてくるわけです。



まとめ

というわけで、

  • fullNameの函数
  • Monadのsequence函数

の2点とも、結局MonadではなくApplicative (ry という話でした。

このように、(説明の都合上)わざとだったり、本人も気づいてなかったり、いずれにせよ

「実はMonadではなく、単にApplicativeで事足りる」

パターンが、Monadの説明の例として使われていることは他にも大量にあるので、みんな「モナド( ゚∀゚)彡 モナド( ゚∀゚)彡 」ばかり言わないで、もうちょっと意識してApplicativeの存在に気づけるようになるといいと思います。

*1:もっと正確にはApply

*2: 7.0.0 final

*3Haskellにも同じものがある

*4: Haskellの場合も同様にfoldr https://github.com/ghc/packages-base/blob/ghc-7.6.3-release/Control/Monad.hs#L97-L103

*5:Traverseのtypeclass自体を説明すると長くなるので省略しますが

トラックバック - http://d.hatena.ne.jp/xuwei/20130505/1367742286