为什么函数绑定到它们传递的第一个类型

时间:2014-10-31 05:00:18

标签: f#

我是F#的新手。我在乱搞,我发现了一些有趣的东西,我希望有人可以告诉我幕后发生的事情。

所以我做了函数:let my_func (x, y) = x + y

然后我使用args 12调用该函数给我3。这是我期望发生的事情但是当我将两个字符串传递给my_func时,即使+是一个有字符串的有效运算符,我也会收到错误。我重新编写了我的代码,但这一次只调用my_func "cat"" dog"给了我"cat dog"。然后我尝试将12传递回my_func,但却发现my_func不再接受整数。

为什么my_func表现得这样?

let my_func (x, y) = x + y
my_func (1, 2) // produces => 3
my_func ("cat", " dog") // Error

重新运行程序 ...

let my_func (x, y) = x + y
my_func ("cat", " dog") // produces => "cat dog"
my_func (1, 2) // Error

2 个答案:

答案 0 :(得分:8)

@MarcinJuraszek向您展示了如何解决这个问题,但没有说明为什么它会发生。

你可以这样想:

F#的类型推断工作从上到下从左到右 - 所以当系统试图找到my_func的类型时,它会找到分配类型从你使用该函数的第一行开始(第一个例子是int s,第二个是string s) - 如果你根本不使用它或在FSharp Interactive中定义它将会确实默认为int

将函数声明为inline使F#能够使用statically resolved type parameters(由于某些细节,这仅适用于inline函数),然后它确实会执行类似 duck的操作-typing 从声明中找出函数需要以某种方式定义静态+运算符的类型。

您可以在函数类型中看到这一点:

val inline my_func :
  x: ^a * y: ^b ->  ^c
    when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)

这个相当复杂的类型就是这样说的:

在函数体中编写(+) : ^a * ^b -> ^c时,必须在^a(想象'a)上使用静态运算符+。正如您所看到的,这比您真正需要的更通用,但这不是问题。 F#将实现具体版本(替换泛型类型)以适用于您应用的此函数(因此在您的示例中,IL中将有两个my_func实例化;一个用于Int,一个用于String s) - 但这在设计时根本不会打扰你。

因此,您现在可以使用更多泛型函数:

  • (+) : Int * Int -> Int Int
  • (+) : String * String -> String String

答案 1 :(得分:2)

您必须使用inline关键字声明您的方法才能使其正常工作:

let inline my_func (x,y) = x + y

在MSDN上阅读更多内容:Inline Functions (F#)