Applicative Functor を数学っぽく理解してみる

「すごい Haskell 楽しく学ぼう!」(略してすごいH本)読んでみた。

 

なんで自分用に整理メモ。(間違ったこと言ってるかもしれない。)

Functor や Monad が箱って話があるけどわかりにくかったので数学っぽく考えてみた。

 

* まず Functor

 Functor は以下のようなもの

  *  Functor F はある集合 a から b への写像 (map) (厳密には射とかなんだろうけど)

  *  F は 関数 f : a -> b を関数 F(f): Fa -> Fb に移す関数 fmap を持ち以下を満たす。

    1. fmap id = id

    2. fmap f * g = fmap f * fmap g

    3. functor 則を満たす。(面倒なんで割愛)

 

図で表すとこんな感じ

                  fmap(f)

       Fa --------------> Fb

   F   ↑                    ↑ F   

         a ------ f ----> b

具体的例)

 a = Int, F = ,  f = (+3)

                   fmap(+3)]             

       [Int]  ---------------> [Int]

        ↑                             ↑

        Int  -----(+3)-----> Int

 

Prelude> :t fmap (+3)
fmap (+3) :: (Functor f, Num b) => f b -> f b
Prelude>  fmap (+3) [4,5,6]
[7,8,9]

fmap によって「手元の」 a, b で定義された関数 f : a->b を

「あちら側」 Fa, Fb で定義された関数 F(f) に導入(map)できる。

 

いちいち (+3) : Fa -> Fb を定義せずとも fmap を使えば自然に (+3) を Fa に導入できるのが Functor のよいところ。


*  次にApplicative Functor

 Applicative Functor F とは Functor であり以下を持つもの

   1. pure :: a -> Fa

   2. (<*>) :: F(a->b)  -> Fa -> Fb

   3. applicutive 則を満たす(面倒なんで割愛)

 

図で表すとこんな感じ

                     <*>(g) <----------- g ∈  F(a->b) ∋ pure(f)

         Fa --------------> Fb                                        ↑

        F ↑                     ↑ F                                        |

           a --------------> b                                           |

                        f  -------------------------------------------+

 

当然 <*>(pure(f)) = fmap(f) (=<$>)

 

Prelude> :m Control.Applicative

Prelude Control.Applicative> :t     (<*>)(pure(+3))
(<*>)(pure(+3)) :: (Num b, Applicative f) => f b -> f b
Prelude Control.Applicative> :t     fmap(+3)
fmap(+3) :: (Functor f, Num b) => f b -> f b

 

F = Maybe や F = のときは Just 3 とか [3] のように a = Int の具体的な値を

F a に移す方法があってそれが pure 。

pure が Functor クラスにはないってことは具体的な a の元を Fa に移す手段は Functor にはない。Functor はあくまで a->b を Fa->Fb に移せるってことをいってるにすぎない。

 

ここで F(a->b) って何?ってなった。

すごいH本でもいきなり Just(+3) とかでてくるが、F(a->b) を考えるのになんの意味があるのかいまいちわからなかった。

単に f を Fa -> Fb として導入したけりゃ fmap で十分じゃん。

 

具体例で考えてみる。

 a = Int, F = [ ] ,  f = (+3)

                   <*>([(+3)])             

       [Int]  ---------------> [Int]

        ↑                             ↑

        Int  -----(+3)-----> Int

 

(+3) は Int -> Int なので F(+3) = [(+3)] となる。(pure(+3))

Prelude> :m Control.Applicative

Prelude Control.Applicative> :t [(+3)]
[(+3)] :: Num a => [a -> a]

たしかに [(+3)] は F(a->a) になっている。

 

そして <*> で Fa -> Fa になる。

Prelude Control.Applicative> :t (<*>) [(+3)]
(<*>) [(+3)] :: Num b => [b] -> [b]

Prelude Control.Applicative>  [(+3)]<*> [2,3,5]
[5,6,8]

Prelude Control.Applicative> pure(+3)<*> [2,3,5]  -- 上と同じ
[5,6,8]
Prelude Control.Applicative> (+3)<$> [2,3,5]         -- 上と同じ
[5,6,8]

さて [a] というのは ++ 演算で閉じているので以下のようなものも F(a->a) となる。

 

Prelude Control.Applicative> :t     [(+3)]++[(*3)]
[(+3)]++[(*3)] :: Num a => [a -> a]

これは以下と同じ
Prelude Control.Applicative> :t     [(+3),(*3)]
[(+3),(*3)] :: Num a => [a -> a]

 

<*> を適用すると
Prelude Control.Applicative> [(+3),(*3)] <*> [2,4]
[5,7,6,12]

となる。

 

(<*>)[(+3),(*3)]  は fmap では表現できない。(fmap の象とならない)

Applicative Functor で F(a->b) の関数を Fa->Fb に導入できるようになった。

(単なる Functor では a->b の関数しか Fa->Fb に導入できない。最初 F(a->b) と pure(a->b) が同じだものだとおもっていた。ちがっていたのね。)

 

さらに「Applicative Functor なら一つの関数で複数のファンクターを続けざまに移せるのです!!」と言っておもむろにアプリカティブスタイルの話になるので??ってなった。

 

以下のような関数を考える

Prelude Control.Applicative> let f x y z = x+y+z
Prelude Control.Applicative> :t      f
f :: Num a => a -> a -> a -> a

 

この関数 f の <*>pure(f) を考えてみる。

関数 a -> a -> a -> a はカリー化関数なので

わかりやすくするため最初の3つの a を x,y,z と書くことにすと

    x->y->z->a 

= x->(y->z->a)

= x->(y->(z->a))

となり、

 

図を書いてみると

F = [ ]

        (<*>) pure(f0)

   [x] ----------------->  [y->(z->a)]

    ↑                                  ↑

    x ------------------> (y->(z->a))

                  f0=f

 

          (<*>) pure(f1)

  [y] -----------------> [z->a]

    ↑                             ↑

   y ------------------> (z->a)

                f1 =  f0(x)

 

         (<*>) pure(f2)

  [z] ------------------> [a]

    ↑                            ↑

   z  ------------------>  a

              f2 = f1(y)

 

pure(f2) <*> [z] = pure(f1 y) <*> [z]

                           = pure(f1) <*> [y] <*> [z]  (applicative 則  pure(fx) = pure(f)<*>pure(x) より)

                           = pure(f0 x) <*> [y] <*> [z]

                           = pure(f0) <*> [x] <*> [y] <*> [z] (上記applicative 則より)

                           =  f0 <$> [x] <*> [y] <*> [z]

                           = f <$>  [x] <*> [y] <*> [z]

 

Applicative 則を使っているからこのように続けざまに移すことができる。

単なる Functor ではできない。

 

Prelude Control.Applicative> f <$> [1,2,3]<*>[4,5,6]<*>[8,9,10]
[13,14,15,14,15,16,15,16,17,14,15,16,15,16,17,16,17,18,15,16,17,16,17,18,17,18,19]

Prelude Control.Applicative> f <$> [1]<*>[2,3]<*>[4,5,6]
[7,8,9,8,9,10]

 

最初  (+) <$> [1,2,3]<*>[2,3]   みたいなのがいまいちわからなかったが、

    (+) <$> [1,2,3] <*> [2,3]

=  [pure(1+),pure(2+),pure(+3)] <*> [2,3]
=  [3,4,4,5,5,6]

となるわけか。