MonadとArrowの関係。

昨日のArrowのエントリ

MonadもArrow(に出来る)…みたいだけど詳しい事は書いてなかった。

と書いたけど、手っ取り早く、GHC6.6.1のControl.Arrowのソースを見てみた。


要は、モナド自体じゃなくて、モナドの>>=の右辺に渡す b -> m c *1な関数の方をArrowにしてしまう、という話だ。

newtype Kleisli m b c = Kleisli { runKleisli :: b -> m c }

instance Monad m => Arrow (Kleisli m) where
	arr f = Kleisli (return . f)
	Kleisli f >>> Kleisli g = Kleisli (\b -> f b >>= g)
	first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))


ただしArrowのカタチの「a b c」に揃えるために、>>= の右辺に渡す b -> m c な関数を、容れ物(Kleisli)に入れる。入れると

Kleisli m b c 

こういうカタチに。「Kleisli m」がArrowの種類の型a、「b」が"引数"の型b、「c」が"返値"の型c。モナドの種類m毎に、Kleisli mなるArrowの種類が出来る。


で、メソッド実装定義を見てみると、

Arrowのからくりである (>>>) と first(並列結合(***)はこれで作れる) の中に、モナドのからくりである (>>=) が仕込まれている。


これによって、 (>>>) が、モナドを (>>=) で繋ぐ役割を果たす。つまり、aをfしてgしてhする

(return a) >>= f >>= g >>= h

が、

runKleisli (arr f >>> Kleisli g >>> Kleisli h) a

になる。


あと、ArrowにはArrowPlus(とArrowZero)ってのがあって、

class Arrow a => ArrowZero a where
	zeroArrow :: a b c
class ArrowZero a => ArrowPlus a where
	(<+>) :: a b c -> a b c -> a b c

やはり、MonadPlusが対応する、と。

instance MonadPlus m => ArrowZero (Kleisli m) where
	zeroArrow = Kleisli (\x -> mzero)
instance MonadPlus m => ArrowPlus (Kleisli m) where
	Kleisli f <+> Kleisli g = Kleisli (\x -> f x `mplus` g x)

誤りなど、乞うご指摘。

*1:普通は a -> m b って書くけど、Arrowのa b cと合わせるため こう書いておく