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#的优势之一不是“简洁和直观”吗
在这里,我们遇到了一个情况,它看起来可能会更好。权衡取舍源于这样一个事实,即这是一个完全删除的编译器技巧。这意味着:
call2
函数,它只能看到您的两个$
方法重载call2
的信息在运行时都消失了,例如,这意味着您无法通过反射来调用它async
stacktraces更好一些call2
来自程序集B,则不能仅用新版本的call2
替换程序集B;您必须针对新程序集重新编译。这可能是一个潜在的向后兼容性问题一个相当有趣的好处是,在特殊情况下,它可以显著提高性能:。另一方面,我确信在某些情况下,它可能会造成积极的伤害,或者只是导致IL代码膨胀。这种权衡源自这样一个事实,即这是一个完全被擦除的编译器技巧。这意味着:
call2
函数,它只能看到您的两个$
方法重载call2
的信息在运行时都消失了,例如,这意味着您无法通过反射来调用它async
stacktraces更好一些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