Implementierung der Haskell-MaybeMonad in F # - wie können wir das faul machen?

Wir versuchen, das Haskell-MaybeMonad-Beispiel aus @ zu erstellehttp: //www.haskell.org/all_about_monads/html/maybemonad.htm in F #.

Die Idee ist, in zwei Wörterbüchern nach einer E-Mail-Adresse zu suchen. Wenn einer der beiden Suchvorgänge ein Ergebnis zurückgibt, untersuchen wir den dritten.

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

Das Problem ist, dass wir die zweite Suche durchführen, auch wenn die erste ein Ergebnis liefert. Das Schöne an Haskell ist, dass es faul bewertet. Jetzt suchen wir nach etwas ähnlichem in F #. Wir haben Folgendes versucht, aber es sieht hässlich aus und scheint die Idee zu brechen, die Vielleicht-Logik im Builder zu verkapseln:

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
}

Gibt es eine bessere Lösung?

Regards, forki

Antworten auf die Frage(4)

Ihre Antwort auf die Frage