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:バインドと呼ぶ