モナドも、コモナドも、あるんだよ(前篇)

さて、今回はみんな大好きなモナドだよ、まあ俺もそうだけど。

まず圏論monadから見てみよう:

Fはendofunctor*1。今私たちの手元にmorphisms f:\hspace{10}a\to Fbがある。問題は:どんな状況の下で、a,bの意味を変更する事により、fが別の圏にf:\hspace{10}a\to bのような形式になるのか?
まず、何らかの方法で二つのmorphisms f:\hspace{10}a\to Fbg:\hspace{10}b\to Fca\to Fcのような形式に「結合」しなければならない。そして、この「結合」は圏のcompositionの定義を満足しなければならない。最後、\eta{\tiny a}:\hspace{10}a\to Faを圏のidとして存在しなければならない。
この新しい圏はKleisli categoryと言い、\cal{K}と表記される。\cal{K}の構成は下記通り:

\cal{K}のobject a 元の圏のobject a
\cal{K}のmorphism f:\hspace{10}a\to{\tiny \cal{K}}b 元の圏のmorphism \hspace{10}f:a\to Fb
f\hspace{5};{\tiny \cal{K}}\hspace{5}g*2 f\hspace{5};\hspace{5}Fg\hspace{5};\hspace{5}\mu
id\tiny K,a \eta\tiny a

\mu\etaはnatural transformation:

\mu:\hspace{10}FF\to F
\eta:\hspace{10}I\to F

あと、compositionの結合則とidの定義を満足するために、\mu\etaが下記の法則を従わなければならない:

1、F\mu\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}\mu F\hspace{5};\hspace{5}\mu
2、F\eta\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}\eta F\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}idF

法則1はcompositionの結合則を保証する:

(f\hspace{5};{\tiny \cal{K}}\hspace{5}g)\hspace{5};{\tiny \cal{K}}\hspace{5}h\hspace{10}=\hspace{10}f\hspace{5};{\tiny \cal{K}}\hspace{5}(g\hspace{5};{\tiny \cal{K}}\hspace{5}h)
\equiv\hspace{10}(f\hspace{5};\hspace{5}Fg\hspace{5};\hspace{5}\mu)\hspace{5};\hspace{5}Fh\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}f\hspace{5};\hspace{5}F(g\hspace{5};\hspace{5}Fh\hspace{5};\hspace{5}\mu)\hspace{5};\hspace{5}\mu
\equiv\hspace{10}f\hspace{5};\hspace{5}Fg\hspace{5};\hspace{5}\mu\hspace{5};\hspace{5}Fh\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}f\hspace{5};\hspace{5}Fg\hspace{5};\hspace{5}FFh\hspace{5};\hspace{5}F\mu\hspace{5};\hspace{5}\mu
\equiv\hspace{10}f\hspace{5};\hspace{5}Fg\hspace{5};\hspace{5}FFh\hspace{5};\hspace{5}\mu F\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}f\hspace{5};\hspace{5}Fg\hspace{5};\hspace{5}FFh\hspace{5};\hspace{5}F\mu\hspace{5};\hspace{5}\mu           (in lhs: naturality \mu)
\equiv\hspace{10}\mu F\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}F\mu\hspace{5};\hspace{5}\mu          (f,g,h\leftarrow id)

法則2は圏におけるidの定義を保証する:

f\hspace{5};{\tiny \cal{K}}\hspace{5}id{\tiny \cal{K}}\hspace{10}=\hspace{10}f\hspace{10}=\hspace{10}id{\tiny \cal{K}}\hspace{5};{\tiny \cal{K}}\hspace{5}f
\equiv\hspace{10}f\hspace{5};\hspace{5}F\eta\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}f\hspace{10}=\hspace{10}\eta\hspace{5};\hspace{5}Ff\hspace{5};\hspace{5}\mu
\equiv\hspace{10}f\hspace{5};\hspace{5}F\eta\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}f\hspace{10}=\hspace{10}f\hspace{5};\hspace{5}\eta F\hspace{5};\hspace{5}\mu          (naturality \eta)
\equiv\hspace{10}F\eta\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}idF\hspace{10}=\hspace{10}\eta F\hspace{5};\hspace{5}\mu          (f\leftarrow id)

triple (F,\hspace{5}\mu,\hspace{5}\eta)をモナド(monad)という。

さて、次はHaskellモナドを考察してみよう:

monadクラスの定義:

class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  return :: a -> m a

と、関数joinの定義:

join :: (Monad m) => m (m a) -> m a

こりゃ一目で分かるっしょ:

join <=> \mu
return <=> \eta

それなら >>= は何ぞ?圏論monad定義でmonadはtripleであり、tripleの中にFがあるのことを注目しよう。つまり、monadはfunctorである。
functorクラスの定義:

class Functor where
  fmap :: (a -> b) -> f a -> f b

fmap と >>= の定義が似てると思わない?ここで >>= の引数の順番をひっくり返してみよう:

  (>>=改) :: (a -> m b) -> m a -> m b

ちょっと違うな…もし >>= が fmap なら最後の結果はm bじゃなくm (m b)でしょう?えっ?m (m b)とm b?その通り、必要なものはjoinだ。
つまり:

  x >>= f = join . (fmap f) $ x 

そして:

join . fmap f <=> Ff\hspace{5};\hspace{5}\mu

Ff\hspace{5};\hspace{5}\muは何かを似てると思わない?そう、Kleisli categoryのcomposition。実際、Haskellもこのような演算子を定義している:

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
f >=> g = \x -> f x >>= g

>>= を展開すると:

f >=> g = \x -> join . (fmap g) . f $ x 

join . fmap g . f <=> f\hspace{5};\hspace{5}Fg\hspace{5};\hspace{5}\mu

完全に一致。
次はhaskellモナド則を検証してみよう:

return >>= f  ==  f                             -- 1
m >>= return  ==  m                             -- 2
m >>= (\x -> f x >>= g)  ==  (m >>= f) >>= g    -- 3
    • 1

return >>= f == f
= join . fmap f . return == f
= join . return . f == f
= join . return == id (when f = id)<=> \eta F\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}idF

    • 2

m >>= return == m
= join . fmap return $ m == m
= join . fmap return == id<=> F\eta\hspace{5};\hspace{5}\mu\hspace{10}=\hspace{10}idF

    • 3

m >>= (\x -> f x >>= g) == (m >>= f) >>= g
= m >>= (f >=> g) == (m >>= return >>= f) >>= g
= m >>= h >>= (f >=> g) == m >>= (h >=> f) >>= g ( where h = return )
= m >>= (h >=> (f >=> g)) == m >>= ((h >=> f) >=> g)
= h >=> (f >=> g) == (h >=> f) >=> g<=> h\hspace{5};{\tiny \cal{K}}\hspace{5}(f\hspace{5};{\tiny \cal{K}}\hspace{5}g)\hspace{10}=\hspace{10}(h\hspace{5};{\tiny \cal{K}}\hspace{5}f)\hspace{5};{\tiny \cal{K}}\hspace{5}g

またしても完全に一致。

以上の考察により、圏論Haskellモナドは全く同じものと言っても良いだろう。しかし、Haskellはjoinではなく>>=演算子Monadクラスのインタフェースとして定義した。まあ実用の面から考えたら当然だが、結果としてjoinの存在感が薄くなり、初心者にとって空気のような存在になるとかならないとか。joinの重要性に対してこのような扱いはあまりにも不憫しすぎだろう…

結論:joinは犠牲になったのた…>>=の犠牲にな…

(多分つづく)
追記:後篇

参考資料:
Dyads, a generalisation of monads, 1994, Maarten M. Fokkinga http://wwwhome.ewi.utwente.nl/~fokkinga/mmf94c.pdf

*1:endofunctorとはF:\hspace{10}\cal{A}\to\cal{A}のようなfunctor,つまりendofunctorのマッピング元とマッピング元は同じの圏の意味である。

*2:「;」はmorphisms compositionを意味する記号:「f\hspace{5};\hspace{5}g\hspace{10}=\hspace{10}g\hspace{5}\circ\hspace{5}f\hspace{10}=\hspace{10}gf」、まあ「g\hspace{5}\circ\hspace{5}f」の記法がHaskellの「g . f」と同じ順番だからこっちの方が好む人が多いかもしれないが…