Pixel Pedals of Tomakomai

北海道苫小牧市出身の初老の日常

"圏論とかモナドなんて簡単だからscalaを使って説明してみた"を検証してみた

射っていうのはscalaだと単なる関数だし、関手はmap、モナドはflatMapなだけです。これのどこが難しいというのでしょう。

圏論とかモナドなんて簡単だからscalaを使って説明してみた

内容について考えてみよう。

圏の定義?

trait Cat {
  type A
  type B
  def f:A => B
}

trait Cat {
  type A
  type B
  type C
  def f:A => B
  def g:B => C
  def f_g:A => C = f andThen g
}

圏論とかモナドなんて簡単だからscalaを使って説明してみた

ここで定義されているCatはただの1本の射であって圏とは言い難い。hom setを定義したというのならまだわかるが、ScalazのHomとは似ているが違う定義だ。具体的に言うと、Homは1本の射fではなく、射を表す型であるCが定義されている。

簡単に考えるのであれば対象が型で射が普通のScalaの関数である圏を考えるのが一番だが、Scalazによればそれは以下の定義で与えられる。

  implicit val Function1Category: Category[Function1] = new Category[Function1] {
    def id[A] = a => a
    def compose[X, Y, Z](f: Y => Z, g: X => Y) = f compose g   
  }

住所圏からOption圏への関手?

さて、先ほど「圏とは言い難い」という表現をしたのだけど、これは「関数1つからなる圏も考えられる」という事実を考慮してのことだ。可算無限歩くらい譲れば先ほどのCatはそれを表現しているとも考えられる*1

これが関手といわれるものです。住所圏からOption圏への関数合成みたいなものですね。

圏論とかモナドなんて簡単だからscalaを使って説明してみた

ここでいう住所圏が「住所 => 郵便番号という関数1つからなる圏」、Option圏が「Option[住所] => Option[郵便番号]という関数1つからなる圏」とすれば、「mapは住所圏からOption圏への関手だ」という説明で問題はない*2 *3。しかし、関数型言語を使う場合に大抵の場合はこのような特殊な圏を考えることは少ないだろう。すべての型が対象、すべての関数が射、という圏で話を進めた方が一般的だし混乱は少ない。

flatMapがモナド

モナドは関手(map) + flattenと同じ事です。

圏論とかモナドなんて簡単だからscalaを使って説明してみた

flatMapがmapとflattenを使って定義されるというのは正しい。問題は「> 関手はmap、モナドはflatMap」かどうかということだが、ここは微妙なとこだ。

というのは、モナドは関手の一種だからだ。Scalaの関手とモナドについてざっくり整理すると、以下のようになる。

  • mapが定義された型*4が関手
  • flattenが定義された関手がモナド*5

さらに本格的な話に興味があれば、Scalazのコードを読むといいだろう。


とは言え、mapとflatMapが分かれば実用上問題がないというのはその通りで、それ以上圏や関手、モナドについて踏み込んで厳密に理解する必要はないと言える。「圏論とかモナドなんて不要だからscalaにおける要点だけ説明してみた」ってことあれば全く同意である

追記

ご本人からコメントがつきましたので、一応回答を。

rirakkumya ぼくは単に「モナドの事をscala”で"説明」したかっただけなのですが、「圏論とかモナドなんて不要だから〜」というのは論点がずれてないですか? scalaの要点なんて正直どうでも良いです。

@rirakkumya さんの圏論への愛情は理解できるが、元のエントリは上述したように圏やモナドの説明としては不適切だ。使えればいいのだから学術的な定義などどうでもいいという主張も理解できるのだが、圏論から学術的な厳密さを引いてしまうと圏論ではなくなってしまう*6。それはもはや単なるmap関数とflatMap関数の説明だ。

もう少し背景を補足すると、 @rirakkumya さんは関手的データモデルに興味を持っており、過去にもそれを表現しようとしていたことが伺える。そういう背景から判断して、上で書いた「関数1つからなる圏も考えられる」という立場に立ってこの説明を書いたというのは間違いないだろう。しかし、そうすると注釈欄にも書いたようにmapは自己関手ではなくなるため、flatMapをモナドと見なすことはできない。

*1:もちろん、恒等射idと合成の定義は別途必要

*2:もちろん、関手はontoである必要はないので、Scalaの圏にOption関手を適用した圏への関手と見ても問題はない

*3:ただしこの関手は自己関手ではないのでモナドにできない

*4:mapの定義のために型パラメータが1つ必要

*5:ほんとはη(returnまたはunit)も必要なんだけど、素のScalaでは型パラメータの型を受け取る1引数コンストラクタで代用する

*6:モナド」という言葉に関しては、現在では圏論の用語だけでなく関数型言語のインタフェースを定義する言葉としても市民権を得ているので、微妙なところ。