为什么显式泛型函数值与类似的非泛型不同?

时间:2013-10-28 05:04:14

标签: generics f#

我们采用一个简单的函数值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 = ()

我的问题是:这种观察到的差异可能是什么原因?

2 个答案:

答案 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()