かとじゅんの技術日誌

技術の話をするところ

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

do式でのモナドの記述方法について簡単にまとめる。

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

をdo式に書き直すと

apply = do 
    a <- Some 1
    Some $ a + 2

Monadにはreturnがあるのでそれに書き換える。

apply = do 
    a <- return 1 :: Option Int
    return $ a + 2

関数の引数にOptionを受け取る場合はこんな感じ。

apply :: Option Int -> Option Int
apply op = do
    a <- op
    return $ a + 1

まぁ、これだけの説明だと書き方が違うだけ何がいいかよくわからないので、引数が入れ子のOption型でその内部の値を取り出し二乗しOption型で返す場合を考えてみる。

case ofでの実装は次のとおり。>>=の時に説明したようにNoneケースを記述するのがだるい。

square :: (Option (Option Int)) -> Option Int
square x = case x of
    None -> None
    Some n -> case n of
        None -> None
        Some m -> Some $ m * m

次は>>=を使って書いてみる。Noneのケースは記述しなくてよくなったが、入れ子が見通しを悪くしている。

square :: (Option (Option Int)) -> Option Int
square x = x >>= (\a -> a >>= (\b -> Some $ b * b))

ちなみに括弧なしでも記述できます。

square :: (Option (Option Int)) -> Option Int
square x = x >>= \a -> a >>= \b -> Some $ b * b

読みづらいw

square x = x >>= (\a ->
           a >>= (\b ->
           Some $ b * b))

すごいH本にも書いある例のように改行してみたけど、やっぱり見づらい。

さて、do式で書いてみる。おお、これはシンプルになった。というかラムダ式が消えた。

square :: (Option (Option Int)) -> Option Int
square x = do
           a <- x
           b <- a
           return $ b * b

という訳で、Monadに対応したデータ型ならdo式が使えるみたい。これは便利。

ここから蛇足ですが、Scalaでも>>=相当のメソッドがあります。flatMapです。

def square(op : Option[Option[Int]]) : Option[Int] = {
  op.flatMap{ a =>
    a.flatMap{ b =>
      Some(b * b)
    }
  }
}

そして、do式相当に使えるのがfor式です。

def square(op : Option[Option[Int]]) : Option[Int] = {
  for {
    a <- op
    b <- a
  } yield { b * b }
}