9

我仍在努力思考 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,显然是从它在程序中的第一次使用中推断出来的。两者都以相同的方式声明和使用,那么为什么在概括上有所不同呢?

我知道在添加的情况下,可以通过声明内联函数来回避问题,这会导致它获得泛型类型定义,即'a * 'b -> 'c (requires member (+)),但这并不能解释为什么在这种情况下需要这样做而不是其他。

4

3 回答 3

7

@TomasP 在这里有一篇关于这个问题的精彩文章:http: //tomasp.net/blog/fsharp-generic-numeric.aspx

在编写具有某些类型参数 'T 的简单通用代码时,我们对类型参数一无所知,也无法将其限制为提供我们可能需要在代码中使用的所有运算符的数字类型。这是 .NET 运行时的限制,F# 提供了两种方法来克服它。

但是为什么<>(以及扩展,=<=>=可以呢?

F# 编译器处理equality和不同(请参阅规范comparison中的第 5.2.10 节平等和比较约束,感谢@Daniel)。你得到了特殊的约束,这是允许的(简单地说,更多细节参见规范): comparison

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

运营商没有这种特殊处理+。为什么不能有这样的约束numeric

Well isn't that operator also defined for strings? In some languages for lists and collections? Surely it would be an addable constraint and not numeric. Then many such overloaded operators can be found in a program with different semantic meaning. So F# provides a 'catch-all' method with static member constraints and the inline keyword. Only equality and comparison are special.

于 2012-05-14T20:30:30.053 回答
2

why the difference in generalization?

通用性和性能之间存在权衡。该comparison约束为min可以作用于任何 F# 类型的任何值的函数提供了通用性,但在一般情况下,它诉诸虚拟调度并且速度要慢很多倍。运算符提供有限的+通用性,并且可以对任何 F# 类型的任何值进行操作,这些值已通过重载增强但在一般情况下不起作用,因此总是非常快,因为从不需要分派。

于 2012-05-19T00:23:36.743 回答
1

comparison是一个编译时约束。所以问题仍然存在:为什么不add概括?正如 yamen 所指出的,通用数学运算需要内联,这会影响代码大小和性能——可能不是编译器应该自动执行的操作。

于 2012-05-14T19:30:44.190 回答