F# 引号:变量可能会转义作用域
我有这段代码:
let rec h n z = if n = 0 then z
else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @>
从 http://www.cs 中的 MetaOcaml 示例转换而来。 Rice.edu/~taha/publications/journal/dspg04a.pdf
在论文中解释了上面的例子将产生以下参数 3
和.<1>.
(采用 MetaOcaml 表示法):
.<(fun x_1 -> (fun x_2 -> (fun x_3 -> x_3 + (x_2 + (x_1 + 1))) 1) 2) 3>.
如您所见,x
被 x_1
、x_2 替换
等等,因为x
否则只会引用最里面的fun
中的x
。
但在 F# 中这是不允许的。我收到编译时错误:“变量‘x’绑定在引号中,但用作拼接表达式的一部分。这是不允许的,因为它可能会逃脱其范围。”所以问题是:如何更改它才能编译并具有与 MetaOcaml 输出相同的语义?
更新评论:我使用 PowerPack 来实际评估报价。但我认为这与它没有任何关系,因为错误是在编译时发生的。到目前为止,QuotationEvaluation 有效。但是,我确实知道这可能不是最有效的实现。
更新 Tomas 的答案: 我真的不希望 x 是全局的,或者逃避范围。但我想要的是相当于
let rec h n z = if n = 0 then z
else (fun x -> (h (n - 1) (x + z))) n
带引号的。您的答案给出 (h 3 <@ 1 @>).Eval() = 4
,其中上述结果为 h 3 1 = 7
。在这里,我希望 7
作为答案。
I have this bit of code:
let rec h n z = if n = 0 then z
else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @>
converted from a MetaOcaml example in http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf
In the paper there is explained that the above example will yield the following with the parameters 3
and .<1>.
(in MetaOcaml notation):
.<(fun x_1 -> (fun x_2 -> (fun x_3 -> x_3 + (x_2 + (x_1 + 1))) 1) 2) 3>.
As you can see the x
´s gets replaced by x_1
, x_2
etc. because the x
would otherwise only refer to the x
in the innermost fun
.
But in F# this isn't allowed. I get the compile-time error: "The variable 'x' is bound in a quotation but is used as part of a spliced expression. This is not permitted since it may escape its scope." So the question is: how can this be changed so it will compile and have the same semantic as the MetaOcaml output?
Update to comment: I use the PowerPack to actually evaluating the quotation. But I don't think this have anything to do with it because the error is at compile-time. So far QuotationEvaluation works. However, I do know it may not be the most efficient implementation.
Update to Tomas´ answer:
I really don't want the x
to be global, or to escape scope. But I want is the equivalent to
let rec h n z = if n = 0 then z
else (fun x -> (h (n - 1) (x + z))) n
with quotations. Your answer gives (h 3 <@ 1 @>).Eval() = 4
where the above yields h 3 1 = 7
. And here, I want 7
to be the answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
F# 引号语法不支持可能逃脱作用域的变量,因此您需要使用
Expr
操作显式构造树。像这样的东西应该可以解决问题:但是,这是一个非常人为的示例(演示 MetaOCaml 中的变量捕获,这在 F# 中不可用)。它只是生成类似
(2 + (1 + ...))
的表达式。您可以通过编写如下内容获得相同的结果:或者甚至更好:
我也在 F# 引用中遇到了此限制,如果支持的话那就太好了。不过,我认为这在实践中并不是一个大问题,因为 F# 引用不用于分阶段元编程。它们对于分析现有 F# 代码比生成代码更有用。
F# quotation syntax doesn't support variables that could potentially escape the scope, so you'll need to construct the tree explicitly using the
Expr
operations. Something like this should do the trick:However, this is quite artificial example (to demonstrate variable capturing in MetaOCaml, which isn't available in F#). It just generates expression like
(2 + (1 + ...))
. You can get the same result by writing something like this:Or even better:
I also came accross this limitation in F# quotations and it would be nice if this was supported. However, I don't think it is such a big problem in practice, because F# quotations are not used for staged meta-programming. They are more useful for analyzing existing F# code than for generating code.