2011-04-06 45 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

相关问题