我是F#的新手,我很惊讶地发现f x y = x + y
的类型实际上是int -> int -> int
。 Appearently,这是由于一些表现权衡。
但为什么这实际上是必要的?为什么不将类型推断为'a -> 'a -> 'a
或类似的东西?它似乎可以进行比较:g x y = x < y
的类型是x:'a -> y:'a -> bool when 'a : comparison
。为什么不对算术运算符呢?
编译器无法从调用站点静态推断出特定的基本类型,并从那里专门化泛型函数,如果失败则回退到某些动态调度?
这可能很明显,但我找不到任何好的资源。这种行为背后的原因是什么?
答案 0 :(得分:10)
是的,对于那些运算符int
是推断的默认类型,除非您指定另一个或由使用推断。如果要为所有类型定义它们,则必须使函数内联:
let inline f x y = x + y
但请注意签名是:
x: ^a -> y: ^b -> ^c
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
这是因为在.NET中你不能使用member constraints但是F#在编译时解析它们。这就是为什么你会看到那些'帽子类型'以及那些类型应该定义静态成员(+)的约束。
另外请注意,类型变量不是你建议的a -> a -> a
,因为在.NET框架中并非所有的加法操作都遵循该签名。在像Haskell这样的其他环境中,情况有所不同,在那里添加严格a -> a -> a
但在.NET中你可以添加例如TimeSpan到DateTime:
System.DateTime(2000,1,1) + new System.TimeSpan(1, 2, 0, 30, 0)
结果是DateTime,签名为:a -> b -> a
比较是一个不同的故事,因为该约束实际上存在于.NET级别,因此它可以在IL中编译和编码,而成员约束需要在编译时解决,这就是为什么函数必须标记为内联。 / p>
我认为你误解了链接问题中的解释:这不是由于性能权衡,真正的原因是.NET类型的系统限制。内联函数在大多数情况下执行得更快(因为它由编译器内联)这一事实是次要影响。