-
文脈を持った値に関数を適用する
f a
に(a -> b)
を適用したい
-
r -> a
からr -> b
にfmap :: (Functor f) => (a -> b) -> f a -> f b
-
文脈を持った値に文脈を持った関数を適用する
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
-
通常の値を文脈に入れる
pure
のこと
-
Applicative値は「文脈の追加された値」
> (*) <$> Just 2 <*> Just 8 Just 16 > (++) <$> Just "exdeath" <*> Nothing Nothing > (-) <$> [3, 4] <*> [1, 2, 3] [2, 1, 3, 3, 2, 1]
-
それぞれの文脈
Maybe
-> 失敗するかもしれない[a]
-> 複数の答えがあり得る計算(非決定性計算)IO a
-> 副作用を共なう計算
-
Applicative値の自然な拡張(強い版) 「普通の値aをとって文脈付きの値を返す関数に,文脈付きの値
m a
を渡したい」(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
-
Monadは
>>=
をサポートするApplicative Functor>>=
は**バインド(bind)**と呼ぶ
> Just (++ "&") <*> Just "yotsuba"
yotsuba&
> Nothing <*> Just "yanda"
Nothing
> (,) <$> Just "yotsuba" <*> Just "to-chan"
Just ("yotsuba", "to-chan")
> (,) <$> Just "yanda" <*> Nothing
Nothing
- Applicative Functorでは一度解いてから適用した
- 同じようにやってみる
applyMaybe :: Maybe a -> (a -> Maybe b) -> Maybe b
applyMaybe Nothing f = Nothing
applyMaybe (Just x) f = f x
使ってみる
> Just "yotsuba" `applyMaybe` \x -> Just (x ++ "&")
Just "yotsuba&"
> Nothing `applyMaybe` \x -> Just (x ++ "&")
Nothing
関数がNothing
を返す場合は?
> Just "yotsuba" `applyMaybe` \x -> if x == "yanda" then Nothing else Just (x ++ "&")
Just "yotsuba&"
> Just "yanda" `applyMaybe` \x -> if x == "yanda" then Nothing else Just (x ++ "&")
Nothing
- Applicative Functorは普通の関数を文脈付きの値に適用できた
- Monadは逆...
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y
fail :: String -> m a
fail msg = error msg
Applicative
のインスタンスではない
return
Applicative
でいうpure
IO
で使ったよね?
>>=
- バインド
- Monad値(文脈付きの値)に「通常の値を取るがMonad値を返す関数」を適用する
>>
- あとで
fail
- パターンマッチに失敗したときにハンドリングするやつ
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
> return "yotsuba" :: Maybe String
Just "yotsuba"
> Just "yotsuba" >>= \x -> Just (x ++ "&")
Just "yotsuba&"
> Nothing >>= \x -> Just (x ++ "&")
Nothing
- パターンマッチを使わずに
Maybe
から値を取り出したように見える - 文脈は維持している(
Nothing
のときはNothing
になる)
>>=
を繰り返し使ってMaybe a
型の値を貸す複数の計算を扱う
> landLeft 2 (0, 0)
(2, 0)
> landRight 1 (1, 2)
(1, 3)
> landRight -1 (1, 2)
(1, 1)
> land Left 2 (landRight 1 (landLeft 1 (0, 0)))
> (3, 1)
-
Maybe
で包んでやる> landLeft 2 (0, 0) Just (2,0) > landLeft 10 (0, 3) Nothing
-
落ちるようになったけど,
landLeft 1 (landRight 1 (0, 0))
みたいに書けなくなった -
>>=
を使えばいいよ> landRight 1 (0, 0) >>= landLeft 2 Just (2, 1) # Nothingを渡してみる > Nothing >>= landLeft 2 Nothing > return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2 Just (2,4) # さっきダメだった例を試す > return (0, 0) >>= landLeft 1 >>= landRight 4 >>= landLeft (-1) >>= landRight (-2) Nothing
-
Maybe
をApplicative値としてだけ扱うだけではここまではできない Applicative FunctorではApplicative値どうしを深く相互作用できない.できるのはせいぜい通常の関数にApplicative Styleで引数を渡すくらい -
ピエールの例は前の結果に依存している -> 前の結果を踏まえて今回の結果が検証される
-
ピエールを滑らせる
banana
の導入> return (0, 0) >>= landLeft 1 >>= banana >>= landRight 1 Nothing
-
既定のMonad値を返す関数
>>
(>>) :: (Monad m) => m a -> m b -> m b m >> n = m >>= \_ -> n
-
Maybe
で使ってみる> Nothing >> Just 3 Nothing > Just 3 >> Just 4 Just 4 > Just 3 >> Nothing Nothing
-
banana
の代わりに使ってみる> return (0, 0) >>= landLeft 1 >> Nothing >>= landRight 1 Nothing
-
もしMonadがなかったら
routine :: Maybe Pole routine = case landLeft 1 (0, 0) of Nothing -> Nothing Just pole1 -> case landRight 4 pole1 of Nothing -> Nothing Just pole2 -> case landLeft 2 pole2 of Nothing -> Nothing Just pole3 -> landLeft 1 pole3
oh...
-
IO
で出てきたdo
は他のMonadでも使える> Just 3 >>= (\x -> Just (show x ++ "!")) Just "3!" > Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y))) Just "3!"
-
let x = 3; y = "!" in show x ++ y
に似てる -
>>=
を使うほうはMonad値なので好きなところを失敗に置きかえられる> Nothing >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y))) Nothing > Just 3 >>= (\x -> Nothing >>= (\y -> Just (show x ++ y))) Nothing > Just 3 >>= (\x -> Just "!" >>= (\y -> Nothing)) Nothing
-
let
に似てる -
スクリプト風に書いてみる
foo :: Maybe String foo = Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y))) -- これはdo記法を使うとこう書ける foo' :: Maybe String foo' = do x <- Just 3 y <- Just "!" Just (show x ++ y)
do
式はlet
行以外は全てMoad- 結果を取り出すには束縛する
- 最後の行では使えない
- 受けるべきラムダ式が無いから
> Just 9 >>= (\x -> Just (x > 8))
Just True
と
marySue :: Maybe Bool
marySue = do
x <- Just 9
Just (x > 8)
を比べると,最後の結果がdo
式全体の結果になる仕組みがわかる
-
ピエールを
do
で渡らせてみるroutine :: Maybe Pole routine = do start <- return (0, 0) first <- landLeft 2 start second <- landRight 2 first landLeft 1 second
-
手続きっぽく見える!
- 前の行の結果に依存した値の列が文脈と一緒に書いてあるだけ
-
バナナを踏ませたければ...
routine :: Maybe Pole routine = do start <- return (0, 0) first <- landLeft 2 start Nothing second <- landRight 2 first landLeft 1 second
-
束縛せずにMonadを使うと,
>>
をつけたのと同じ結果になる -
do
を使うか>>=
を使うかはお好きに!
-
do
式ではパターンマッチが使えるjustH :: Maybe Char justH = do (x:xs) <- Just "hello" return x
-
失敗したときどうなるの?
- パターンマッチは失敗したら次のパターンを試す
let
は複数のパターンを試せなかった -> 即座にエラーdo
ではfail
が使われるMaybe
ではfail _ = Nothing
ってなってる
-
Applicativeとしてのリストは非決定性計算を表す
> (*) <$> [1, 2, 3] <*>[10, 100, 1000] [10, 100, 1000, 20, 200, 2000, 30, 300, 3000]
-
Monadならもっとうまく書ける
instance Monad [] where return x = [x] xs >>= f = concat (map f xs) fail _ = []
-
使ってみる
> [3, 4, 5] >>= \x -> [-x, x] [-3, 3, -4, 4, -5, 5]
-
つまりこういうこと
[3, 4, 5] >>= \x -> [-x, x] concat (map (\x -> [-x, x]) [3, 4, 5]) concat [[-3, 3], [-4, 4], [-5, 5]] [-3, 3, -4, 4, -5, 5]
-
空リストのときは?
> [] >>= \x -> ["bad", "mad", "red"] [] > [1, 2, 3] >>= \x -> [] []
-
連結させてみる
> [1, 2] >>= \n -> ['a', 'b'] >>= \ch -> return (n, ch) [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
-
do
記法でlistOfTuples :: [(Int, Char)] listOfTuples = do n <- [1, 2] ch <- ['a', 'b'] return (n, ch)
-
さっきのはリスト内包表記に似てる
> [(n, ch) | n <- [1, 2], ch <- ['a', 'b']] [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
-
実はリストモナドのSyntax Suger
-
リスト内包表記ではfilterができる
> [x | x <- [1..50], '7' `elem` show x] [7, 17, 27, 37, 47]
-
guard
関数とMonadPlus
型クラス Monoidの性質をあわせ持つ型クラスclass Monad m => MmonadPlus m where mzero :: m a mplus :: m a => m a -> m a
mzero
= mempty
mplus
= mappend
-
リストはMonoidなので
MonadPlus
のインスタンスにできるinstance MonadPlus [] where mzero = [] mplus = (++)
-
一方
guard
はこんな感じguard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero
True
なら()
を最小の文脈に入れ,False
なら失敗を作る> guard (5 > 2) :: Maybe () Just () > guard (1 > 2) :: Maybe () Nothing > guard (5 > 2) :: [()] [()] > guard (1 > 2) :: [()] []
-
リスト内包表記っぽく
> [1..50] >>= (\x -> guard ('7' `elem` show x) >> return x) [7, 17, 27, 37, 47]
-
guard
を>>
につないでみる> guard (5 > 2) >> return "cool" :: [String] ["cool"] > guard (1 > 2) >> return "cool" :: [String] []
-
do
で書き直すsevensOnly :: [Int] sevensOnly = do x <- [1..50] guard ('7' `elem` show x) return x
-
return
したものを>>=
した場合は,元の値に関数を適用した場合と同じ> return 3 >>= (\x -> Just (x + 100000)) Just 100003 > (\x -> Just (x + 100000)) 3 Just 100003 > return "WoM" >>= (\x -> [x, x, x]) ["WoM", "WoM", "WoM"] > (\x -> [x, x, x]) "WoM" ["WoM", "WoM", "WoM"]
-
>>=
を使ってMonad値をreturnに食わせても何も起こらない> Just "move on up" >>= return Just "move on up" > [1, 2, 3, 4] >>= return [1, 2, 3, 4] > putStrLn "Wah!" >>= return Wah!
-
>>=
の連鎖があるときに,どの順序で評価しても結果は同じ- つまり
(m >>= f) >>= g
とm >>= (\x -> f x >>= g)
が等価
- つまり
-
以下の式は等価
- 評価される順番は関係無い
> return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2 Just (2, 4) > return (0, 0) >>= (\x -> landRight 2 x >>= (\y -> landLeft 2 y >>= (\z -> landRight 2 z))) Just (2, 4)
-
関数合成を思い出してみる
(.) :: (b -> c) -> (a -> b) -> (a -> c) f . g = (\x -> f (g x))
-
fとgにMonad関数を渡すことを考える
>>=
使えばいける
(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c) f <=< g = (\x -> g x >>= f)