4. Functor型クラス
• Functor型クラス
– fmap だけをもっていて、具体的な実装がない
• 「ある型 a から別の型 b への関数」と、
• 「ある型 a に適⽤用さ れたファンクター値」を取り、
• 「別の型 b のほうに適⽤用されたファンクター値」を返す。
• 「わけがわからなくても⼼心配無⽤用」で⾶飛ばしたけど、
向きあわなければならない時が来たらしい。
class Functor f where
fmap :: (a -> b) -> f a -> f b
5. ファンクター値って
• Hoogle によると…
– リストはファンクター
– Maybe はファンクター
– Either a b も、
Either a まで部分適⽤用したらファンクター
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
11. ファンクターとしてのI/Oアクション
• IOアクションもファンクター
return
(f result)f :: a -> b
instance Functor IO where
fmap f action = do
result <- action
return (f result)
外界
action result
IOアクションを実⾏行行
result に束縛
戻り値もIOアクションに
ならないといけないので、return を使う。
12. ファンクターとしてのI/Oアクション実例例
• ユーザーに⽂文字列列を⼊入⼒力力させて、逆順に出⼒力力
• fmap で書ける
• IOに対する名前付けを 1 こ減らせるので、
よりエレガントに書ける。
– 複数かけたいなら関数合成で (事前に定義・ラムダでもいい)
main = do line <- getLine
let line' = reverse line
putStrLn $ "You said " ++ line' ++ " backwards!"
putStrLn $ "Yes, you said " ++ line' ++ " backwards!"
main = do line <- fmap reverse getLine
putStrLn $ "You said " ++ line ++ " backwards!"
putStrLn $ "Yes, you really said " ++ line ++ " backwards!"
13. ファンクターとしての関数
• (->) r もファンクター
– え?
• r -> a は (->) r a とも書ける
– (->) は2つの型をとる型コンストラクタと⾒見見ることができ
る。
– よって、部分適⽤用して、(->) r はファンクターになれる。
14. ファンクターとしての関数
fmap :: (a -> b) -> f a -> f b
f は (->) r
fmap :: (a -> b) -> ((->) r a) -> ((->) r b)
中置表記に直して
fmap :: (a -> b) -> (r -> a) -> (r -> b)
r -> a の出⼒力力を a -> b につなぐ
b . r $ a
ぶっちゃけこれって
16. 関数の持ち上げ(lifting)
• カリー化を再度度考えてみる
– a -> b -> c
– a 型を引数にとって (b -> c) 型を返す ともとれる
• この⾒見見⽅方で fmap をみると
(a -> b) -> f a -> f b は
(a -> b) -> (f a -> f b) ともみることができる。
• このような操作を「関数の持ち上げ」という。
– ようは部分適⽤用のみかたを変えてみたってことで理理解
あってますかね?
20. 法則を破っちゃう例例
• 法則を破っている CMaybe 型を定義する
• fmap の戻り値の counter を、fmap にかけると + 1
される実装になっている
– f を id にしたら、前後で値が変わってしまう
CJust counter x → CJust (counter + 1) x
• 第1法則を満たさない
• ファンクター則を満たさないファンクターはバグを⽣生む
– ファンクターであることを信じて使ったらえらい⽬目に合う。
data CMaybe a = CNothing | CJust Int a deriving (Show)
instance Functor CMaybe where
fmap f CNothing = CNothing
fmap f (CJust counter x) = CJust (counter+1) (f x)
21. アプリカティブファンクター (1)
• fmap の f に2引数関数を突っ込むと、
ファンクター値の中に関数が⼊入ったものができる。
ghci> :t fmap (++) (Just "hey")
fmap (++) (Just "hey") :: Maybe ([Char] -> [Char])
ghci> :t fmap compare (Just 'a')
fmap compare (Just 'a') :: Maybe (Char -> Ordering)
ghci> :t fmap compare "A LIST OF CHARS"
fmap compare "A LIST OF CHARS" :: [Char -> Ordering]
ghci>:t fmap(x y z -> x + y / z)[3,4,5,6]
fmap (x y z -> x + y / z) [3,4,5,6] :: (Fractional a) => [a -> a -> a]
22. アプリカティブファンクター (2)
• 使い⽅方としては、ファンクター値の中の関数を
引数にとる関数に、fmap すること。
• このアプローチでは限界がある
– Just (3 *) の中の関数を Just 5 に対して適⽤用して、
Just 15 にすることはできない。
ghci> let a = fmap (*) [1,2,3,4]
ghci> :t a
a :: [Integer -> Integer]
ghci> fmap (f -> f 9 ) a
[9,18,27,36]
ghci> fmap ($9) a [9,18,27,36]
23. アプリカティブファンクター (3)
• そこで登場する Control.Applicative
• pure の f がアプリカティブファンクターとなるもの
– アプリカティブ値という
• <*> がさっきの Just 15 を解決できる関数
– 関数の⼊入ったファンクター値 と
値の⼊入ったファンクター値 を引数にとって、
値に関数を適⽤用したファンクター値 を返す。
• そしてもれなく Functor でもあるので fmap も使える
class (Functor f) => Applicative f where
pure:: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
24. Maybeはアプリカティブファンクター
• Maybe の Applicative インスタンスをみてみる
– Hoogle でみたらなんか違うことになってるので後で解決したい
• pure (アプリカティブ値) は Just
• 「関数の⼊入ったファンクター」として Nothing を
渡したら Nothing
• 「関数の⼊入ったファンクター」に Just f を渡すと、
第2引数に fmap f する
– 第2引数もファンクター値なので、fmap の第2引数にそのまま
渡せる
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
25. Maybeを試す
• Maybe の <*> を試してみる
• Just = pure になっているが、アプリカティブとして
使うときにとどめたほうがよい。
ghci> Just (+3) <*> Just 9
Just 12
ghci> pure (+3) <*> Just 10
Just 13
ghci> pure (+3) <*> Just 9
Just 12
ghci> Just (++"hahah") <*> Nothing
Nothing
ghci> Nothing <*> Just "woot"
Nothing
fmap (+3) (Just 9)
fmap (+3) (Just 10)
fmap (+3) (Just 9)
fmap (++"hahah") Nothing
fmap Nothing (Just "woot")
27. pure f <*> x = fmap f x = f <$> x
• pure f <*> x は fmap f x と同じ
• なので、pure f <*> x <*> y … は
fmap f x <*> y … ともかける。
• よく使うので、Control.Applicative に、
fmap f x を f <$> x と書けるよう
定義してくれている。
ghci> (++) <$> Just "johntra" <*> Just "volta"
Just "johntravolta"
28. リストもアプリカティブ
• リストも Applicative のインスタンスがある
– pure は値を持てる最⼩小の⽂文脈なので、
1要素のリストとなる。
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]
32. IOの<*>を使ってみる
myAction :: IO String
myAction = do
a <- getLine
b <- getLine
return $ a ++ b
myAction :: IO String
myAction = (++) <$> getLine <*> getLine
33. 関数もアプリカティブ
• 関数だって Applicative のインスタンスがある
• pure は引数をそのまま返す関数 (idと同じ?)
• <*> の実装が呪⽂文っぽいけど、気にしなくてOK
– アプリカティブに使えるということを認識識することが重要
instance Applicative ((->) r) where
pure x = (_ -> x)
f <*> g= x -> f x (g x)
34. 関数をアプリカティブとして使う
ghci> :t (+) <$> (+3) <*> (*100)
(+) <$> (+3) <*> (*100) :: (Num a) => a -> a
ghci> (+) <$> (+3) <*> (*100) $ 5
508
35. ZipList
• [(+3),(*2)] <*> [1,2] ってやったら
[3+1, 3+2, 2*1, 2*2] として [4,5,2,4] じゃなくて
[1+3, 2*2] として [4, 4] となってほしい
• そこで登場するのが ZipList
– 当然これもアプリカティブです
instance Applicative ZipList where
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith (f x -> f x) fs xs
37. アプリカティブ則
• アプリカティブファンクターは以下の法則を持つ
– pure f <*> x = fmap f x
– pure id <*> v = v
– pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
– pure f <*> pure x = pure (f x)
– u <*> pure y = pure ($ y) <*> u
• 特に pure f <*> x = fmap f x は重要