F#中的通用函数

时间:2012-05-14 19:20:47

标签: generics f# automatic-generalization

我仍然试图围绕F#如何概括(或不是)函数和类型,并且有一个案例让我烦恼:

let min(a, b) = if a < b then a else b

let add(a, b) = a + b

let minInt = min(3, 4)
let minFloat = min(3.0, 4.0) // works!

let addInt = add(3, 5)
let addFloat = add(3.0, 5.0) // error: This expression was expected to have type
                             // int but here has type float

这里min具有泛型类型'a * 'a -> 'a (requires comparison),而add具有一个具体类型int * int -> int,显然是从它在程序中的第一次使用推断出来的。两者都以相同的方式声明和使用,那么为什么推广的区别呢?

我理解在add的情况下,通过声明函数inline来解决问题,这会导致它获得泛型类型定义,即'a * 'b -> 'c (requires member (+)),但这并不能解释为什么这样做在这种情况下需要而不是另一种。

3 个答案:

答案 0 :(得分:7)

@TomasP在这个问题上有一篇很好的文章:http://tomasp.net/blog/fsharp-generic-numeric.aspx

  

当编写具有某些类型参数'T的简单通用代码时,我们   什么都不知道类型参数,没有办法   将它限制为一个数字类型,提供我们所有的运算符   可能需要在我们的代码中使用。这是.NET运行时的限制   而F#提供了两种克服它的方法。

但为什么<>(以及=<=>=)为什么好?

F#编译器对equalitycomparison的处理方式不同(请参阅specs中的5.2.10等式和比较约束一节,感谢@Daniel)。您获得特殊 comparison约束,这是允许的(简单地,请参阅规范以获取更多详细信息):

  

如果类型是命名类型,则类型定义不具有,   并且不推断具有NoComparison属性和类型   definition实现System.IComparable或者是数组类型或者是   System.IntPtr或是System.UIntPtr。

+运营商没有这种特殊处理方式。为什么不存在诸如numeric

之类的约束

那么运营商是否也为字符串定义了?在某些语言中列表和集合?肯定会是addable约束而不是numeric。然后可以在具有不同语义含义的程序中找到许多这样的重载运算符。所以F#提供了一个带有静态成员约束和inline关键字的'catch-all'方法。只有equalitycomparison是特殊的。

答案 1 :(得分:2)

  

为何泛化的区别?

在一般性和表现之间存在权衡。 comparison约束为诸如min之类的函数提供了通用性,这些函数可以对任何F#类型的任何值起作用,但在一般情况下它会转向虚拟分派并且慢很多倍。 +运算符提供了有限的通用性,并且可以对任何已经过载扩充但在一般情况下不起作用的F#类型的任何值起作用,因此总是非常快,因为永远不需要调度。 / p>

答案 2 :(得分:1)

comparison是编译时约束。所以问题仍然存在:为什么不add一般化?正如yamen指出的那样,泛型数学操作需要内联,这会影响代码大小和性能 - 可能不是编译器应该自动执行的操作。