在F#中实现Haskell-MaybeMonad - 我们如何才能获得这种懒惰? [英] Implementing the Haskell-MaybeMonad in F# - how can we get this lazy?
问题描述
我们正在尝试从 http://www.haskell构建Haskell-MaybeMonad示例.org / all_about_monads / html / maybemonad.html 。
这个想法是在两个词典中搜索一个mailaddress。如果其中一个查找返回结果,我们将查看第三个。
The idea is to search for a mailaddress in two dictionaries. If one of the both lookups returns a result we look into the third one.
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
问题是我们执行第二次查找,即使第一次查找返回结果。 Haskell的好处在于它评估懒惰。现在我们在F#中寻找类似的东西。我们尝试了以下内容,但看起来很丑,似乎打破了在构建器中封装可能逻辑的想法:
The problem is that we perform the second lookup even if the first one returns a result. The nice thing about Haskell is here, that it evaluates lazy. Now we looking for something similar in F#. We tried the following but it looks ugly and seems to break the idea of encapsulating the maybe logic in the builder:
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
}
是否有更好的解决方案?
Is there a better solution?
问候,
forki
Regards, forki
推荐答案
您可以实施其他方法Run / Combine in MaybeBuilder的结果如下:
You can implement additional methods Run/Combine in MaybeBuilder so result shall be the following:
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
member this.Combine(a, b) = if Option.isSome a then a else b()
member this.Run(f) = f()
let maybe = MaybeBuilder()
//Sample dictionaries (the same with original sample)
let fullNamesDb = ...
let nickNamesDb = ...
let prefsDb = ....
let lookUp name =
let findName m = maybe {
let! v = Map.tryFind name m
return! prefsDb.TryFind v
}
maybe {
return! findName fullNamesDb
return! findName nickNamesDb
}
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
这篇关于在F#中实现Haskell-MaybeMonad - 我们如何才能获得这种懒惰?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!