Monad を数学っぽく理解してみる。
Monad についてのまとめてみた。(間違ったことをいっているかもしれないけど。)
* Monad m は Applicative Functor で以下を満たすもの
* 以下の関数をもつ。
1. (>>=) :: m a -> (a -> m b) -> m b
2. (>>) :: m a -> m b -> m b
3. return :: a -> m a
4. fail :: String -> m a
* monad 則をみたす。
m a --------------------------> m b
↑ ↑
a ---------------------------> b
return = pure となる。
Functor では a -> b を Fa -> Fb に
Applicative Functor では F(a->b) を Fa->Fb に導入できた。
Monad では a -> mb の関数を >>= によって ma -> mb に導入できる。(bind)
(>>= はたぶん記述しやすさのため ma ->(a->mb)->mb になっているが、
(a->mb) -> ma -> mb と同じことのはず。)
はじめ Applicatvie Functor の <*> は上記 Monad の定義から導き出せるのだと思っていた。そのため Monad は Applicative Functor だと言っているのだと。
その証明には g ∈ m(a->b) から a -> mb を作れればよいだけなので簡単にできるかなと思いきや、 g から a -> mb をどうにも作れなかった。
まあ本来 (Applicative m) => Monad m って型制約があればすむ話なんで、「Monad は Applicative Functor です」っていうのは Monad の全インスタンスは Applicative Functor としての実装もあるってことなのかも。
Monad を理解するためまた具体例で考えてみる。
m = [ ] , a=b=Int
[Int] --------------------------> [Int]
↑ ↑
Int ---------------------------> Int
>>= を使って Int -> [Int] を [Int]->[Int] に導入してみる。
Prelude> [2,3,4,5] >>= (\x->return(x*3))
[6,9,12,15]
[Int] --------------------------> [Int] ∋ return(x*3)
↑ ↑ ↑
Int ---------------------------> Int |
x ------------(*3)------------> x ------+
Int -> [Int] をつくるのに a->b->mb と b を介している。最後の return は b -> mb のため。
>>= の引数定義により
Prelude> [2,3,4,5] >>= (\x->return(x*3)) >>= (\x->return(x+2)) >>= (\x->return(x-3))
[5,8,11,14]
のように連続して a->mb を適用できる。
* >>= の何がうれしいのか?
m の先の世界 m a はいわゆる箱の中の世界。 m a の中身の詳細はこちらがわの人間はしらない。
でもせめて「こっち側と関連している部分」くらいは箱の中から値を取り出して操作をすることができる。それが Monad。
「こっち側と関連している部分」というのはどこかというと m a の部分集合で return a の行き先となる部分。
Monad 則では
x ∈ a , f ∈ (a -> m b) とすると
(return x) >> f = fx
なので return によって m a に移される部分では m a の元を a の元として抜き出して m b に送るというコードがかける。
例えば
Maybe Int ---------> Maybe Int
↑ ↑
Int --------------------> Int
Prelude> (Just 3) >>= \x -> return (3*x)
Just 9
Monad 則によると Just 3 = (return 3)::Maybe Int にラムダ関数をバインドさせるときは x が 3 がくると考えてよい。
(考えてよいというのは Monad の定義や Monad 則では何が来るのかは規定されていないため。もし別の x が来たとしても、 Just x == Just 3 となるはずなので問題ない。そういう意味で Monad は箱の中に入っているものを取り出せる。)
一方
Prelude> Nothing >>= \x -> return (3*x)
Nothing
ラムダ関数の x になにが来るのかは Maybe Int の実装に依存する。
同様に
Prelude> [2] >>= \x->return(3*x)
[6]
は Monad 則より x に 2 が来ると考えてよいが
Prelude> [2,3,4] >>= \x->return(3*x)
[6,9,12]
では x に何がくるのかは [ ] クラスの >>= 実装に依存する。
(見て明らかなように [ ] の各成分がわたってきているわけだけど。)
IO の場合も同じように a -> m b を m a -> m b に移すと考えられる。
IO Char -------> IO String
↑ ↑
Char ------------> String
Prelude> getChar >>= \x -> return $ foldl (++) "" $ replicate 3 (show x)
e
"'e''e''e'"
Prelude> return 'e' >>= (\x -> return $ foldl (++) "" $ replicate 3 (show x))
"'e''e''e'"
getChar で取得した 'e' ::IO Char にせよ return で取得した 'e' ::IO Char にせよ、
IO Char 上では同一なので、ラムダ関数の x の実装がそのまま同様に適用される。