かとじゅんの技術日誌

技術の話をするところ

Maybeを自作してみる(Monad編 その1)

Monadを調べていると、モナモナ言いたくなりますね!

さて、OptionをMonad対応する例を書いてみます。

Monad型クラスは次のような定義になっています。returnと>>=を実装せいということらしい。

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

return関数はApplicative Functorのpureと同等の処理でよいらしいので、値コンストラクタを指定します。 >>=*1はOptionと、Someの値を引数にとりOptionを返す関数を受け取り、Optionを返します。 >>は問答無用で>>の右側に指定された値を返す関数を適用します。 コードは次のとおり。

import Control.Monad

data Option a = None | Some a deriving (Show)

instance Functor Option where
  fmap f (Some x) = Some (f x)
  fmap f None = None

instance Monad Option where
  return = Some
  None >>= f = None
  Some x >>= f = f x
  fail _ = None

apply = let
            m1 = None >>= \x -> Some(x+1)
            m2 = Some 1 >>= \x -> Some(x+2)
            m3 = Some 1 >> None
            m4 = Some 1 >> Some(2)
            m5 = Some 1 >>= \x -> Some(x+2) >>= \y -> Some(y+3)
            m6 = Some 1 >> None >>= \y -> Some(y+1)
            m7 = (Some (Some 1)) >>= fmap (+2)
         in
            [m1, m2, m3, m4, m5, m6, m7]

main = mapM print apply

結果はこれ

None
Some 3
None
Some 2
Some 6
None
Some 3

この>>=関数は、Noneの時はNoneを返し、Someの時はf xを返します。

apply関数内のm1はNoneなので関数は適用されずにNone。m2は別の値を持つSomeに変換される。m3は問答無用でNoneに変換される。m4も問答無用で Some(2)に変換される。m6は関数呼び出しが連なっています。m7は途中でNoneに変換されるので2つ目の関数は適用されずにNoneに変換される。

Some 1 >>= \x -> Some(x+2) >>= \y -> Some(y+3)

このように>>=をつなげて使って、Someの場合だけのコードを記述できるというのがシンプルになっていいですね。これをcase ofで記述するNoneの場合のコードも記述しないといけないわけですから。

*1:バインドと呼ぶ