我需要一个不同的行为!然后让!在我的自定义计算表达式中。
我尝试通过以下方式实现这一目标:
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
}
但是编译器给了我一个错误:
无法根据此程序点之前的类型信息确定方法“绑定”的唯一重载。可用的重载如下所示(或在错误列表窗口中)。可能需要类型注释。
有没有办法实现不同的do!并让!?
答案 0 :(得分:2)
如果你想保持Bind
泛型中使用的let!
操作,那么在翻译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
重载。
答案 1 :(得分: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)
它将a(将其转换为obj
)并检查它是否为unit
类型,然后重定向到正确的重载。