【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
大切だが、最初はあまり気にしなくていい、と思う。