什么时候将代码引用中的 let 绑定替换为它们的值?

When are let bindings in code quotations replaced with their values?

如果我 运行 F# Interactive window 中来自 MSDN (https://msdn.microsoft.com/en-us/library/dd233212.aspx) 的第一个示例 window,我得到预期的输出:

fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10

但是如果我在我的程序的 Main 中 运行 它,所有 let 绑定都被它们的常量值替换:

[<EntryPoint>]
let main argv = 

    let a = 2

    // exprLambda has type "(int -> int)".
    let exprLambda = <@ fun x -> x + 1 @>
    // exprCall has type unit.
    let exprCall = <@ a + 1 @>

    println exprLambda
    println exprCall
    println <@@ let f x = x + 10 in f 10 @@>

结果:

fun (x:System.Int32) -> x + 1
2 + 1
let f = fun (x:System.Int32) -> x + 10 in f 10

这是正常现象还是错误?是否记录了此规则?我可以做些什么来强制它达到预期的输出?

编辑:
这个答案 () 指出 (Variables are automatically replaced with values if the variable is defined outside of the quotation). 但我在其他地方找不到任何提及。

编辑2:我真正想做的事
此代码 (https://gist.github.com/0x53A/8848b04c2250364a3c22) 进入包罗万象的情况,并以 not implemented:parseQuotation:Value (Variable "ax1") 失败(我预计它会进入 | Var(var) ->),因此不仅是编译时已知的常量,而且还有函数参数扩展到它们的值。

编辑 3:
我在调试器下 运行 工作版本 (https://gist.github.com/0x53A/53f45949db812bde5d97),看起来那个实际上是错误: 引用是 {Call (None, op_Addition, [PropertyGet (None, a, []), Value (1)])} witha = Program.a,所以这似乎是模块中的 let 绑定被编译成属性这一事实的副作用。如果我是正确的,我也许应该在 Microsoft 提交文档错误...

一般来说,引用变量的问题是它们会超出范围。也就是说,如果您无法找出 foo 变量所指的内容,那么在您的引文中使用变量 foo 并没有任何意义。

因此,例如,以下是可以的,因为变量 x 是由 lambda 定义的:

<@ fun x -> x @>

但是如果你有类似下面的东西,捕获变量x就没有意义了,因为一旦函数returns,x就不再在范围内:

fun x -> <@ x @>

这与您描述的情况相同 - 顶级绑定成为模块的静态成员,因此可以捕获它们,但局部变量在表达式求值后不可用,因此它们被替换为值。所以,一般规则是:

  • 访问顶级绑定引用为静态成员 getter
  • 访问引号内定义的变量被捕获为变量访问
  • 访问函数的局部变量会捕获变量的值

在某些情况下,能够捕获变量名肯定会有用。我真正希望能够做的一个例子是让人们写例如:

plot(years, GDP)

这个想法是 plot 函数会得到一个带有变量名的引号(然后可以用于例如绘图轴)。实际上 an F# 4.0 change proposal 可以让你做到这一点。