top page > computer > haskell > web_lecture > for_programmer > between_functor_monad.html
更新日:
文責: 重城良国

ファンクターとモナドのあいだ

(工事中 20%)

はじめに

このページはたぶん「わかりにくい」。具体例や「アプリカティブのよさ」を追加するなど大幅に書き換える必要がある。

モナドはすべてファンクターである。それはモナド関数によってfmapが定義できることから明らかだ。もともとはそれだけだった。しかし、近年になってファンクターとモナドのあいだのアプリカティブファンクターというものが便利であることがわかり、Haskellにも導入された(ここらへんの歴史について調べたい)。

モナドはすべてアプリカティブファンクターである。アプリカティブファンクターはすべてファンクターである。

(「文脈つきの値」とするか「値を含む構造」とするか) (「文脈つきの値」という言葉についてどこかで説明する必要がある)

モナドはすべてファンクター

モナドには以下の関数がある。

ret :: a -> m a
bind :: m a -> (a -> m b) -> m b

この2つを使って関数fmapを定義することができる。

fmap f = (`bind` (ret . f))

また、モナド則から

f `pipe` ret == f
where (g `pipe` h) x = g x `bind` h
=> f x `bind` ret == f x
=> (`bind` ret) == id
=> (`bind` ret . id) == id
=> fmap id == id

ファンクタ則も満たす。よってモナドならばファンクタだ。

モナドにはできてファンクターにはできないこと

(ここらへんに関数joinあたりの話題を置くか)

関数bindの引数をいれかえると

flip bind :: (a -> m b) -> m a -> m b

となる。これと

fmap :: (a -> b) -> m a -> m b

とはよく似ている。(厳密には違うが)型変数はどんな型に置きかえてもいいので関数fmapの型の型変数bを型(m b)にしてみる。すると

fmap :: (a -> m b) -> m a -> m (m b)

となる。つまりモナドにできてファンクターにできないことは二重になったmをひとつにまとめることだ。

(このあたり何か具体例でも挙げられるといいのだけど...。そもそもファンクターにできる構造でモナドにすることができない構造には何があるかな)

ファンクターからアプリカティブファンクターへ

(ファンクターでは多引数関数に複数の文脈つきの値を適応できないという話)

ファンクターでは構造のなかみの値に対して関数を適用することができる。しかし、多引数関数に対して複数の文脈つきの値を与えることはできない。2引数関数に対して関数fmapによって引数をひとつ与えることを考える。

g :: a -> b -> c
x :: f a
fmap g x :: f (b -> c)

このように文脈つきの関数が返る。文脈つきの関数を扱うことは関数fmapにはできない。そこで以下のような関数appを考える。

app :: f (a -> b) -> f a -> f b

関数fmapのかわりに関数appを使うことを考える。一番最初に普通の関数に文脈をつける必要があるので

pure :: a -> m a

も定義する。すると2引数関数を2つの文脈つきの値に適用するには

g :: a -> b -> c
x :: f a
y :: f b
pure g `app` x `app` y :: f c

のようにできる。

モノイダル則

アプリカティブ関数は以下のふたつである。

pure :: a -> f a
app :: f (a -> b) -> f a -> f b

これらの関数から以下の関数が定義できる。

fmap :: (a -> b) -> f a -> f b
unit :: f ()
tup :: f a -> f b -> f (a, b)

それぞれの定義は

fmap = app . pure
unit = pure ()
u `tup` v = pure (,) `app` u `app` v

となる。このときアプリカティブ関数は

fmap id == id
fmap snd (unit `tup` v) == v
fmap fst (u `tup` unit) == u
fmap asl (u `tup` (v `tup` w)) == (u `tup` v) `tup` w
where asl (x, (y, z)) = ((x, y), z)

を満たすように定義されている必要がある。fmap snd, fmap fst, fmap aslは型を合わせるためにある。本質的には

unit `tup` v == v
u `tup` unit == u
u `tup` (v `tup` w) == (u `tup` v) `tup` w

と同じことだ。

アプリカティブはすべてファンクター

上記より明らか。

モナドはすべてアプリカティブファンクター

関数appは関数bindで

u `app` v = u `bind` \f -> v `bind` \x -> ret (f x)

のように定義することができる。また、pure = retと定義できる。すると

unit = ret ()

であり

u `tup` v
=> pure (,) `app` u `app` v
=> (ret (,) `bind` \f -> u `bind` \x -> ret (f x)) `app` v
=> (u `bind` \x -> ret (x ,)) `app` v
=> (u `bind` \x -> ret (x ,)) `bind` \f -> v `bind` \y -> ret (f y)
=> u `bind` \x -> ret (x ,) `bind` \f -> v `bind` \y -> ret (f y)
=> u `bind` \x -> v `bind` \y -> ret (x, y)

となる。

fmap snd (unit `tup` v)
=> fmap snd (ret () `tup` v)
=> fmap snd (ret () `bind` \x -> v `bind` \y -> ret (x, y))
=> fmap snd (v `bind` \y -> ret ((), y))
=> fmap snd $ v `bind` ret . (() ,)
=> fmap snd $ fmap (() ,) v
=> fmap (snd . (() ,)) v
=> fmap id v
=> v

fmap fst (u `tup` unit)
=> fmap fst (v `tup` ret ())
=> fmap fst (v `bind` \x -> ret () `bind` \y -> ret (x, y))
=> fmap fst (v `bind` \x -> ret (x, ()))
=> fmap fst (v `bind` ret . (, ()))
=> fmap fst $ (`bind` ret . (, ())) v
=> fmap fst $ fmap (, ()) v
=> fmap (fst . (, ())) v
=> fmap id v
=> v

モノイダル則のふたつが示された。モノイダル則のみっつめは

fmap asl (u `tup` (v `tup` w)) == (u `tup` v) `tup` w
where asl (x, (y, z)) = ((x, y), z)

だ。これもモナド則から導くことができる。これを示すのはそのままだと煩雑になるのであとでより見やすい記法を学んでからとする。

モナドにはできてアプリカティブファンクターにはできないこと

(ファンクターをモナドにできる関数joinのような関数はアプリカティブとモナドのあいだにはないのかな)

(アプリカティブファンクターでは、aの値によってm bのmの部分、つまり構造を変化させることができない。そのあたりを具体例を挙げて書きたい。)

app :: m (a -> b) -> m a -> m b
flip bind :: (a -> m b) -> m a -> m b

appとflip bindとを比較すると、関数appではa型の値によって変化させられるのはb型の値だけであるのに対して、関数bindではa型の値によってm bのmの部分つまり構造(文脈)の部分も変化させられることがわかる。

まとめ

ファンクターとモナドのあいだにはアプリカティブファンクターがある。ファンクターではできないことだが、アプリカティブファンクターでは多引数関数に対して複数の文脈つきの値を与えることができる。しかし、モナドではできることである、文脈つきの値のなかみの値によって文脈を変化させることはできない。

メモ

fmap :: (a -> b) -> m a -> m b
app :: m (a -> b) -> m a -> m b
flip bind :: (a -> m b) -> m a -> m b

tup :: m a -> m b -> m (a, b)
tup m n = m `bind` \x -> n `bind` \y -> ret (x, y)

m `app` n = m `bind` \f -> n `bind` \x -> ret (f x)

ファンクタにretとjoinを追加するとモナド

ファンクタにretとtupを追加するとアプリカティブファンクター

joinがあればtupは表現できる。

アプリカティブの話は説明するのがやっかいだ。とくにモナドならばアプリカティブだというところでモナド則からモノイダル則を導くのに手間がかかる。

さきにモナド則から導かれる法則をいくつか見ておくといいのかもしれない。

fmap f [ ret | ... ] == [ f ret | ... ]
[ ret | ..., (x, y) <- [ (x, y) | x <- u, y <- v ], ... ] == [ ret | ..., x <- u, y <- v, ... ]

アプリカティブファンクターのよさをどこかで示したい。

「共通する構造: モナド」へもどる 「モナドではないアプリカティブの例」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics