给定类型
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”的唯一重载。可能需要类型注释。候选:静态成员 T.OverloadedMethod : p:int -> unit,静态成员 T.OverloadedMethod : p:string -> unit
我们可以实现我们想要的,例如使用“操作员技巧”
//Case 2
type T2 =
static member ($) (_:T2, p:int) = T.OverloadedMethod(p)
static member ($) (_:T2, p:string) = T.OverloadedMethod(p)
let inline call2 o = Unchecked.defaultof<T2> $ o
call2 3
call2 "3"
这里的 F# 编译器(显然)做了更多的工作,而不是简单地回退到 .NET 分辨率。
然而,这看起来很难看,并且意味着代码重复。听起来案例1应该是可能的。
什么技术原因证明这种行为是合理的?我的猜测是有一些权衡(可能与 .NET 互操作性),但找不到更多信息。
编辑
我从帖子中提取了这个原因:
“特征调用是 F# 编译器功能,因此必须有两种不同的方法来编写简单调用和特征调用。对两者使用相同的语法并不方便,因为它可能会造成混淆,在简单调用的情况下可能会出现一些用途被意外编译为特征调用”。
让我们换个角度来看这个问题:
查看代码,编译器应该做什么看起来真的很简单:
1) call是一个内联函数,所以推迟编译到使用站点
2) call 3是一个使用站点,这里的参数是 int 类型的。但是 T.OverloadedMethod(int) 存在,所以让我们生成一个对它的调用
3)像以前的情况一样调用“3”,用字符串代替 int
4)调用 3.0错误,因为 T.OverloadedMethod(float) 不存在
我真的很想看到一个代码示例,其中让编译器执行此操作将是一个问题,证明要求开发人员为 trait 调用编写附加代码是合理的。
归根结底,F# 的优势之一不就是“简洁和直观”吗?
在这里,我们遇到了一个看起来可能会更好的案例。