fugafuga.write

日々のログ

すごいH本 part83

ピエールの人生とは

せっかくの休暇にバランス棒やってんのに鳥に邪魔されるピエール

まず、鳥とポールを定義

その後に、ポールの右と左に鳥が止まる関数を定義

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)

実行

*Main> landLeft 2 (0, 0)
(2,0)
*Main> landRight 5 (1, 2)
(1,7)
*Main> landRight (-1) (1, 2)
(1,1)

鳥がいなくなる場合、負の数を指定することで実現する。

landLeft と landRight は Pole を返すので、関数合成ができる。

*Main> landLeft 2 . landRight 5 . landLeft 1 $ (0, 0)
(3,5)

ポールを先に書いて鳥を止まらせる関数を後ろに書くほうが読みやすそう

よって、以下のような関数を定義する

x -: f = f x

これで関数を適用するのに引数 -> 関数の順番で書けるようになる。

*Main> 100 -: (*3)
300
*Main> True -: not
False
*Main> (0, 0) -: landLeft 2
(2,0)
*Main> (0, 0) -: landLeft 2 -: landRight 1 -: landLeft 2
(4,1)

ピエールは落ちる

ポールの片方に一気に10羽きた場合、

*Main> landLeft 10 (0, 3)
(10,3)

ピエールは落ちる。

この場合はどうか、

*Main> (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
(0,2)

最終的には問題ないが、途中経過で(0, 4)の状態になる瞬間があるのでピエールは落ちる。

landLeft と landRight は失敗を表現できる必要がある。 鳥の止まり方が偏りすぎた場合は、失敗を返すようにしたい。

成功か失敗か、ということは 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

実行

*Main> landLeft 2 (0, 0)
Just (2,0)
*Main> landLeft 10 (0, 3)
Nothing

ピエールが落ちるかどうかを検知できるようになった。

しかし、この変更により landLeft, landRight を関数合成できなくなってしまった。 関数は Maybe Pole を返すが、引数として要求しているのは、Pole だからだ。 なんとかして Pole を取る関数に Maybe Pole を渡したい。

こんなこともあろうかと言う声が聞こえる。 満を持して>>=の登場である。

*Main> landRight 1 (0, 0) >>= landLeft 2
Just (2,1)

Maybe Pole 型の値を Pole 型を要求する関数の引数として渡せている。

Nothing の場合

*Main> Nothing >>= landLeft 2
Nothing

ちゃんとピエールが落ちている。

鳥が止まる一連のイベントは以下のようになる

*Main> return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2
Just (2,4)

return でポールの状態を Just で包んでいる。

以前出てきたこのパターンをモナド適用を使って書き換える。

*Main> (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
(0,2)

*Main> return (0, 0) >>= landLeft 1 >>= landRight 4 >>= landLeft (-1) >>= landRight (-2)
Nothing

ピエールはしっかり落ちている。

まとめ

Maybe をアプリカティブ値として扱うだけでは、ここまでのことはできない。 アプリカティブファンクターの機能ではアプリカティブ値同士を深く相互作用させることができない。 アプリカティブにできることは、通常の関数にアプリカティブスタイルで引数を渡すことぐらい。

アプリカティブ値同士を扱うためにモナド適用が使える。

所感

今のところ、モナドは一連の処理の中で文脈を保持してくれて関数合成に使える便利なものというイメージ。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る