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

时间:2015-06-09 22:36:18

标签: f#

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

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

这是正常还是错误?这些规则是否记录在案?我该怎么做才能强制它达到预期的输出?

修改
这个答案(https://stackoverflow.com/a/4945137/1872399)陈述(Variables are automatically replaced with values if the variable is defined outside of the quotation).,但我无法在其他地方找到这一点。

编辑2:我真正想做的事
这段代码(https://gist.github.com/0x53A/8848b04c2250364a3c22)进入了catch-all案例并且失败了not implemented:parseQuotation:Value (Variable "ax1")(我希望它进入| Var(var) ->)所以不仅在编译时已知常量,而且还有函数参数扩展到它们的值。

编辑3:
我在调试器下运行了工作版本(https://gist.github.com/0x53A/53f45949db812bde5d97),看起来实际上是一个bug: 报价为{Call (None, op_Addition, [PropertyGet (None, a, []), Value (1)])}a = Program.a,因此这似乎是将模块中的绑定编译为属性这一事实的副作用。如果我是对的,我应该在Microsoft提交doc-bug ...

1 个答案:

答案 0 :(得分:2)

一般来说,引用变量的问题在于它们逃避了它们的范围。也就是说,如果您没有任何方法可以找出foo变量引用的内容,那么在引号中包含变量foo并没有任何意义。

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

<@ fun x -> x @>

但如果您有类似以下内容,则捕获变量x没有意义,因为一旦函数返回,x不再在范围内:

fun x -> <@ x @>

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

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

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

plot(years, GDP)

这个想法是plot函数会得到一个带有变量名的引号(然后可以用于例如绘图轴)。实际上an F# 4.0 change proposal允许您执行此操作。