如果我在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 ...
答案 0 :(得分:2)
一般来说,引用变量的问题在于它们逃避了它们的范围。也就是说,如果您没有任何方法可以找出foo
变量引用的内容,那么在引号中包含变量foo
并没有任何意义。
因此,例如,以下是可以的,因为变量x
由lambda定义:
<@ fun x -> x @>
但如果您有类似以下内容,则捕获变量x
没有意义,因为一旦函数返回,x
不再在范围内:
fun x -> <@ x @>
这与您描述的情况相同 - 顶级绑定成为模块的静态成员,因此可以捕获它们,但是在表达式求值后局部变量不可用,因此它们被值替换。因此,一般规则是:
有些情况下,能够捕获变量名称会很有用。我真正希望能够做的一个例子是让人们写一个例子:
plot(years, GDP)
这个想法是plot
函数会得到一个带有变量名的引号(然后可以用于例如绘图轴)。实际上an F# 4.0 change proposal允许您执行此操作。