简体   繁体   English

F#泛型/函数重载语法

[英]F# generics / function overloading syntax

I'm confused on how to label a function as generic without an explicit type declaration like ('a -> 'a) 我对如何在没有显式类型声明的情况下将函数标记为泛型感到困惑('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 现在add的值是

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. 这是F#壁橱中令人尴尬的骨架。

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. 啊哈,只要我有一个(+) ,它就会由于某种原因变成int
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. 事实证明,如果我将函数设置为inline ,则F# 确实认为它是泛型的,但它也为它提供了一个怪异的when子句,并且我的泛型参数使用了这个奇怪的^符号,而不是通常的刻度。
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. 这种奇怪的语法称为“静态解析的类型参数”(请参见此处 ,获得一些连贯的解释),其基本思想是函数(+)要求其参数具有定义的static member (+) 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 (ie "any type, as long as it has such and such members"), so F# has to fake it and resolve these calls at compile time. 现在,与此有关的问题是CLR不支持这种通用参数(即“任何类型,只要它具有这样和这样的成员”),因此F#必须伪造它并在编译时解析这些调用。 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 ). 但是正因为如此,使用此功能的任何方法都不能编译为真正的通用IL方法,因此必须进行单态化(由inline启用)。

But then, it would be very inconvenient to require that every function that uses arithmetic operators be declared inline , wouldn't it? 但是,然后要求将使用算术运算符的每个函数都声明为inline是非常不便的,不是吗? 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. 因此,F#又走了一步,并尝试根据稍后在代码中实例化的方式来修复这些静态解析的通用参数。 That's why your function turns into string->string->string as soon as you use it with a string once. 这就是为什么一旦将函数与string一起使用,函数就会变成string->string->string的原因。

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: 但是,如果将函数标记为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)

If I understand you correctly, use inline: 如果我对您的理解正确,请使用内联:

let inline add a b = a + b

add 2 3 |> printfn "%A"
add "Hello " "World!" |> printfn "%A" 

Print: 打印:

5
"Hello World!"

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

Make it 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

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

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