这对我来说实际上是非常出乎意料的,但请考虑一下 F# 中的这段代码:
let f x =
printfn $"{x}"
fun x' -> x'
let y<'t> = f 1 //> val y<'t> : (obj -> obj)
y 2
//>
//1
//val it: obj = 2
Run Code Online (Sandbox Code Playgroud)
我期望的是,只有当您将 f 1 绑定到“y”时,它才会打印“1”(这会告诉我“f”主体只执行一次),但似乎它在每次调用时都执行“f”主体的“y”。这是与自动循环相关的不可避免的影响,还是我遗漏了一些东西,并且有一种方法可以在返回函数的每次调用上绕过外部函数体执行?
关于这里发生的事情的提示是事实 已't被约束obj且 的签名y是(obj -> obj)。这就是 F# 编译器有效地说:“我放弃,这些没有真正的类型,无论它是什么”,并发出可以在运行时执行但没有任何真正类型安全的东西。
这样做的副作用是,因为它无法“固定”y到已知签名,所以它无法评估f,因此它只是y作为对 的直接调用发出f,因为您已经通过参数化它有效地告诉编译器这很好with 't(最终只是成为obj或“无论什么”)。
为什么会发生这种情况?价值限制!
我怀疑您已经在 F# Interactive 中逐块评估了这一点。定义的代码行let y = f 1无法使用更多信息进行编译。您可以通过两种方式执行此操作:
y真实类型,将其签名固定到您正在使用的类型。let y: int -> int = f 1这样它就被固定为一个具体的类型。这就是为什么如果您在 FSI 中执行整个代码片段或将其作为程序运行,事情就会像您期望的那样工作:
let f x =
printfn $"{x}"
fun x' -> x'
let y = f 1
y 2
y 3
Run Code Online (Sandbox Code Playgroud)
这是因为它y是通用的。
每次您引用时y,您都会选择一个特定的内容't来与之配合。例如:
let a = y<int>
let b = y<string>
Run Code Online (Sandbox Code Playgroud)
a和b不能是相同的值,因为它们是从 的不同实例中获得的y。它们必须是两个不同的值。这又意味着y它本身不能是单一值。它必须是一个函数。
这就是它的本质:它被编译为函数,每次引用它时,都会使用您选择的通用参数实例化该函数,并执行函数体以获得结果。
如果删除通用参数并给出具体y类型,问题就会消失:
let y = f 1 : obj -> obj
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
77 次 |
| 最近记录: |