5

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

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

4

2 回答 2

4

F#通常不允许创建泛型值,因为这会引入“值限制”困难(也就是说,您不能创建泛型的语法值,即使它是返回函数的代码)。所以,你f2不应该被允许,但是......

该规则有一个例外 - 拥有像这样的泛型值通常很有用List.empty,因此如果您声明一个带有显式泛型类型参数的值,它实际上被编译成一个返回结果的函数。

这正是您的示例中发生的情况:

let f2<'a> = printfn "*bind f2*"; fun () -> printfn "f2()"

这里,f2是一个泛型值(尽管它实际上并没有在任何地方使用类型参数),但它有一个显式的泛型类型参数,因此它实际上被编译成一个方法,每次f2访问时都会调用它并返回结果(功能unit -> unit

我在规范中找不到任何明确的解释,但有一篇很好的 MSDN 文章(参见“案例 4”)和前 F# 团队成员的博客

于 2013-10-28T05:20:48.870 回答
2

像往常一样,托马斯是对的。请注意,您可以创建一个行为相同的泛型函数f1,但您必须使用名义泛型类型作为各种缓存:

type private Container<'a> () =
    static member val f2 : unit -> unit = printfn "*bind f2*"; fun () -> printfn "f2()"

let f2<'a>() = Container<'a>.f2()
于 2013-10-28T15:07:47.653 回答