如何翻译一个`其中T:U`泛型类型参数从C#到F#的约束? [英] How do I translate a `where T : U` generic type parameter constraint from C# to F#?

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

问题描述

F#是给我一些麻烦的类型推理规则。我正在写一个简单的计算,但建设者不能让我的泛型类型变量的约束权。






中的代码,我希望看起来在如下的 C#

 类FinallyBuilder< TZ> 
{
只读动作< TZ> finallyAction;

公共FinallyBuilder(动作< TZ> finallyAction)
{
this.finallyAction = finallyAction;
}

市民对结核病绑定< TA,TB>(TA X,Func键< TA,TB>续)其中TA:TZ
{// ^^^^^^ ^^^^^^^
试//这是什么让我头疼
{//在F#版本
返回续(X);
}
终于
{
finallyAction(X);
}
}
}






最好的(但非编译代码),我拿出在 F#版本至今:

 键入FinallyBuilder<'Z> (finallyAction:'Z  - >单位)= 

会员this.Bind(X:'一)(续:'A - >'B)=
试续点¯x
终于finallyAction(X:>'Z)//非法投由于缺少约束

//注:更改为避免不良语法高亮这里SO。



不幸的是,我不知道我会怎么翻译其中TA:TZ 键入的绑定法的约束。我想到了一个'当'它应该是类似:> Z ,但F#编译器不随地喜欢这一点,我总是受限于另一些通用型的变量结束。



莫非?有人请告诉我正确的F#代码






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

 让清理=新FinallyBuilder(好玩点¯x - > ...)

清理{
让! X = ... // x和y将在
让被传递到上面的lambda函数! Y = ... //这个块的结束; x和y可具有不同类型的!
}


解决方案

我不认为这可能写约束这样在F#(虽然我不知道是什么原因)。无论如何,syntacticalaly,你会想要写这样的事情(如布赖恩建议):

 键入FinallyBuilder<'T> (finallyAction:'T  - >单位)= 
会员this.Bind<'A,'B当'A:> T>(X:'A)(续:'A - >'B)= //'
试续点¯x
终于finallyAction(X:>'T)

可惜的是,这提供了以下错误:




错误FS0698:无效约束:用于约束的类型是密封的,这意味着该约束只能由至多一个溶液



$ b满足$ b

这似乎是相同的情况下,如这个邮件列表。凡唐赛姆说以下内容:




这是强加的,使F#类型推断听话的限制。特别是,在一个子类型约束的权类型必须是标称。注意形式A:>的约束; B总是急切地解决了'A ='B,如在F#规范的第14.6指定




您可以随时使用 OBJ 解决这个传递给你的建设者功能结果
修改:即使您使用 OBJ ,使用绑定的值让!将有更具体的类型(当调用 finallyAction ,F#会自动某种类型参数的值转换为 OBJ ):

 键入FinallyBuilder(finallyAction:OBJ  - >单位)= 
会员x.Bind(v,F)=
试FV
终于finallyAction v
会员x.Return(v)= v

让清理= FinallyBuilder(printfn%A)

让解析度=
清理{让!一个=新System.Random()
让! B =你好
返回3}


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

解决方案

I don't think it is possible to write constraint like this in F# (although I'm not exactly sure why). Anyway, syntacticalaly, 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) 

Unfortunatelly, this gives the following error:

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 seems to be the same case as the one discussed in this mailing list. Where Don Syme says the following:

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.6 of the F# specification.

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 }

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

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