c# - 如何将 `where T : U` 泛型类型参数约束从 C# 转换为 F#?

标签 c# generics f# type-constraints computation-expression

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.

不幸的是,我不知道如何翻译 where TA : TZ Bind 上的类型约束方法。我认为它应该类似于 ′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# 中编写这样的约束(尽管我不确定为什么)。不管怎样,从句法上讲,你会想写这样的东西(正如 Brian 建议的那样):

type FinallyBuilder<'T> (finallyAction : 'T -> unit) = 
  member this.Bind<'A, 'B when 'A :> 'T>(x : 'A) (cont : 'A -> 'B) =  //' 
    try cont x 
    finally finallyAction (x :> 'T) 

不幸的是,这会产生以下错误:

error FS0698: Invalid constraint: the type used for the constraint is sealed, which means the constraint could only be satisfied by at most one solution

这似乎与 this mailing list 中讨论的情况相同. Don Syme 说了以下内容:

This is a restriction imposed to make F# type inference tractable. In particular, the type on the right of a subtype constraint must be nominal. Note constraints of the form 'A :> 'B are always eagerly solved to 'A = 'B, as specified in section 14.5.2 (Solving Subtype Constraints) of the F# specification.

您始终可以通过在传递给构建器的函数中使用 obj 来解决此问题。
编辑:即使您使用obj,使用let! 绑定(bind)的值也会有更具体的类型(当调用finallyAction, F# 会自动将某些类型参数的值转换为 obj):

type FinallyBuilder(finallyAction : obj -> unit) =  
  member x.Bind(v, f) =  
    try f v 
    finally finallyAction v 
  member x.Return(v) = v

let cleanup = FinallyBuilder(printfn "%A")

let res = 
  cleanup { let! a = new System.Random()
            let! b = "hello"
            return 3 }

关于c# - 如何将 `where T : U` 泛型类型参数约束从 C# 转换为 F#?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4173283/

相关文章:

c# - 将 XML 转换为 PDF 时 itextsharp 5.4 出现问题

java - 泛型返回类型 E

generics - 如何在 Vec 中存储具有不同参数的类型?

exception - F# - 为什么 Seq.map 不传播异常?

f# - 功能增量

c# - MVC 3 复选框关闭不使用RequiredAttribute 的验证

c# - 通用单例

c# - 如何使用 asp.net mvc webapi 发送电子邮件附件

java - 获取通用类型的通用类型

c# - 我如何在 C# 中获得特定运算符的函数?