首页 文章

F#泛型/函数重载语法

提问于
浏览
10

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

let add a b = a + b

这给了我们

val add : a:int -> b:int -> int

但是我们可以立即打电话

add "Hello " "World!"

现在add的值是

val add : a:string -> b:string -> string
val it : string = "Hello World!"

如果我们再打电话

add 2 3 // then we get
error: This expression was expected to have type string but here has type int

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

3 回答

  • 17

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

    试试这个:

    > let mapPair f (x,y) = (f x, f y)
    val mapPair : f:('a -> 'b) -> x:'a * y:'a -> 'b * 'b
    

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

    现在试试这个:

    > let makeList a b = [a;b]
    val makeList : a:'a -> b:'a -> 'a list
    

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

    > let 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 list
        when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)
    

    嗯,有趣 . 事实证明,如果我使函数 inline ,那么F#确实认为它是通用的,但它也给它这个奇怪的 when 子句,而我的通用参数有这个奇怪的 ^ 符号而不是通常的勾号 .
    这个奇怪的语法被称为"statically resolved type parameters"(参见here,有点连贯的解释),基本思想是函数 (+) 要求其参数定义 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
    

    现在,问题是CLR不支持这种通用参数(即"any type, as long as it has such and such members"),所以F#必须伪造它并在编译时解析这些调用 . 但正因为如此,使用此功能的任何方法都无法编译为真正的通用IL方法,因此必须是单态的(由 inline 启用) .

    但是,要求使用算术运算符的每个函数都被声明为 inline ,而不是'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'为什么你的函数只要用 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)
    
  • 2

    如果我理解正确,请使用内联:

    let inline add a b = a + b
    
    add 2 3 |> printfn "%A"
    add "Hello " "World!" |> printfn "%A"
    

    打印:

    5
    "Hello World!"
    

    链接:http://ideone.com/awsYNI

  • 2

    成为inline

    let inline add a b = a + b
    (*
    val inline add :
      a: ^a -> b: ^b ->  ^c
        when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)
    *)
    add "Hello " "World!"
    // val it : string = "Hello World!"
    add 2 3
    // val it : int = 5
    

相关问题