F#的类型推断规则让我有些困扰。我正在编写一个简单的计算生成器,但是无法正确设置通用类型变量的约束。
以下是我想要的代码在C#中的样子:
class FinallyBuilder<TZ>
{
readonly Action<TZ> finallyAction;
public FinallyBuilder(Action<TZ> finallyAction)
{
this.finallyAction = finallyAction;
}
public TB Bind<TA, TB>(TA x, Func<TA, TB> cont) where TA : TZ
{ // ^^^^^^^^^^^^^
try // this is what gives me a headache
{ // in the F# version
return cont(x);
}
finally
{
finallyAction(x);
}
}
}
到目前为止,我想出来的针对F#版本的最佳(但无法编译的)代码如下:
type FinallyBuilder<′z> (finallyAction : ′z -> unit) =
member this.Bind (x : ′a) (cont : ′a -> ′b) =
try cont x
finally finallyAction (x :> ′z) // cast illegal due to missing constraint
// Note: ' changed to ′ to avoid bad syntax highlighting here on SO.
很不幸,我不知道如何翻译
Bind
方法上的 where TA : TZ
类型约束。我认为应该是类似于 ′a when ′a :> ′z
的内容,但是 F# 编译器并不喜欢这种方式,最终我总是会得到一些将泛型类型变量限制为另一个的结果。请问有人能够展示正确的 F# 代码吗?
背景: 我的目标是编写一个像这样的 F# 自定义工作流:
let cleanup = new FinallyBuilder (fun x -> ...)
cleanup {
let! x = ... // x and y will be passed to the above lambda function at
let! y = ... // the end of this block; x and y can have different types!
}
finallyAction
指定obj
会带来一个令人讨厌的副作用,即将自定义绑定(let!
)到类型为obj
,这意味着我不能再对它们进行有意义的处理了。需要进一步思考如何以不同的方式实现该构建器。但我希望他们能在未来版本的 F# 语言中修复这个问题... - stakx - no longer contributingobj
,使用let!
绑定的值的类型也应该是实际(更具体)的类型。我编辑了答案,包括演示这一点的示例(我最终也测试了它:-))。 - Tomas Petricek