我们来看一个简单的函数值f1:
let f1 = printfn "*bind f1*"; fun () -> printfn "f1()"
Run Code Online (Sandbox Code Playgroud)
f1 在FSI中绑定为
*bind f1*
val f1 : (unit -> unit)
Run Code Online (Sandbox Code Playgroud)
并且,被调用,表现如预期
> () |> f1 |> f1;;
f1()
f1()
val it : unit = ()
Run Code Online (Sandbox Code Playgroud)
现在让我们采用类似的函数值,但明确地通用f2<'a>:
let f2<'a> = printfn "*bind f2*"; fun () -> printfn "f2()"
Run Code Online (Sandbox Code Playgroud)
f2 在FSI中绑定为
val f2<'a> : (unit -> unit)
Run Code Online (Sandbox Code Playgroud)
没有任何*bind f2*输出,但随后被调用,在每次f2调用时输出:
> () |> f2 |> f2;;
*bind f2*
f2()
*bind f2*
f2()
val it : unit = ()
Run Code Online (Sandbox Code Playgroud)
我的问题是:这种观察到的差异可能是什么原因?
F#通常不允许创建泛型值,因为这会带来“值限制”困难(即,您无法创建泛型语法值,即使它是返回函数的代码)。所以,你f2不应该被允许,但是......
该规则有一个例外 - 拥有像 and 这样的泛型值通常很有用,List.empty因此如果您使用显式泛型类型参数声明一个值,它实际上会被编译成返回结果的函数。
这正是您的示例中发生的情况:
let f2<'a> = printfn "*bind f2*"; fun () -> printfn "f2()"
Run Code Online (Sandbox Code Playgroud)
这里,f2是一个泛型值(即使它在任何地方都没有真正使用类型参数),但它有一个显式的泛型类型参数,因此它实际上被编译成一个方法,每次访问时都会调用该方法f2并返回结果(功能unit -> unit)
我在规范中找不到对此的任何明确解释,但有一篇很好的 MSDN 文章(参见“案例 4”)以及前 F# 团队成员的博客。