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# version 提出的最佳(但非编译代码)是:
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#代码吗?
Background: 我的目标是能够像这样写一个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!
}
2 回答
我不认为在F#中写这样的约束是可能的(尽管我不确定为什么) . 无论如何,在语法上,你想要写这样的东西(正如Brian建议的那样):
不幸的是,这会产生以下错误:
这似乎与this mailing list中讨论的情况相同 . Don Syme说以下内容:
您始终可以通过在传递给构建器的函数中使用
obj
来解决此问题 .EDIT :即使使用
obj
,使用let!
绑定的值也会有更多特定类型(调用finallyAction
时,F#会自动将某些类型参数的值转换为obj
):它会是这样的
但是让我编写代码以确保完全正确......
啊,看起来会是这样的:
除了那个
http://cs.hubfs.net/forums/thread/10527.aspx
指出F#不会形成"T1 :> T2"形式的约束,其中两者都是类型变量(它假设T1 = T2) . 但是这对你的情况可能没问题,你打算用什么作为
Z
的具体实例?可能有一个简单的解决方法或一些不太通用的代码来满足这个场景 . 例如,我想知道这是否有效:它似乎:
哦,我明白了,但
d
和c
现在的类型为Animal
. 嗯,让我看看我是否还有任何聪明才智......好吧,显然你可以做到
抛出类型安全性(如果事情不是finallyActionable,将在运行时抛出强制转换异常) .
或者您可以创建特定于类型的构建器:
但我认为我没有其他聪明的想法 .