2010-07-01 33 views
5

chúng tôi đang cố gắng tạo mẫu Haskell-MaybeMonad từ http://www.haskell.org/all_about_monads/html/maybemonad.html trong F #.Thực hiện Haskell-MaybeMonad trong F # - làm thế nào chúng ta có thể có được điều này lười biếng?

Ý tưởng là tìm kiếm địa chỉ thư trong hai từ điển. Nếu một trong hai tra cứu trả về kết quả, chúng tôi sẽ xem xét kết quả thứ ba.

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 

Vấn đề là chúng tôi thực hiện lần tra cứu thứ hai ngay cả khi lần đầu tiên trả về kết quả. Điều tốt đẹp về Haskell là ở đây, rằng nó đánh giá lười biếng. Bây giờ chúng tôi tìm kiếm một cái gì đó tương tự trong F #. Chúng tôi đã thử những điều sau nhưng có vẻ xấu xí và dường như phá vỡ ý tưởng đóng gói logic có thể trong trình tạo:

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 
} 

Có giải pháp nào tốt hơn không?

Kính trọng, forki

Trả lời

8

Bạn có thể thực hiện phương pháp bổ sung Chạy/Kết hợp trong MaybeBuilder nên kết quả sẽ là như sau:

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 
+0

Ý tưởng tuyệt vời. Điều đó thật tuyệt vời. – forki23

1

Luôn luôn Lazy, đó là một cách hiệu quả những gì bạn có ở đây nhưng với cú pháp khác nhau:

let mplus m1 (m2 : Lazy<'a option>) = 
    match m1 with 
    | Some _ as m -> m 
    | None -> m2.Force() 

let (+) = mplus 

let lookUp name = maybe { 
    let! combined = fullNamesDb.TryFind name + lazy (nickNamesDb.TryFind name) 
    return! prefsDb.TryFind combined 
} 
+0

Cảm ơn gợi ý. Nhưng có vẻ như sự lười biếng luôn được phản ánh trong loại ngay cả bên trong biểu thức có thể {}. – forki23

+0

Tôi không thể nghĩ ra một cách đẹp hơn. Những gì bạn đang làm giống như C# 's '??' coalescing nhà điều hành trong C#, và tôi không nghĩ rằng có một F # tương đương. –

+0

'seq {yield fullNamesDb.TryFind name; năng suất nickNamesDb.TryFind name} |> Seq.collect Option.toList |> Seq.head ;; ' –

Các vấn đề liên quan