什么时候将代码引用中的 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 可以让你做到这一点。
如果我 运行 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 可以让你做到这一点。