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 } }