如何将 `where T : U` 泛型类型参数约束从 C# 转换为 F#? [英] How do I translate a `where T : U` generic type parameter constraint from C# to F#?

查看:11
本文介绍了如何将 `where T : U` 泛型类型参数约束从 C# 转换为 F#?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

F# 的类型推断规则给我带来了一些麻烦.我正在编写一个简单的计算构建器,但无法正确设置通用类型变量约束.

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.

我想要的代码在 C# 中如下所示:

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);
        }
    }
}

<小时>

到目前为止,我为 F# 版本 想到的最好的(但非编译代码)是:


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.

不幸的是,我不知道如何翻译 where TA : TZBind 方法的类型约束.我认为它应该类似于 'a when 'a :>'z,但 F# 编译器在任何地方都不喜欢这样,我总是最终得到一些约束到另一个的泛型类型变量.

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.

谁能告诉我正确的 F# 代码?

Could someone please show me the correct F# code?

背景:我的目标是能够编写这样的 F# 自定义工作流:

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! 
}

推荐答案

我认为在 F# 中不可能写出这样的约束(虽然我不确定为什么).不管怎样,从句法上来说,你会想写这样的东西(正如布赖恩建议的那样):

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):

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) 

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

Unfortunately, this gives the following error:

错误 FS0698:无效的约束:用于约束的类型是密封的,这意味着最多只能通过一种解决方案来满足约束

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

这似乎与 这个邮件列表.唐·赛姆 (Don Syme) 说的内容如下:

This seems to be the same case as the one discussed in this mailing list. Where Don Syme says the following:

这是为了使 F# 类型推断易于处理而施加的限制.特别是,子类型约束右侧的类型必须是名义类型.注意'A :> 形式的约束.'B 总是急切地求解为 'A = 'B,如 F# 规范的第 14.5.2 节(解决子类型约束)中所述.

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 来解决此问题.
EDIT:即使您使用 obj,使用 let! 绑定的值也会有更具体的类型(当调用 finallyAction>, F# 会自动将某个类型参数的值强制转换为 obj):

You can always solve this by using obj in the function passed to your builder.
EDIT: Even when you use obj, the values bound using let! will have more specific types (when calling finallyAction, F# will automatically cast the value of some type parameter to 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 }

这篇关于如何将 `where T : U` 泛型类型参数约束从 C# 转换为 F#?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆