如何将 `where T : U` 泛型类型参数约束从 C# 转换为 F#?
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!
}
F# is giving me some trouble with its type inference rules. I'm writing a simple computation builder but can't get my generic type variable constraints right.
The code that I would want looks as follows in 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);
}
}
}
The best (but non-compiling code) I've come up with for the F# version so far is:
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.
Unfortunately, I have no clue how I would translate the where TA : TZ
type constraint on the Bind
method. I thought it should be something like ′a when ′a :> ′z
, but the F# compiler doesn't like this anywhere and I always end up with some generic type variable constrained to another.
Could someone please show me the correct F# code?
Background: My goal is to be able to write an F# custom workflow like this:
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!
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为不可能在 F# 中编写这样的约束(尽管我不太清楚为什么)。无论如何,从语法上来说,你想写这样的东西(正如布莱恩建议的那样):
不幸的是,这会产生以下错误:
这似乎与 此邮件列表。唐·赛姆 (Don Syme) 说道:
您始终可以通过在传递给构建器的函数中使用 obj 来解决此问题。
编辑:即使您使用
obj
,使用let!
绑定的值也将具有更具体的类型(当调用finallyAction
>,F# 会自动将某些类型参数的值转换为obj
):I don't think it is possible to write constraint like this in F# (although I'm not exactly sure why). Anyway, syntactically, you'd want to write something like this (as Brian suggests):
Unfortunately, this gives the following error:
This seems to be the same case as the one discussed in this mailing list. Where Don Syme says the following:
You can always solve this by using
obj
in the function passed to your builder.EDIT: Even when you use
obj
, the values bound usinglet!
will have more specific types (when callingfinallyAction
, F# will automatically cast the value of some type parameter toobj
):它会是这样的
,但让我对其进行编码以确保完全正确...
啊,看起来会是这样的:
除了
http://cs.hubfs.net/forums/thread/10527.aspx
指出 F# 不执行“T1 :> T2”形式的约束,其中两者都是类型变量(假设 T1 = T2)。不过,这可能适合您的情况,您到底打算使用什么作为
Z
的具体实例?可能有一个简单的解决方法或一些不太通用的代码可以满足该场景。例如,我想知道这是否有效:似乎:
哦,我明白了,但是
d
和c
现在具有Animal
类型。嗯,让我看看我是否还有剩余的聪明才智……好吧,显然你可以这样做
,这会抛弃类型安全(如果事情不是finallyActionable,则会在运行时抛出强制转换异常)。
或者你可以制作特定于类型的构建器:
但我认为我没有其他聪明的想法。
It will be something like
but let me code it up to ensure that's exactly right...
Ah, it looks like it would be this:
except that
http://cs.hubfs.net/forums/thread/10527.aspx
points out that F# does not do contraints of the form "T1 :> T2" where both are type variables (it assumes T1 = T2). However this might be ok for your case, what exactly did you plan to use as concrete instantiations of
Z
? There is probably a simple workaround or some less-generic code that will meet the scenario. For example, I wonder if this works:It seems to:
Oh, I see, but
d
andc
now have typeAnimal
. Hm, let me see if there is any remaining cleverness in me...Well, obviously you can do
which throws away type safety (will throw a cast exception at runtime if the thing is not finallyActionable).
Or you can make type-specific builders:
But I think I am out of other clever ideas.