我们采用一个简单的函数值f1
:
let f1 = printfn "*bind f1*"; fun () -> printfn "f1()"
f1
在FSI中绑定为
*bind f1*
val f1 : (unit -> unit)
并且,被调用,表现如预期
> () |> f1 |> f1;;
f1()
f1()
val it : unit = ()
现在让我们采用类似的函数值,但明确地使用f2<'a>
:
let f2<'a> = printfn "*bind f2*"; fun () -> printfn "f2()"
f2
在FSI中绑定为
val f2<'a> : (unit -> unit)
没有任何*bind f2*
输出,但随后被调用,在每次f2
调用时输出它:
> () |> f2 |> f2;;
*bind f2*
f2()
*bind f2*
f2()
val it : unit = ()
我的问题是:这种观察到的差异可能是什么原因?
答案 0 :(得分:4)
F#不会通常允许创建通用值,因为它引入了“值限制”的困难(也就是说,即使它是代码,也无法创建通用的语法值)返回功能)。因此,不应允许f2
,而是......
规则有一个例外 - 拥有像List.empty
这样的通用值通常很有用,所以如果用一个显式泛型类型参数声明一个值,它实际上会被编译成一个返回结果的函数。 / p>
这正是你的例子中发生的事情:
let f2<'a> = printfn "*bind f2*"; fun () -> printfn "f2()"
这里,f2
是一个通用值(即使它实际上并没有在任何地方使用类型参数),但它有一个显式的泛型类型参数,所以实际上它被编译成每次调用的方法访问f2
并返回结果(函数unit -> unit
)
我在规范中找不到任何明确的解释,但有good MSDN article(参见“案例4”)和blog by former F# team member。
答案 1 :(得分:2)
f1
相同的泛型函数,但您必须使用名义泛型类型作为排序缓存:
type private Container<'a> () =
static member val f2 : unit -> unit = printfn "*bind f2*"; fun () -> printfn "f2()"
let f2<'a>() = Container<'a>.f2()