简体   繁体   English

F#中的通用函数

[英]Generic functions in F#

I'm still trying to wrap my head around how F# generalizes (or not) functions and types, and there's a case that's bugging me: 我仍然试图围绕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

Here min has the generic type 'a * 'a -> 'a (requires comparison) while add has a concrete type int * int -> int , apparently inferred from its first use in the program. 这里min有通用类型'a * 'a -> 'a (requires comparison)而add有一个具体类型int * int -> int ,显然是从它在程序中的第一次使用推断出来的。 Both are declared and used in the same way, so why the difference in generalization? 两者都以相同的方式声明和使用,那为什么泛化的区别呢?

I understand that in the case of add, the problem can be side-stepped by declaring the function inline, which causes it to get a generic type definition, ie 'a * 'b -> 'c (requires member (+)) , but that doesn't explain why this is needed in this case and not the other. 我理解在add的情况下,通过声明函数inline来解决问题,这会导致它获得泛型类型定义,即'a * 'b -> 'c (requires member (+)) ,但这并不能解释为什么在这种情况下需要这种情况而不是另一种情况。

There is an excellent write up on this very issue by @TomasP here: http://tomasp.net/blog/fsharp-generic-numeric.aspx @TomasP在这个问题上有一篇很好的文章: http ://tomasp.net/blog/fsharp-generic-numeric.aspx

When writing simple generic code that has some type parameter 'T, we don't know anything about the type parameter and there is no way to restrict it to a numeric type that provides all the operators that we may need to use in our code. 在编写具有某个类型参数'T的简单通用代码时,我们对类型参数一无所知,并且无法将其限制为提供我们可能需要在代码中使用的所有运算符的数字类型。 This is a limitation of the .NET runtime and F# provides two ways for overcoming it. 这是.NET运行时的限制,F#提供了两种克服它的方法。

But why are < and > (and by extension, = , <= and >= ) OK? 但为什么<> (以及扩展, =<=>= )好吗?

The F# compiler treats equality and comparison differently (see section 5.2.10 Equality and Comparison Constraints in the specs , thanks @Daniel). F#编译器以不同的方式处理equalitycomparison (请参阅规范中的5.2.10等式和比较约束,感谢@Daniel)。 You get the special comparison constraint, which is allowed when (simply, see the spec for more detail): 你得到了特殊的 comparison约束,这是允许的(简单来说,请参阅规范了解更多细节):

If the type is a named type, then the type definition does not have, and is not inferred to have, the NoComparison attribute, and the type definition implements System.IComparable or is an array type or is System.IntPtr or is System.UIntPtr. 如果类型是命名类型,那么类型定义没有,也没有推断出具有NoComparison属性,并且类型定义实现System.IComparable或者是数组类型或者是System.IntPtr或者是System.UIntPtr 。

There is no such special handling for the + operator. +运算符没有这样的特殊处理。 Why couldn't there be a constraint such as numeric ? 为什么不能存在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 . 当然它将是一个可addable约束而不是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. 所以F#提供了一个带有静态成员约束和inline关键字的'catch-all'方法。 Only equality and comparison are special. 只有equalitycomparison才是特殊的。

why the difference in generalization? 为何泛化的区别?

There is a trade-off between generality and performance. 在一般性和表现之间存在权衡。 The comparison constraint provides generality for functions like min that can act upon any value of any F# type but in the general case it resorts to virtual dispatch and is many times slower. comparison约束为像min这样的函数提供了通用性,它可以对任何F#类型的任何值起作用,但在一般情况下它会转向虚拟分派并且慢很多倍。 The + operator provides restricted generality and can act upon any value of any F# type that has been augmented with an overload but does not work in the general case and, therefore, is always very fast because dispatch is never needed. +运算符提供有限的通用性,并且可以对任何已经过载增强但在一般情况下不起作用的F#类型的任何值起作用,因此总是非常快,因为永远不需要调度。

comparison is a compile-time constraint. comparison是编译时约束。 So the question remains: why isn't add generalized? 所以问题仍然存在:为什么不add一般化? As yamen pointed out, generic math ops require inlining, which affects code size and performance—probably not something the compiler should do automatically. 正如yamen指出的那样,泛型数学操作需要内联,这会影响代码大小和性能 - 可能不是编译器应该自动执行的操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM