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
}

但是编译器给了我一个错误:

无法根据此程序点之前的类型信息确定方法“绑定”的唯一重载。可用的重载如下所示(或在错误列表窗口中)。可能需要类型注释。

有没有办法有不同的实现 do! 然后让!?

4

2 回答 2

2

如果您想保持通用Bind使用的操作let!,那么没有办法说 F# 在翻译时应该使用不同do!的实现(重载必然必须重叠)。

一般来说,如果你想获得不同的行为,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.

于 2011-04-06T14:38:22.607 回答
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)并检查它是否是 type unit,然后重定向到正确的重载。

于 2011-04-06T14:44:54.557 回答