F#でhttp://www.haskell.org/all_about_monads/html/maybemonad.htmlから Haskell-MaybeMonad サンプルをビルドしようとしています。
アイデアは、2 つの辞書でメールアドレスを検索することです。両方のルックアップのいずれかが結果を返す場合、3 番目のルックアップを調べます。
let bindM x k =
match x with
| Some value -> k value
| None -> None
let returnM x = Some x
type MaybeBuilder() =
member this.Bind(x, k) = bindM x k
member this.Return(x) = returnM x
member this.ReturnFrom(x) = x
member this.Delay(f) = f()
let maybe = MaybeBuilder()
//Sample dictionaries
let fullNamesDb =
[("Bill Gates", "billg@microsoft.com")
("Bill Clinton", "bill@hope.ar.us")
("Michael Jackson", "mj@wonderland.org")
("No Pref Guy", "guy@nopref.org")]
|> Map.ofList
let nickNamesDb =
[("billy", "billg@microsoft.com")
("slick willy", "bill@hope.ar.us")
("jacko", "mj@wonderland.org") ]
|> Map.ofList
let prefsDb =
[("billg@microsoft.com", "HTML")
("bill@hope.ar.us", "Plain")
("mj@wonderland.org", "HTML")]
|> Map.ofList
let mplus m1 m2 = if m1 <> None then m1 else m2
let (+) = mplus
let lookUp name = maybe {
let! combined = fullNamesDb.TryFind name + nickNamesDb.TryFind name
return! prefsDb.TryFind combined
}
let billGatesPref = lookUp "Bill Gates" |> printfn "%A" // Some "HTML"
let billyPref = lookUp "billy" |> printfn "%A" // Some "HTML"
let billClintonPref = lookUp "Bill Clinton" |> printfn "%A" // Some "Plain"
let steffenPref = lookUp "Steffen" |> printfn "%A" // None
let noPref = lookUp "No Pref Guy" |> printfn "%A" // None
System.Console.ReadKey() |> ignore
問題は、最初のルックアップが結果を返したとしても、2 番目のルックアップを実行することです。Haskell の良いところは、lazy と評価されることです。次に、F# で同様のものを探します。次のことを試しましたが、見栄えが悪く、ビルダーで多分ロジックをカプセル化するという考えが壊れているようです。
let mplus m1 m2 = if m1 <> None then m1 else m2()
let (+) = mplus
let lookUp name = maybe {
let! combined = fullNamesDb.TryFind name + fun _ -> nickNamesDb.TryFind name
return! prefsDb.TryFind combined
}
より良い解決策はありますか?
よろしく、フォーキ