takafumi blog

日々の勉強メモ

【Haskell】 ファンクター、アプリカティブの簡単まとめ

環境   ghc 7.8.3 CentOS7.0

「すごいHaskellたのしく学ぼう!」第10,11章 メモ

ファンクター、アプリカティブのとりあえず、最低限必要そうな部分をまとめ。

でも間違いなく、こちらの方が、はるかに分かりやすいです。

なので、以下は自分用のまとめです。


Functor(ファンクター)
▽定義
instance Functor IO where
    fmap f action = do
    result <- action
    return (f result)


▽Functorとは、値が入った、ある箱(状態)である
Just 3 -- Just(Maybe)という箱に値が入っている

[3]    -- Listという箱に値が入っている


▽箱には直接、関数を適応できない。なのでfmapを使う
ghci> Just 3 + 3

<interactive>:221:1:
    No instance for (Num (Maybe a0)) arising from a use of ‘it’
    In a stmt of an interactive GHCi command: print it

ghci> fmap (+3) (Just 3)
Just 6


▽fmap と <$>は同じ

fmapの別表記(周囲表記)が<$>

ghci> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
ghci> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b


▽Functorのインスタンスにするにはfmapを実装すればよい
-- Maybeのファンクター実装
instance Functor Maybe where
    fmap f (Just x) = Just (f x)
    fmap f Nothing = Nothing


▽関数もFunctor。関数のfmapは関数合成と同じ
-- (->)のFunctor定義
instance Functor ((->) r) where  
    fmap f g = f . g

つまりfmap (+3) (+1)(+3) . (+1)と同じという事


▽Functorはファンクター則を満たす必要がある

・第一法則

id でファンクター値を写した場合、ファンクター値が変化してはいけない
つまりfmap id = idを満たす

・第二法則

「 f と g の合成関数でファンクター値を写したもの」と、「まず g 、次に f でファンクター値を写したもの」が等しいことを要求する
つまりfmap (f . g) = fmap f . fmap gを満たす


Applicative Functor(アプリカティブファンクター)
▽Applicativeとは?
class Functor f => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

コレを定義したインスタンスが、アプリカティブファンクター。

2引数以上の関数の入ったFunctorと、他のFunctorを作用させるために定義された、Functorの拡張定義と考えれる。

1引数関数はfmapを普通に使える

ghci> fmap (+1) (Just 1)
Just2

しかし2引数関数だと

ghci> :t fmap (+) (Just 1)
fmap (+) (Just 1) :: Num a => Maybe (a -> a)

これは、Just (+1)となっている。

これと、別のMaybeを作用させるにはどうするか?
そこで、Applicativeを用いる

ghci> import Control.Applicative

ghci Control.Applicative> let justPlus1 = fmap (+) (Just 1)
ghci Control.Applicative> :t justPlus1
justPlus1 :: Num a => Maybe (a -> a)

ghci Control.Applicative> justPlus1 <*> (Just 1)
Just 2

3引数関数でも

ghci Control.Applicative> fmap (,,) (Just 1) <*> (Just 2) <*> (Just 3)
Just (1,2,3)

<*>をつなげればよい。


▽アプリカティブ・スタイル

<*><$>を続けざまにもちいる事をアプリカティブ・スタイルと呼ぶ

ghci Control.Applicative> (,,) <$> (Just 1) <*> (Just 2) <*> (Just 3)
Just (1,2,3)


▽liftA2は、通常値の関数をFunctorに適用できる関数に変換する
ghci Control.Applicative> :t liftA2
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c

ghci Control.Applicative> :t (+)
(+) :: Num a => a -> a -> a

ghci Control.Applicative> :t liftA2 (+)
liftA2 (+) :: (Applicative f, Num c) => f c -> f c -> f c

一番上の:t liftA2の見方を変えると、

Applicative f => (a -> b -> c) -> (f a -> f b -> f c)

となる。つまり、通常関数をファンクターに適用できる関数への変換になっている。

当たり前だが、liftA2はアプリカティブスタイルでも書ける

ghci Control.Applicative> liftA2 (:) (Just 1) (Just [3])
Just [1,3]

ghci Control.Applicative> (:) <$> Just 1 <*> Just [3]
Just [1,3]


▽アプリカティブ則

アプリカティブファンクターは以下を満たす必要がある。

pure id <*> v = v
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u

大切だが、最初はあまり気にしなくていい、と思う。


takafumi-s.hatenablog.com