Your SlideShare is downloading. ×
すごいHaskell読書会#10
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

すごいHaskell読書会#10

784

Published on

0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
784
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
5
Comments
0
Likes
3
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. すごいHaskellたのしく学ぼう!11. ファンクターからアプリカティブファンクターへ伊勢  シン / 伊藤  伸裕すごいHaskell読書会 in ⼤大阪#10 @ Fenrir Inc.
  • 2. はじめるまえに•  ファンクターそのもの、合点がいったのが昨⽇日–  なので結構間違いがありそう。答え合わせしたい。•  doitakaの⽇日記さんの下記のエントリが理理解する上でとても助けになった。–  関数がファンクターである、とは•  http://d.hatena.ne.jp/doitaka/20130609/1370782111–  Applicative、pure、関数についての再考、帰ってきた  Functor•  http://d.hatena.ne.jp/doitaka/20130613/1371146740–  ファンクターとアプリカティブファンクター(解決編)•  http://d.hatena.ne.jp/doitaka/20130618/1371571806
  • 3. 帰ってきたファンクター•  「帰ってきた」– そもそもファンクターってなんだったか。– 第7章でやった話•  「型や型クラスを⾃自分で作ろう」•  7.11 Functor型クラス
  • 4. Functor型クラス•  Functor型クラス– fmap だけをもっていて、具体的な実装がない•  「ある型  a から別の型  b への関数」と、•  「ある型  a に適⽤用さ  れたファンクター値」を取り、•  「別の型  b のほうに適⽤用されたファンクター値」を返す。•  「わけがわからなくても⼼心配無⽤用」で⾶飛ばしたけど、向きあわなければならない時が来たらしい。class Functor f wherefmap :: (a -> b) -> f a -> f b
  • 5. ファンクター値って•  Hoogle によると…– リストはファンクター– Maybe はファンクター– Either a b も、Either a まで部分適⽤用したらファンクターinstance Functor Maybe wherefmap _ Nothing = Nothingfmap f (Just a) = Just (f a)instance Functor (Either a) wherefmap _ (Left x) = Left xfmap f (Right y) = Right (f y)
  • 6. なんとなく⾒見見えた気がする•  Functor の「写す」とはこういう感じだろうか。fa fba -> bclass Functor f wherefmap :: (a -> b) -> f a -> f b
  • 7. なんとなく⾒見見えた気がする•  MaybeだったらMaybea Maybef af :: a -> binstance Functor Maybe wherefmap _ Nothing = Nothingfmap f (Just a) = Just (f a)
  • 8. ここまで7章の復復習•  「帰ってきた」– そもそもファンクターってなんだったか。– 第7章でやった話•  「型や型クラスを⾃自分で作ろう」•  7.11 Functor型クラス
  • 9. ここまで7章の復習
  • 10. cf. 第7回の練習問題https://gist.github.com/yashigani/5231928#-12
  • 11. ファンクターとしてのI/Oアクション•  IOアクションもファンクターreturn(f result)f :: a -> binstance Functor IO wherefmap f action = doresult <- actionreturn (f result)外界action resultIOアクションを実⾏行行result に束縛戻り値もIOアクションにならないといけないので、return  を使う。
  • 12. ファンクターとしてのI/Oアクション実例例•  ユーザーに⽂文字列列を⼊入⼒力力させて、逆順に出⼒力力•  fmap で書ける•  IOに対する名前付けを 1  こ減らせるので、よりエレガントに書ける。–  複数かけたいなら関数合成で (事前に定義・ラムダでもいい)main = do line <- getLinelet line = reverse lineputStrLn $ "You said " ++ line ++ " backwards!"        putStrLn $ "Yes, you said " ++ line ++ " backwards!"main = do line <- fmap reverse getLineputStrLn $ "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 bf は  (->) rfmap :: (a -> b) -> ((->) r a) -> ((->) r b)中置表記に直してfmap :: (a -> b) -> (r -> a) -> (r -> b)r -> a の出⼒力力を a -> b につなぐb . r $ aぶっちゃけこれって
  • 15. ファンクターとしての関数の実例例•  関数ファンクターの定義は  Control.Monad.Instances にあるのでためそう。– 232ページ
  • 16. 関数の持ち上げ(lifting)•  カリー化を再度度考えてみる– a -> b -> c– a 型を引数にとって (b -> c)  型を返す  ともとれる•  この⾒見見⽅方で  fmap をみると(a -> b) -> f a -> f b は(a -> b) -> (f a -> f b) ともみることができる。•  このような操作を「関数の持ち上げ」という。– ようは部分適⽤用のみかたを変えてみたってことで理理解あってますかね?
  • 17. 関数の持ち上げ(lifting) の例例•  部分適⽤用して名前つけたらわかりやすい– ちなみに例例がインパクトありすぎて原著あたったけど、これは⽇日本語版のみらしい。ghci> :set -XNoMonomorphismRestriction ghci> let shout = fmap (++"!")ghci> :t shoutshout :: Functor f => f [Char] -> f [Char] ghci> shout ["ha","ka","ta","no"]["ha!","ka!","ta!","no!"]f a -> f b になったことがわかる
  • 18. ファンクター則•  ファンクターは2つの性質を満たす必要がある•  第1法則–  id でファンクター値を写したら、ファンクター値は変化しない•  第2法則–  「f と g の合成関数でファンクター値を写したもの」と、「まず  g、次に  f でファンクター値を写したもの」が等しい–  fmap (f . g) x = fmap f (fmap g x)  が必ず成り⽴立立つこと•  Haskell ではこれらが成り⽴立立つかチェックされないので、ファンクター則が成り⽴立立たない Functor を作れてしまう。–  ⾃自分でテストする必要がある。–  もちろん標準の Functor 派⽣生型クラスは全部満たしてる。
  • 19. ファンクター第1法則をもう少し⾒見見てみる•  Just で考える– id は引数をそのまま返す関数– Nothing だった場合は Nothing になる。– Just x が⼊入った場合fmap id (Just x)Just (id x)Just xJust x は変化していない
  • 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 aa :: [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 = JustNothing <*> _ = Nothing(Just f) <*> something = fmap f something
  • 25. Maybeを試す•  Maybe の <*> を試してみる•  Just = pure になっているが、アプリカティブとして使うときにとどめたほうがよい。ghci> Just (+3) <*> Just 9 Just 12ghci> pure (+3) <*> Just 10 Just 13ghci> pure (+3) <*> Just 9Just 12ghci> Just (++"hahah") <*> NothingNothingghci> Nothing <*> Just "woot" Nothingfmap (+3) (Just 9)fmap (+3) (Just 10)fmap (+3) (Just 9)fmap (++"hahah") Nothingfmap Nothing (Just "woot")
  • 26. アプリカティブ・スタイル•  <*> の重ねがけ– 左結合しますpure (+) <*> Just 3 <*> Just 5Just ((+) 3) <*> Just 5Just ((+) 3 5)Just 8
  • 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]
  • 29. リストのアプリカティブを使う•  <*> をつかうと map の重ねがけがきれいに![(+),(*)] <*> [1,2] <*> [3,4][(1+), (2+), (1*), (2*)] <*> [3,4][(1+3), (1+4), (2+3), (2+4), (1*3), (1*4), (2*3), (2*4)][4, 5, 5, 6, 3, 4, 6, 8]
  • 30. リスト内包表記をアプリカティブスタイルで•  アプリカティブスタイルを使うと、リスト内包表記を置き換えられることが多々ある。ghci> [ x * y | x <- [2,5,10], y <- [8,10,11]] [16,20,22,40,50,55,80,100,110]ghci> (*) <$> [2,5,10] <*> [8,10,11] [16,20,22,40,50,55,80,100,110]
  • 31. IOもアプリカティブファンクター•  IOも Applicative のインスタンスがあるinstance Applicative IO where pure = returna <*> b= do f <- ax <- b return (f x)
  • 32. IOの<*>を使ってみるmyAction :: IO String myAction = doa <- getLineb <- getLine return $ a ++ bmyAction :: IO StringmyAction = (++) <$> getLine <*> getLine
  • 33. 関数もアプリカティブ•  関数だって Applicative のインスタンスがある•  pure は引数をそのまま返す関数 (idと同じ?)•  <*> の実装が呪⽂文っぽいけど、気にしなくてOK– アプリカティブに使えるということを認識識することが重要instance Applicative ((->) r) wherepure x = (_ -> x)f <*> g= x -> f x (g x)
  • 34. 関数をアプリカティブとして使うghci> :t (+) <$> (+3) <*> (*100) (+) <$> (+3) <*> (*100) :: (Num a) => a -> a ghci> (+) <$> (+3) <*> (*100) $ 5508
  • 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
  • 36. ZipListを使ってみる•  ZipListはShowをサポートしていないので、getZipListを使って⽣生のリストを得る必要がある。ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100] [101,102,103]ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..] [101,102,103]ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2] [5,3,3,4]ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat"[(d,c,r),(o,a,a),(g,t,t)]
  • 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 は重要
  • 38. まとめ•  雰囲気はつかめたものの、まだちゃんと理理解できてない。•  アプリカティブスタイルすごく便便利利そう。– 実際にはどういう活⽤用をするのか調べてみたい。

×