Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

第13章 モナドがいっぱい

13.1 アプリカティブファンクターを強化する

Functor → Applicative Functor → Monad

Functor

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

ghci> fmap (2*) (Just 8)
Just 16
ghci> fmap (2*) Nothing
Nothing

★ 普通の関数を、箱に入った値に適用して、箱に入った別の値にできる!

中置記法(ちゅうちきほう、infix notation)での書き方もあるよ:

ghci> (2*) `fmap` Just 8
Just 16
ghci> (2*) `fmap` Nothing
Nothing

ghci> import Control.Applicative
ghci> (2*) <$> Just 8
Just 16
ghci> (2*) <$> Nothing
Nothing

Applicative Functor

Functor を強化

ghci> import Control.Applicative
ghci> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

ghci> Just (2*) <*> Just 8
Just 16
ghci> Just (2*) <*> Nothing
Nothing

<*> は ap と呼ばれる

★ Functorに包まれた関数をFunctorに包まれた値に適用できる!

pure を使って書き直す

ghci> pure (2*) <*> Just 8
Just 16
ghci> pure (2*) <*> Nothing
Nothing

ghci> pure (*) <*> Just 2 <*> Just 8
Just 16
ghci> pure (*) <*> Just 2 <*> Nothing
Nothing

ghci> (*) <$> Just 2 <*> Just 8
Just 16
ghci> (*) <$> Just 2 <*> Nothing
Nothing

★ 普通の多引数関数を、文脈をもった値たちに、文脈を保ったままま適用できる!

引数が増えても大丈夫。

ghci> (,,) <$> Just 2 <*> Just 4 <*> Just 6
Just (2,4,6)
ghci> (,,,) <$> Just 2 <*> Just 4 <*> Just 6 <*> Just 8
Just (2,4,6,8)

Monad

文脈の、それぞれのクラスインスタンスでの意味合い:

  • Maybe a ... 失敗するかもしれない計算
  • [a] ... 複数の答があり得る計算(非決定性計算)
  • IO a ... 副作用を伴う計算

★ 普通の値 a を取って文脈付きの値を返す関数に、文脈付きの値 m a を渡したい! </style>

ghci> import Control.Monad
ghci> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

>>= はバインド(bind)と呼ぶ。

_人人人人人人人人人人人_
> あれにしか見えない <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

(>>= の写真は http://qiita.com/suin/items/0255f0637921dcdfe83b にあります)

Functor, Applicative, Monad

おさらい

(<$>) :: Functor f     =>   (a -> b) -> f a        -> f b
(<*>) :: Applicative f => f (a -> b) -> f a        -> f b
(>>=) :: Monad m       => m a        -> (a -> m b) -> m b

13.2 Maybeから始めるモナド

実は Maybe はモナドだったんです!

Maybe a は a 型の値、ただし、失敗する可能性という文脈を持っているもの。

fmap の場合

--- Just 値だったら中身の値に適用
ghci> fmap (++"!") (Just "wisdom")
Just "wisdom!"
--- Nothing だったら Nothing のまま。だって関数を適用する相手が居ないんですから!
ghci> fmap (++"!") Nothing
Nothing

ap の場合

--- 関数と値が両方 Just なら、結果も Just になる。
ghci> Just (+3) <*> Just 3
Just 6
--- 関数か値のどちらかが欠けていれば、結果をでっち上げるわけにはいかないので Nothing
ghci> Nothing <*> Just "greed"
Nothing
ghci> Just Data.Char.ord <*> Nothing
Nothing

--- アプリカティブスタイルで普通の関数をMaybe値に適用するときも同じ
ghci> max <$> Just 3 <*> Just 6
Just 6
ghci> max <$> Just 3 <*> Nothing
Nothing
ghci> max <$> Nothing <*> Just 6
Nothing

>>= の自然な定義を考える。

--- 「モナド値」と「普通の値を取る関数」を引数に取り、
--- なんとかしてその関数をモナド値に適用したい
(>>=) :: Monad m => m a -> (a -> m b) -> m b

--- 普通の値を取る関数(と普通の値に適用する)の例
ghci> (\x -> Just (x+1)) 3
Just 4

自然な定義

  • Just 値が来たら、その中身を取り出して関数を適用
  • Nothing値が来た時は、適用すべき値gないので結果をNothingとする
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= _ = Nothing
Just x  >>= f = f x
--- Just 値を食わせると、その中の値に関数を適用する
ghci> Just 3 >>= \x -> Just (x+1)
Just 4
--- Nothingを食わせると、結果もNothingになる
ghci> Nothing >>= \x -> Just (x+1)
Nothing
--- Just値を食わせて関数が Nothing を返す場合もある
ghci> Just 1 >>= \x -> if x > 2 then Just x else Nothing
Nothing

Maybe をアプリカティブとして使ったときと似ている (式のどこかにNothing があったら答も Nothing)

何が便利なの?→ 13.4 で見る。

13.3 Monad型クラス

class Monad m where
    return :: a -> m a

    (>>=) :: m a -> (a -> m b) -> m b
    x >> y = x >>= \_ -> y

    fail :: String -> m a
    fail msg = error msg

歴史的経緯により Applicative の型制約は付いていないが、 すべての Monad は Applicative Functor である。

cf. Functor から Applicative Functor へ

class Functor f => Applicative f where
    ...

メンバ関数

  • return ... Applicative の pure と同じもの。第8章の IO でも出てきてた。
  • >>= ... またの名をバインド。モナド値に、通常の値を取りモナド値を取る関数を適用。
  • >> ... デフォルト実装あり。13.4節でバナナとして出てくる。
  • fail ... もっぱらHaskellシステムが呼び出す。後ほど13.5節で見る。

Maybe モナド

instance Monad Maybe where
    return x = Just x
    Nothing >>= _ = Nothing
    Just x  >>= f = f x
    fail _ = Nothing

遊んでみる

--- return は Applicative の pure と同じ
ghci> return "WHAT" :: Maybe String
Just "WHAT"
--- bind
ghci> Just 9 >>= \x -> return (x*10)
Just 90
ghci> Nothing >>= \x -> return (x*10)
Nothing

13.4 綱渡り

失敗するかもしれないという文脈を持つ Maybe a に対して、 >>= を繰り返し使って、Maybe a 値を返す複数の計算を扱う方法を見ていく。

ピエールくん登場。

  • 養魚場で働いている。休暇を取って綱渡りに挑戦。
  • バランス棒の左あるいは右に鳥がとまりにくる。
  • 左右にとまっている鳥の数の差が3以内ならバランスを取れる。
  • 差が3を超えるとバランスを崩してぶざまに転落。(安全ネットがあるので怪我はしない)

鳥たちが来たり去ったりした後にピエールくんが綱の上に居られるかを判定する プログラムを書いてみる。

まず、型シノニムを使って、バランス棒を整数のペアとして表現

--- 鳥の数
type Birds = Int
--- バランス棒 = (左側の鳥の数, 右側の鳥の数)
type Pole = (Birds, Birds)

次に、バランス棒の左側もしくは右側に鳥をとまらせる関数を作る

--- バランス棒の左側に鳥がとまる
landLeft :: Birds -> Pole -> Pole
landLeft n (left, right) = (left + n, right)
--- バランス棒の右側に鳥がとまる
landRight :: Birds -> Pole -> Pole
landRight n (left, right) = (left, right + n)

試してみる

--- 左側に2羽とまる
ghci> landLeft 2 (0, 0)
(2,0)
--- 右側に1羽とまる
ghci> landRight 1 (1, 2)
(1,3)
--- 鳥を飛び立たせる処理は、負の数がとまる処理で代用
ghci> landRight (-1) (1, 2)
(1,1)

--- 繰り返し
ghci> landLeft 2 (landRight 1 (landLeft 1 (0, 0)))
(3, 1)

見やすくするために、バランス棒を先に書いて、鳥をとまらせる関数を後に書く記法を導入。

x -: f = f x

関数を適用するのに、まず引数、次に関数を書けるようになった

ghci> 100 -: (*3)
300
ghci> True -: not
False
ghci> (0, 0) -: landLeft 2
(2,0)

これを使うと先ほどの処理は読みやすく書き直せる

ghci> (0, 0) -: landLeft 1 -: landRight 1 -: landLeft 2
(3, 1)

うゎぁあああああ落ちるぅぅうううああああ

片方に一気に10羽の鳥が来たらどうなる?

ghci> landLeft 10 (0, 3)
(10, 3)
--- ピエールは確実に宙を舞う!

次の場合はどう?

ghci> (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
(0, 2)
--- 一見、問題なさそうだが...
(0, 0) → (1, 0) → (1, 4) → (0, 4) あ!

_人人人人人人人人_
> 落ちている! <
 ̄Y^Y^Y^Y^Y^Y^Y ̄

Maybe を使い、失敗するかもしれないという文脈で表現。

landLeft :: Birds -> Pole -> Maybe Pole
landLeft n (left, right)
    | abs ((left + n) - right) < 4 = Just (left + n, right)
    | otherwise                    = Nothing

landRight :: Birds -> Pole -> Maybe Pole
landRight n (left, right)
    | abs (left - (right + n)) < 4 = Just (left, right + n)
    | otherwise                    = Nothing

ガード記法を使って、更新後の左右の鳥の数の差が4より小さいか判定している。

  • もし4より小さいなら、新しいバランス棒の状態を Just に包んで返す
  • 差が4以上になった場合は、失敗を意味する Nothing を返す

使ってみる

ghci> landLeft 2 (0, 0)
Just (2,0)
ghci> landLeft 10 (0, 3)
Nothing

鳥を止まらせる操作を -: で繋げていく能力は失われた? (戻り値は Pole 型でなく Maybe Pole 型)

何とかして、Pole を取って Maybe Pole を返す関数に、 Maybe Pole 型を渡したい!

それ、>>= (bind) でできるよ!

ghci> landRight 1 (0, 0) >>= landLeft 2
Just (2,1)
ghci> Nothing >>= landLeft 2
Nothing
ghci> return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2
Just (2,4)
{-
--- ピエール・シミュレーターに「失敗するかもしれない計算という文脈」を導入する前の例
ghci> (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
(0, 2)
--- 実は落ちている。
-}
ghci> return (0, 0) >>= landLeft 1 >>= landRight 4 >>= landLeft (-1) >>= landRight (-2)
Nothing
--- ちゃんと失敗を検出できてる!

ロープの上のバナナ

バランス棒にとまっている鳥の数によらず、 いきなりピエールを滑らせて落っことす関数を作ってみよう。

banana :: Pole -> Maybe Pole
banana _ = Nothing

この関数は、鳥をとまらせる関数と混ぜて使える

ghci> return (0, 0) >>= landLeft 1 >>= banana >>= landRight 1
Nothing

入力に関係なく既定のモナド値を返す関数が用意されている

(>>) :: (Monad m) => m a -> m b -> m b
m >> n = m >>= \_ -> n

Maybe でこれを使うと、

ghci> Nothing >> Just 3
Nothing
ghci> Just 3 >> Just 4
Just 4
ghci> Just 3 >> Nothing
Nothing

--- banana を使った例の書き換え
ghci> return (0, 0) >>= landLeft 1 >> Nothing >>= landRight 1
Nothing

ところで、Maybe を失敗の文脈付きの値として扱って関数に食わせるという懸命な選択をしなかったら、 どうなっていただろうか?

バランス棒に鳥をとまらせる一連の処理

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
--- ちゃんと Just (4, 4) が返るけど、辛い…

Maybeモナドの >>= を使うことで、「失敗するかもしれない計算」という文脈を保って、 すっきりした記述で、関数適用を繋げていくことができた。

13.5 do記法

Haskell にとってモナドはとても便利→専用構文が用意されている→その名は do 記法。

8章で複数の I/O アクションを 1 つにしたのと同じく、複数のモナド値を糊付けするのに使える。

--- 見飽きた例
ghci> Just 3 >>= (\x -> Just (show x ++ "!"))
Just "3!"
--- bind の入れ子
ghci> Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))
Just "3!"

これを見ていると let 構文を思い出しますよね

ghci> let x = 3; y = "!" in show x ++ y
"3!"

この2つの例は大きな違いがある。

  • let構文の方で使っている値は普通の値
  • >>= の方で使っている値はモナド値、つまり失敗の文脈を持つ

好きな箇所を失敗で置き換えられる

ghci> Nothing >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))
Nothing
ghci> Just 3 >>= (\x -> Nothing >>= (\y -> Just (show x ++ y)))
Nothing
ghci> Just 3 >>= (\x -> Just "!" >>= (\y -> Nothing))
Nothing

改行を入れてかいてみる。

foo :: Maybe String
foo = Just 3   >>= (\x ->
      Just "!" >>= (\y ->
      Just (show x ++ y)))

--- Just "3!"

ラムダがいっぱい → do 記法ですっきり書き直せる

foo :: Maybe String
foo = do
    x <- Just 3
    y <- Just "!"
    Just (show x ++ y)

--- Just "3!"

いずれかが Nothing だったら、 do 式全体も Nothing

foo :: Maybe String
foo = do
    x <- Nothing :: Maybe String
    y <- Just "!"
    Just (show x ++ y)

--- Nothing

do 自由自在

  • do 式 (do expression) は、let 行を除いてすべてモナド値で構成される。
  • モナドの結果を調べるには <- を使う。
  • 変数を <- で Maybe String の結果に束縛(bind)すると、変数の型は String になる
  • >>= を使ってモナド値をラムダに食わせたときと全く同じ。
  • do 式の最後のモナド値だけは <- で結果を束縛できない。
  • それを受けるべきラムダがないから。
  • 最後のモナドの返り値は、それまでの失敗の可能性をすべて踏まえた上で、 do 式で糊付けしたモナド全体の結果になる。

ghci> Just 9 >>= (\x -> Just (x > 8))
Just True

do 記法で書き直すと

marySue :: Maybe Bool
marySue = do
    x <- Just 9
    Just (x > 8)

帰ってきたピエール

routine :: Maybe Pole
routine = do
    start <- return (0, 0)
    first <- landLeft 2 start
    second <- landRight 2 first
    landLeft 1 second

--- Just (3,2)

banana の皮を置いとくと、

routine :: Maybe Pole
routine = do
    start <- return (0, 0)
    first <- landLeft 2 start
    Nothing
    second <- landRight 2 first
    landLeft 1 second

--- Nothing

パターンマッチと失敗

do 式におけるパターンマッチ

justH :: Maybe Char
justH = do
    (x:xs) <- Just "hello"
    return x

--- Just 'h'

do 式の中でパターンマッチが失敗した場合、Monad 型クラスの一員である fail 関数が使われる。

デフォルトでは、 fail はプログラムを異常終了させる。

fail :: (Monad m) => String -> m a
fail msg = error msg

Maybe モナドの場合

fail _ = Nothing

パターンマッチに失敗するような do 式をわざと書いてみる

wopwop :: Maybe Char
wopwop = do
    (x:xs) <- Just ""
    return x

--- Nothing が返る (Maybe モナドの文脈での失敗発生)

異常終了して全プログラムが巻き添えになるよりもうれしいよね!

13.6 リストモナド

  • Maybe ... 失敗の文脈の付いた値
  • リスト ... 非決定性計算

たくさんの選択肢とたくさんの選択肢の組み合わせ→もっとたくさんの選択肢

ghci> (*) <$> [1,2,3] <*> [10,100,1000]
[10,100,1000,20,200,2000,30,300,3000]

リストの非決定性という文脈は、うまくモナドに焼き直すことができます。

instance Monad [] where
    return x = [x]
    xs >>= f = concat (map f xs)
    fail _ = []
  • return は pure と同じなので、値を取って、最小限の文脈に入れるもの。
  • >>= は「文脈付きの値(モナディックな値)」を「通常の値を取って文脈付きの値を返す関数」に 食わせる演算
ghci> [3, 4, 5] >>= \x -> [x, -x]
[3,-3,4,-4,5,-5]

{-
concat (map f [3, 4, 5])
= concat [[3,-3],[4,-4],[5,-5]]
= [3,-3,4,-4,5,-5]
-}

空リスト [] は、返すべき値が何もない、ということなので、Maybe の場合の Nothing と似てる。

--- [] が現れたら、どんな関数が来ても [] が返る
ghci> [] >>= \x -> ["bad","mad","rad"]
[]
--- 関数が常に [] を返す場合
ghci> [1,2,3] >>= \x -> []
[]

Maybeのときと同様に、>>= を使えばリストをいくつでも連結して非決定性を伝播させることができる

ghci> [1, 2] >>= \n -> ['a', 'b'] >>= \ch -> return (n, ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

{-
concat (map (\n -> ['a', 'b'] >>= \ch -> return (n, ch)) [1, 2])
= concat [['a', 'b'] >>= \ch -> return (1, ch), ['a', 'b'] >>= \ch -> return (2, ch)]
= concat [concat (map (\ch -> return (1, ch)) ['a', 'b']), concat (map (\ch -> return (2, ch)) ['a', 'b'])]
= concat [concat [[(1, 'a')], [(1, 'b')]], concat [[(2, 'a')], [(2, 'b')]]]
= concat [[(1, 'a'), (1, 'b')], [(2, 'a'), (2, 'b')]]
= [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
-}

do 記法

listOfTuples :: [(Int, Char)]
listOfTuples = do
    n <- [1,2]
    ch <- ['a','b']
    return (n,ch)

{-
ghci> listOfTuples
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
-}

do 記法とリスト内包表記

リストの do 記法を見てピントきたあなた、これを見て下さい。

ghci> [ (n, ch) | n <- [1, 2], ch <- ['a', 'b'] ]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

リスト内包表記はリストモナドの構文糖衣(syntax sugar)にすぎないのです!

MonadPlus と guard 関数

リスト内包表記では、出力する要素を選別 (filter) することができました。

--- 7 の付く数だけを選んで出す
ghci> [ x | x <- [1..50], '7' `elem` show x ]
[7,17,27,37,47]

この選別はどんなリストモナドに翻訳されるのでしょうか?

それを知るために guard 関数と MonadPlus 型クラスを学ぶ。

class Monad m => MonadPlus m where
    -- Monoid 型クラスにおける mempty に対応するもの
    mzero :: m a
    -- Monoid 型クラスにおける mappend に対応するもの
    mplus :: m a -> m a -> m a

リストはモノイドでもあったので、MonadPlus のインスタンスにできる。

instance MonadPlus [] where
    mzero = []
    mplus = (++)

リストに関して

  • mzero は、候補が1つもない、失敗した非決定性計算を表している。
  • mplus は2つの非決定的値を1つの値にくっつける。

guard 関数の定義

guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero
--- Maybe の場合
ghci> guard (5 > 2) :: Maybe ()
Just ()
ghci> guard (1 > 2) :: Maybe ()
Nothing
--- リストの場合
ghci> guard (5 > 2) :: [()]
[()]
ghci> guard (1 > 2) :: [()]
[]

リストモナドでは、 guard を使って解の候補をふるい落とすことができる!

ghci> [1..50] >>= (\x -> guard ('7' `elem` show x) >> return x)
[7,17,27,37,47]
  • マッチしてたら [()] >> return x = return x = [x]
  • マッチしてなかったら [] >> return x = []
  • concat したらマッチしたものだけのリストになる。

do 記法で書くと、

sevensOnly :: [Int]
sevensOnly = do
    x <- [1..50]
    guard ('7' `elem` show x)
    return x

騎士の旅

騎士 = Knight (桂馬)

XOXOX
OXXXO
XXNXX
OXXXO
XOXOX
--- 現在位置を表す型シノニム
type KnightPos = (Int, Int)

--- 移動 (非決定性計算)
moveKnight :: KnightPos -> [KnightPos]
moveKnight (c, r) = do
    (c', r') <- [(c+2,r-1), (c+2,r+1), (c-2,r-1), (c-2,r+1)
                ,(c+1,r-2), (c+1,r+2), (c-1,r-2), (c-1,r+2)]
    guard (c' `elem` [1..8] && r' `elem` [1..8])
    return (c', r')

リストモナドを使わずに書くこともできます。例えば filter を使うと

moveKnight (c, r) = filter onBoard
    [(c+2,r-1), (c+2,r+1), (c-2,r-1), (c-2,r+1)
    ,(c+1,r-2), (c+1,r+2), (c-1,r-2), (c-1,r+2)]
    where onBoard (c, r) = c `elem` [1..8] && r `elem` [1..8]

どっちの書き方をしてもいい。ともかく使ってみる。

ghci> moveKnight (6, 2)
[(8,1),(8,3),(4,1),(4,3),(7,4),(5,4)]
ghci> moveKnight (8, 1)
[(6,2),(7,3)]

次の位置は非決定的値で得られているので、 >>= を使えばまた moveKnight にぶちこめる!

--- 3手で行ける位置を返す関数
in3 :: KnightPos -> [KnightPos]
in3 start = do
   first <- moveKnight start
   second <- moveKnight first
   moveKnight second

do 記法を使わずに書くなら

in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight

1つ目の位置から2つ目の位置にちょうど3手で行けるか?

canReachIn3 :: KnightPos -> KnightPos -> Bool
canReachIn3 start end = end `elem` in3 start

試してみる。

ghci> (6, 2) `canReachIn3` (6, 1)
True
ghci> (6, 2) `canReachIn3` (7, 3)
False

13.7 モナド則

左恒等性

return x >>= f = f x

Maybeモナドの場合

ghci> return 3 >>= (\x -> Just (x+100000))
Just 100003
ghci> (\x -> Just (x+100000)) 3
Just 100003

リストモナドの場合

ghci> return "WoM" >>= (\x -> [x,x,x])
["WoM","WoM","WoM"]
ghci> (\x -> [x,x,x]) "WoM"
["WoM","WoM","WoM"]

右恒等性

m >>= return = m

Maybeモナドの場合

ghci> Just "move on up" >>= return
Just "move on up"
ghci> Nothing >>= return
Nothing

リストモナドの場合

ghci> [1,2,3,4] >>= return
[1,2,3,4]
ghci> [] >>= return
[]

結合法則

(m >>= f) >>= g = m >>= (\x -> f x >>= g)

Maybeモナドの場合

ghci> (Just 3 >>= (\x -> Just (x + 1))) >>= (\y -> Just (y * 2))
Just 8
ghci> Just 3 >>= (\x -> Just (x + 1) >>= (\y -> Just (y * 2)))
Just 8

リストモナドの場合

ghci> ([1,2,3] >>= (\x -> [x, x + 1])) >>= (\y -> [y, y * 2])
[1,2,2,4,2,4,3,6,3,6,4,8]
ghci> [1,2,3] >>= (\x -> [x, x + 1] >>= (\y -> [y, y * 2]))
[1,2,2,4,2,4,3,6,3,6,4,8]

モナド関数と関数合成

関数合成

(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = (\x -> f (g x))

モナド関数の合成

(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = (\x -> g x >>= f)

モナドの結合法則が成り立つならば

f <=< (g <=< h)
= f <=< (\x -> h x >>= g)
= \y -> (\x -> h x >>= g) y >>= f
= (\y -> h y >>= g) >>= f
= \y -> (h y >>= g) >>= f
= \y -> h y >>= (\x -> g x >>= f)
= (\x -> g x >>= f) <=< h
= (f <=< g) <=< h

モナドの左恒等性が成り立つならば

f <=< return
= \x -> return x >>= f
= \x -> f x
= f

モナドの右恒等性が成り立つならば

return <=< f
= \x -> f x >>= return
= \x -> f x
= f

モナド則が成り立つ ⇒ モナド関数と <=< はモノイドを成す

通常の関数の以下と類似

f . (g . h) = (f . g) . h
f . id = f
id . f = f

13章のまとめ

  • Monad は Applicative Functor の強化版
  • 文脈を保って関数を何回も適用できる
  • do 記法は Monad 演算の構文糖衣
  • リスト内包表記も Monad 演算の構文糖衣。フィルタリングは guard 関数と同じ。

演習問題

横9マス、縦9マスからなる将棋盤に順に歩を打っていくゲームを考える。 それぞれの手を (row, col) のペアで表現し、 二歩と行き所のない駒は禁じ手なのでそれを打った時点で敗北とする。

  • (return [] >>= putFu (3, 4) >>= putFu (7,4)) == Nothing
  • (return [] >>= putFu (9, 2)) == Nothing

このゲームを実現するための関数 putFu を作れ。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.