F#泛型/函数重载语法

t3d*_*son 11 generics f# types function

我很困惑如何将函数标记为泛型而没有类似的显式类型声明 ('a -> 'a)

let add a b = a + b
Run Code Online (Sandbox Code Playgroud)

这给了我们

val add : a:int -> b:int -> int
Run Code Online (Sandbox Code Playgroud)

但是我们可以立即打电话

add "Hello " "World!"
Run Code Online (Sandbox Code Playgroud)

现在add的值是

val add : a:string -> b:string -> string
val it : string = "Hello World!"
Run Code Online (Sandbox Code Playgroud)

如果我们再打电话

add 2 3 // then we get
error: This expression was expected to have type string but here has type int
Run Code Online (Sandbox Code Playgroud)

如何确保函数适用于所有已(+)定义函数的类型

Fyo*_*kin 18

这是F#在壁橱里尴尬的骨架.

试试这个:

> let mapPair f (x,y) = (f x, f y)
val mapPair : f:('a -> 'b) -> x:'a * y:'a -> 'b * 'b
Run Code Online (Sandbox Code Playgroud)

完全通用!显然,函数应用程序和元组工作.

现在试试这个:

> let makeList a b = [a;b]
val makeList : a:'a -> b:'a -> 'a list
Run Code Online (Sandbox Code Playgroud)

嗯,也是通用的.这个怎么样:

> let makeList a b = [a + b]
val makeList : a:int -> b:int -> int list
Run Code Online (Sandbox Code Playgroud)

啊哈,只要我(+)在那里,就会int出于某种原因.
让我们继续玩:

> 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)
Run Code Online (Sandbox Code Playgroud)

嗯,有趣.事实证明,如果我创建函数inline,那么F#确实认为它是通用的,但它也给它这个奇怪的when子句,而我的泛型参数有这个奇怪的^符号而不是通常的tick.
这种奇怪的语法被称为"静态解析类型参数"(这里有一些相关的解释),其基本思想是函数(+)要求其参数具有已static member (+)定义的参数.我们来验证:

> 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
Run Code Online (Sandbox Code Playgroud)

现在,问题是CLR不支持这种通用参数(即"任何类型,只要它有这样的成员"),因此F#必须伪造它并在编译时解析这些调用.但正因为如此,使用此功能的任何方法都无法编译为真正的通用IL方法,因此必须是单态的(由其启用inline).

但是,要求声明使用算术运算符的每个函数都是非常不方便的inline,不是吗?因此,F#又采取了另一个步骤,并尝试根据它们在代码中稍后实例化的方式来修复这些静态解析的泛型参数.这就是为什么你的功能string->string->string一旦与你一起使用就会变成原因string.

但是如果你标记你的函数inline,F#将不必修复参数,因为它不必将函数编译为IL,因此你的参数保持不变:

> let inline add a b = a + b
val inline add :
   a: ^a -> b: ^b ->  ^c
      when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)
Run Code Online (Sandbox Code Playgroud)