2011-04-06 48 views
1

我需要一個不同的行爲來做!然後讓!在我自定義的計算表達式中有沒有辦法做不同的實現!然後讓!在計算表達式中?

我嘗試通過以下方式來實現這一目標:

type FooBuilder() = class 
    member b.Bind<'T, 'U>(x:'T, f:unit->'U):'U = failwith "not implemented" //do! implementation 
    member b.Bind<'T, 'U>(x:'T, f:'T->'U):'U = failwith "not implemented" //let! implementation 
    member b.Return<'T>(x:'T):'T = failwith "not implemented" //return implementation 
end 

let foo = FooBuilder() 
let x = foo { 
    do!() 
    return 2 
} 

但是編譯器給我一個錯誤:

A unique overload for method 'Bind' could not be determined based on type information prior to this program point. The available overloads are shown below (or in the Error List window). A type annotation may be needed.

有沒有辦法有不同的實現做的!然後讓!?

回答

2

如果您想保留let!通用中的Bind操作,那麼在翻譯do!(重載必須重疊)時,無法說F#應該使用不同的實現。

一般來說,如果你想獲得let!do!的不同行爲,那麼它表明你的計算表​​達式可能被錯誤地定義了。這個概念非常靈活,它可以用來做更多的事情,而不僅僅是宣佈monad,但是你可能會把它拉得太遠。如果你可以寫更多關於你想達到的信息,那將是有用的。無論如何,這裏有一些可能的解決方法...

你可以添加一些額外的包裝和寫一些像do! wrap <| expr

type Wrapped<'T> = W of 'T 
type WrappedDo<'T> = WD of 'T 

type FooBuilder() = 
    member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = failwith "let!" 
    member b.Bind<'T, 'U>(x:WrappedDo<unit>, f:unit->'U):'U = failwith "do!" 
    member b.Return<'T>(x:'T):Wrapped<'T> = failwith "return" 

let wrap (W a) = WD a 
let bar arg = W arg 

let foo = FooBuilder() 

// Thanks to the added `wrap` call, this will use the second overload 
foo { do! wrap <| bar() 
     return 1 } 

// But if you forget to add `wrap` then you still get the usual `let!` implementation 
foo { do! wrap <| bar() 
     return 1 } 

另一種方法是使用動態類型測試。這是有點低效率(以及一些不雅的),但它可能做的伎倆,根據您的情況:

member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = 
    if typeof<'T> = typeof<unit> then 
    failwith "do!" 
    else 
    failwith "let!" 

然而,這仍然使用do!過載,當你寫let!() = bar

+0

非常感謝你對此詳細解釋 – 2011-04-06 14:54:00

1

你可以嘗試別的東西,有點難看,但應該工作:

let bindU (x, f) = f x // you must use x, or it'll make the Bind method less generic. 
let bindG (x, f) = f x 
member b.Bind(x : 'a, f : 'a -> 'b) = 
    match box x with 
    | :? unit -> bindU (x, f) 
    | _ -> bindG (x, f) 

這盒一個(將其轉換爲obj)並檢查它是否是unit類型,然後重定向到正確的過載。

+0

你可能還想看看http://ramon.org.il/wp/2011/04/taking-computation-expressions-one-step-further/。它爲綁定關鍵字提供了更多的自由。 – 2011-04-06 14:46:39

+0

我想感謝您解決此問題。 – 2011-04-06 14:56:00

相關問題