我是F#的新手。我在乱搞,我发现了一些有趣的东西,我希望有人可以告诉我幕后发生的事情。
所以我做了函数:let my_func (x, y) = x + y
。
然后我使用args 1
和2
调用该函数给我3
。这是我期望发生的事情但是当我将两个字符串传递给my_func
时,即使+
是一个有字符串的有效运算符,我也会收到错误。我重新编写了我的代码,但这一次只调用my_func
"cat"
和" dog"
给了我"cat dog"
。然后我尝试将1
和2
传递回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
答案 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)