F#泛型/函数重载语法 [英] F# generics / function overloading syntax
问题描述
我很困惑如何在没有显式类型声明(如('a - >'a)
let add ab = a + b
这给了我们
val add:a:int - > b:int - > int
然而,我们可以立即调用
添加你好世界!
现在add的值是
val add:a:string - > b:字符串 - >字符串
val it:string =Hello World!
如果我们接着调用
add 2 3 //然后我们得到
错误:这个表达式预计有字符串类型但是这里有类型int
如何确保某个函数可以在所有类型的函数中都有效,这些类型的函数定义了(+)
>
这是F#在衣柜里尴尬的骷髅。
试试这个:
>让mapPair f(x,y)=(f x,f y)
val mapPair:f :('a - >'b) - > x:'a * y:'a - > 'b *'b
完全一般!显然,函数应用程序和元组是可以工作的。
现在试试这个:
>让makeList a b = [a; b]
val makeList:a:'a - > b:'a - > 'a list
嗯,也是通用的。这样如何:
>让makeList a b = [a + b]
val makeList:a:int - > b:int - > int list
当我有一个(+)
在那里,因为某些原因它会变成
int
。
让我们继续玩:
> let inline makeList a b = [a + b]
val inline makeList:
a:^ a - > b:^ b - > ^ c列出
当(^ a或^ b):(静态成员(+):^ a * ^ b - > ^ c)
嗯,有趣。原来,如果我让函数 inline
,那么F#确实是,它认为它是通用的,但它也给它这个奇怪的当
子句时,我的泛型参数有这个奇怪的 ^
符号,而不是通常的tick。
这个奇怪的语法被调用静态解析的类型参数(参见这里有一个连贯的解释),基本思想是函数(+)
要求它的参数有一个定义了静态成员(+)
。让我们来验证一下:
>让x = 0:> obj
let y = 0:> obj
let z = x + y
Script1.fsx(14,13):错误FS0001:'obj'类型不支持操作符'+'
>键入My()=
static member(+)(a:My,b:My)= My()
let x = My()
let y = My()
让z = x + y
val x:My
val y:My
val z:My
现在,问题在于CLR不支持这种通用参数(即任何类型,只要它具有这样和那样的成员),所以F#必须<假的它并在编译时解决这些调用。但是正因为如此,任何使用这个特性的方法都不能被编译为真正的通用IL方法,因此必须单态化(由 inline
启用)。 然后,要求每个使用算术运算符的函数都被声明为 inline
,wouldn会很不方便,wouldn是吗?因此,F#还有一个额外的步骤,并尝试根据代码中稍后实例化的方式修复这些静态解析的泛型参数。这就是为什么当你使用字符串
时,你的函数会变成 string-> string-> string
一次。
但是如果你标记你的函数 inline
,F#不需要修改参数,因为它不会不必将函数编译为IL,因此参数保持不变:
>让inline添加一个b = a + b
val inline add:
a:^ a - > b:^ b - > (^ a或^ b):(静态成员(+):^ a * ^ b - > ^ c)
I'm confused on how to label a function as generic without an explicit type declaration like ('a -> 'a)
let add a b = a + b
This gives us
val add : a:int -> b:int -> int
However we can then immediately call
add "Hello " "World!"
and now the value of add is
val add : a:string -> b:string -> string
val it : string = "Hello World!"
If we then call
add 2 3 // then we get
error: This expression was expected to have type string but here has type int
How do I ensure that a function works on all types that say have the function (+)
defined
This is F#'s embarrassing skeleton in the closet.
Try this:
> let mapPair f (x,y) = (f x, f y)
val mapPair : f:('a -> 'b) -> x:'a * y:'a -> 'b * 'b
Fully generic! Clearly, function application and tuples work.
Now try this:
> let makeList a b = [a;b]
val makeList : a:'a -> b:'a -> 'a list
Hmmm, also generic. How about this:
> let makeList a b = [a + b]
val makeList : a:int -> b:int -> int list
Aha, as soon as I have a (+)
in there, it becomes int
for some reason.
Let's keep playing:
> let inline makeList a b = [a + b]
val inline makeList :
a: ^a -> b: ^b -> ^c list
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
Hmmm, interesting. Turns out, if I make the function inline
, then F# does consider it generic, but it also gives it this weird when
clause, and my generic parameters have this strange ^
symbol instead of the usual tick.
This strange syntax is called "statically resolved type parameters" (see here for a somewhat coherent explanation), and the basic idea is that the function (+)
requires its arguments to have a static member (+)
defined. Let's verify:
> let x = 0 :> obj
let y = 0 :> obj
let z = x + y
Script1.fsx(14,13): error FS0001: The type 'obj' does not support the operator '+'
> type My() =
static member (+)( a:My, b:My ) = My()
let x = My()
let y = My()
let z = x + y
val x : My
val y : My
val z : My
Now, the problem with this is that CLR does not support this kind of generic parameters (i.e. "any type, as long as it has such and such members"), so F# has to fake it and resolve these calls at compile time. But because of this, any methods that use this feature cannot be compiled to true generic IL methods, and thus have to be monomorphised (which is enabled by inline
).
But then, it would be very inconvenient to require that every function that uses arithmetic operators be declared inline
, wouldn't it? So F# goes yet another extra step and tries to fix these statically resolved generic parameters based on how they are instantiated later in the code. That's why your function turns into string->string->string
as soon as you use it with a string
once.
But if you mark your function inline
, F# wouldn't have to fix parameters, because it wouldn't have to compile the function down to IL, and so your parameters remain intact:
> let inline add a b = a + b
val inline add :
a: ^a -> b: ^b -> ^c
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
这篇关于F#泛型/函数重载语法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!