F#泛型/函数重载语法 [英] F# generics / function overloading syntax

查看:98
本文介绍了F#泛型/函数重载语法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很困惑如何在没有显式类型声明(如('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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆