Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F#重载泛化_F#_Overloading_Generalization - Fatal编程技术网

F#重载泛化

F#重载泛化,f#,overloading,generalization,F#,Overloading,Generalization,给定类型 type T = static member OverloadedMethod(p:int) = () static member OverloadedMethod(p:string) = () 假设我们想要创建一个通用函数,该函数根据参数的类型解析为特定的重载。最直观的方法是 //Case 1 let inline call o = T.OverloadedMethod o //error call 3 call "3" 但是,尽管有内联定义,但这不起作用,

给定类型

type T = 
    static member OverloadedMethod(p:int) = ()
    static member OverloadedMethod(p:string) = ()
假设我们想要创建一个通用函数,该函数根据参数的类型解析为特定的重载。最直观的方法是

//Case 1

let inline call o = T.OverloadedMethod o //error

call 3
call "3"
但是,尽管有内联定义,但这不起作用,编译器会抱怨

错误FS0041无法为方法“OverloadedMethod”创建唯一重载 根据该程序点之前的类型信息确定。A. 可能需要类型注释。候选人:静态成员 T.OverloadedMethod:p:int->unit,静态成员T.OverloadedMethod: p:字符串->单位

我们可以实现我们想要的,例如使用“操作员技巧”

//案例2
T2型=
静态成员($)(u:T2,p:int)=T.重载方法(p)
静态成员($)(u3;:T2,p:string)=T.重载方法(p)
让内联调用2 o=Unchecked.defaultof$o
电话23
呼叫2“3”
这里的F#编译器(显然)做了更多的工作,而不是简单地回到.NET分辨率

然而,这看起来很难看,并且意味着代码重复。听起来案例1应该是可能的

有什么技术原因可以证明这种行为?我的猜测是有一些折衷(可能与.NET互操作性有关),但找不到更多信息

编辑

从这些帖子中,我摘录了这一点作为理由:

“trait调用是F#编译器的一项功能,因此必须有两种不同的方法来编写简单调用和trait调用。对这两种调用使用相同的语法并不方便,因为这可能会造成混淆,在将简单调用意外编译为trait调用的情况下可能会出现一些用法。”

让我们从另一个角度来看待这个问题:

从代码来看,编译器应该做什么似乎很简单:

1) 调用是一个内联函数,因此将编译推迟到使用站点

2) 调用3是一个使用站点,其中参数的类型为int。但是T.OverloadedMethod(int)存在,所以让我们生成对它的调用

3) 调用“3”与前面的案例类似,使用字符串代替int

4) 调用3.0错误,因为T.OverloadedMethod(float)不存在

我真的希望看到一个代码示例,在这个示例中,让编译器这样做是一个问题,需要开发人员为trait调用编写额外的代码

归根结底,F#的优势之一不是“简洁和直观”吗


在这里,我们遇到了一个情况,它看起来可能会更好。

权衡取舍源于这样一个事实,即这是一个完全删除的编译器技巧。这意味着:

  • C#code无法看到(并利用)任何
    call2
    函数,它只能看到您的两个
    $
    方法重载
  • 所有关于
    call2
    的信息在运行时都消失了,例如,这意味着您无法通过反射来调用它
  • 它不会出现在调用堆栈中。这可能是好事,也可能是坏事——例如,在最新版本的F#编译器中,它们选择性地内联了某些函数,以使
    async
    stacktraces更好一些
  • F#在呼叫现场烘焙。如果您在程序集中的调用代码是程序集A
    call2
    来自程序集B,则不能仅用新版本的
    call2
    替换程序集B;您必须针对新程序集重新编译。这可能是一个潜在的向后兼容性问题

  • 一个相当有趣的好处是,在特殊情况下,它可以显著提高性能:。另一方面,我确信在某些情况下,它可能会造成积极的伤害,或者只是导致IL代码膨胀。

    这种权衡源自这样一个事实,即这是一个完全被擦除的编译器技巧。这意味着:

  • C#code无法看到(并利用)任何
    call2
    函数,它只能看到您的两个
    $
    方法重载
  • 所有关于
    call2
    的信息在运行时都消失了,例如,这意味着您无法通过反射来调用它
  • 它不会出现在调用堆栈中。这可能是好事,也可能是坏事——例如,在最新版本的F#编译器中,它们选择性地内联了某些函数,以使
    async
    stacktraces更好一些
  • F#在呼叫现场烘焙。如果您在程序集中的调用代码是程序集A
    call2
    来自程序集B,则不能仅用新版本的
    call2
    替换程序集B;您必须针对新程序集重新编译。这可能是一个潜在的向后兼容性问题

  • 一个相当有趣的好处是,在特殊情况下,它可以显著提高性能:。另一方面,我敢肯定,在某些情况下,它可能会造成积极的伤害,或者只是导致IL代码膨胀。

    这种行为的原因是

    let inline call o = T.OverloadedMethod o
    
    这不是一种特质。必须在调用站点解决的是相当简单的.NET重载,但是由于函数类型并不意味着要解决的重载无法编译,因此需要此功能

    如果要“延迟”重载解析,则需要执行trait调用,将函数内联是必要的,但还不够:

    let inline call (x:'U) : unit =
        let inline call (_: ^T, x: ^I) = ((^T or ^I) : (static member OverloadedMethod: _ -> _) x)
        call (Unchecked.defaultof<T>, x)
    
    let内联调用(x:'U):单位=
    让内联调用(^T,x:^I)=(^T或^I):(静态成员重载方法:“->”x)
    调用(未选中的.defaultof,x)
    

    通过自动推断这些约束,使用运算符可以节省许多击键次数,但正如您在问题中所看到的,它需要在重载中包含一个伪参数。

    这种行为的原因是

    let inline call o = T.OverloadedMethod o
    
    这不是一种特质。这是一个相当简单的.NET重载,必须在调用站点解决,但由于函数类型并不意味着要解决哪个重载,因此它无法解决com