はじめに
monad って一体なんなのって、象の絵がかいてある本とか、色々な所で色々なやり方で論じられていると思うので、適宜調べればいいと思うんですけど、やっぱり自分用のまとめも欲しい。とりあえずここに『復習 モナド』としてまとめておきます。
レジュメ
-- 射の合成
(.) :: ( b -> c) -> ( a -> b) -> ( a -> c)
(<=<) :: Monad m => ( b -> m c) -> ( a -> m b) -> ( a -> m c)
(=<=) :: Comonad w => (w b -> c) -> (w a -> b) -> (w a -> c)
(<<<) :: Category cat => cat b c -> cat a b -> cat a c
-- 関手
($) :: ( a -> b) -> ( a -> b)
map :: ( a -> b) -> ([a] -> [b])
(<$>) :: Functor f => ( a -> b) -> (f a -> f b)
-- Applicative
repeat :: a -> [a]
pure :: Applicative f => a -> f a
(<*>) :: Applicative f => f (a -> b) -> (f a -> f b)
-- Monad
fmap :: Functor f => (a -> b) -> (f a -> f b)
return :: Monad m => a -> m a
join :: Monad m => m (m a) -> m a
(=<<) :: Monad m => (a -> m b) -> (m a -> m b)
-- Comonad
fmap :: Functor f => (a -> b) -> (f a -> f b)
head :: [a] -> a
extract :: Comonad w => w a -> a
duplicate :: Comonad w => w a -> w (w a)
(<<=) :: Comonad w => (w a -> b) -> (w a -> w b)
合成と圏論
小さいプログラムを『合成 compose』して、大きなプログラムを作りたい。たとえば f :: Float -> Int という「関数」と g :: Int -> String という「関数」を組み合わせて、 (g . f) :: Float -> String という「関数」を作れる。
a -> b の a の方を『ドメイン domain』、 b の方を『余ドメイン codomain』と呼ぼう。関数 g のドメインと関数 f の余ドメインが両方とも Int だから、合成できることが分かる。
「関数」だけじゃなくて、「無限リストの処理」とか「副作用のある計算」みたいなプログラムでも同じことができる。こういった、ドメインと余ドメインを持っていて合成できるものを『射 morphism』、その全体を『圏 category』と呼ぶ。圏論は圏や射についての理論。
Haskellの (.) 演算子は関数の合成。同様に (<=<) 演算子はKleisli圏(後で出てきます)の射 (a -> m b) の合成。Haskellにはそのまま Category という名前の型クラスが定義されていて、 (<<<) 演算子はどの圏の射の合成にも使える、オールマイティーな演算子。逆向きの (>=>) や (>>>) という演算子もある。
関手とFunctor型クラス
2013年2月2日 内容が間違っているので修正
HaskellのFunctor型クラスの定義する fmap 関数は、リストのに対する map 関数を一般化したものだと思える。「関数 (a -> b)」を「コンテナ型の関数 (f a -> f b)」に変換してくれる関数を定義している。
関数の圏の対象からコンテナ型の関数の圏の対象への関数 a -> f a は『対象関数 object function』、Qiita内で圏論に言及するエントリのまとめ (2)関数からコンテナ型の関数への写像 (a -> b) → (f a -> f b) は『射関数 mapping function』にあたる。関手はこの2つの関数から成り立っている。(Functor型クラスでは射関数 fmap だけが定義されている)
『関手 functor』はには次の性質が成り立つ。
-- 合成の保存
functor (g <<< f) === functor g <<< functor f
-- 恒等射の保存
functor id === id
恒等射は他の射と合成しても何も変わらない射。
id <<< f === f
g <<< id === g
よく分からないイメージ図。
a →【g】→ b
⇩ map
[a] →【map g】→ [b]
a →【g】→ b
⇩ fmap
f a →【fmap g】→ f b
(<$>)
演算子はfmapの別名で、(fmap f)
と (f <$>)
は同じ。
どうやら時間切れのようだ……
続きはまた次回。