Maybeモナド
コンピュテーション式を用いると様々なモナドが定義できます。
コンピュテーション式を使っていたり、RetrunとBindを実装しているからモナドになるわけではなく
モナド則というものを満たさなければモナドにはなりませんが、既にモナドと分かっているものがたくさんありますので
それを学んでいきましょう。
モナドとは、圏論という数学の理論に出てくる用語で、とても抽象化された概念ではっきり言って初心者には理解が困難です。
ただ、圏論のモナドは理解できなくとも、プログラミングで触れるモナドの使い方を理解するのは可能な気がします。
中には難しいモナドもありますが、まずはとっつきやすいMaybeモナドを紹介することにしました。
Maybe型はF#やOCaml,Scala等ではOption型と呼ばれています。
それにならうとF#ではOptionモナドと呼ぶべきなのかもしれませんが、
参考にしたHaskellにならってMaybeモナドと呼ぶことにします。
option型自体は
option型のページで解説しましたのでそちらも参考にしてください。
リストにある値があるかをoption型で包んで教えてくれるList.tryFindも解説しています。
まずは、maybeモナドのコードを先に出します。
Maybeモナド
let (>>=) x f =
match x with
| Some y -> f y
| None -> None
type MaybeBuilder() =
member this.Bind(x, f) = x >>= f
member this.Return(x) = Some x
let maybe = new MaybeBuilder()
let db = Map.ofList [("x", 1); ("y", 2);]
let try1 = maybe{
let! a = Map.tryFind "x" db
let! b = Map.tryFind "y" db
return (a+b)
}
let try2 = maybe{
let! a = Map.tryFind "x" db
let! c = Map.tryFind "a" db //dbにない
let! b = Map.tryFind "y" db
return (a+b+c)
}
let try3 = maybe{
let! a = Map.tryFind "x" db
let! b = Map.tryFind "y" db
let! c = Map.tryFind "a" db //dbにない
return (a+b)
}
let try4 = maybe{
let! a = Map.tryFind "x" db
let! b = Map.tryFind "y" db
let! c = Some 5
return (a+b+c)
}
printfn "%A" try1
printfn "%A" try2
printfn "%A" try3
printfn "%A" try4
BindはHaskellにならって>>=という二項演算子にて定義しています。
F#入門のモナドはすべてこの形式で定義しようと思っていますので
各々見比べてみてください。
Bindの一般的な型はM<'T> * ('T -> M<'U>) -> M<'U>なのでした。
参考
Mはoption型なので'T option -> ('T -> 'U option) -> 'U optionがBindの型になります。
参考サイトではM<'T> -> ('T -> M<'U>) -> M<'U>となっていませんが、
*となっているので最初の2つの項はどちらが先でもよいということになります。
Bindの実装をみてみると、Someの時だけfを適用してNoneの時はfは使わないという風になっています。
let!でつなげると、成功した場合だけ値が帰ってくることになります。
もし計算が失敗したら、その時点で結果はNoneになります。
try1では両方の値がdbにあるので、Some 1,Some 2の和をとってSome 3が帰ってきます。
try2では計算の途中で失敗する計算が含まれているので結果はNoneになります。これは想定された動作です。
try3ではaとbの計算は成功していて、returnでもaとbしかつかっていないにもかかわらず結果はNoneになります。cの計算が失敗しているからです。
これは一見副作用があるようにもみえ、注意が必要なケースです。
try4では、直接Some 5をBindして和を求めています。
本記事の作成には
F#プログラマのためのMaybeモナド入門を参考にさせていただきました。