Implementando o Haskell-MaybeMonad em F # - como podemos ficar tão preguiçosos?

estamos tentando construir a amostra Haskell-MaybeMonad dehttp://www.haskell.org/all_about_monads/html/maybemonad.html em F #.

A idéia é procurar um endereço de email em dois dicionários. Se uma das duas pesquisas retornar um resultado, verificamos a terceira.

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", "[email protected]")    
     ("Bill Clinton", "[email protected]")
     ("Michael Jackson", "[email protected]")
     ("No Pref Guy", "[email protected]")]
       |> Map.ofList

let nickNamesDb =
    [("billy", "[email protected]")
     ("slick willy", "[email protected]")
     ("jacko", "[email protected]") ]
        |> Map.ofList

let prefsDb =
    [("[email protected]", "HTML")
     ("[email protected]", "Plain")
     ("[email protected]", "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

O problema é que realizamos a segunda pesquisa, mesmo que a primeira retorne um resultado. O bom de Haskell está aqui, que avalia preguiçoso. Agora estamos procurando algo semelhante em F #. Tentamos o seguinte, mas parece feio e parece quebrar a ideia de encapsular a lógica talvez no construtor:

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
}

Existe uma solução melhor?

Atenciosamente, forki

questionAnswers(2)

yourAnswerToTheQuestion