[英]F# quotations: variable may escape scope
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 从http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf中的MetaOcaml示例转换而来
In the paper there is explained that the above example will yield the following with the parameters 3
and .<1>.
在本文中,解释了上面的示例将使用参数
3
和.<1>.
得出以下结果.<1>.
(in MetaOcaml notation): (以MetaOcaml表示法):
.<(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
. 如您所见,
x
被x_1
, x_2
等替换,因为x
否则只会在最内层的fun
引用x
。
But in F# this isn't allowed. 但是在F#中这是不允许的。 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."
我收到编译时错误:“变量'x'用引号引起来,但被用作拼接表达式的一部分。这是不允许的,因为它可能逃避其范围。” So the question is: how can this be changed so it will compile and have the same semantic as the MetaOcaml output?
所以问题是:如何更改它,使其可以编译并具有与MetaOcaml输出相同的语义?
Update to comment: I use the PowerPack to actually evaluating the quotation. 更新以评论:我使用PowerPack实际评估报价。 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. 更新为Tomas的答案:我真的不希望
x
是全局的,也不希望逃避范围。 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
. 您的答案为
(h 3 <@ 1 @>).Eval() = 4
,其中上面的结果为h 3 1 = 7
。 And here, I want 7
to be the answer. 在这里,我希望
7
是答案。
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. F#引号语法不支持可能会逃避范围的变量,因此您需要使用
Expr
操作显式构造树。 Something like this should do the trick: 这样的事情应该可以解决问题:
open Microsoft.FSharp.Quotations
let rec h n (z:Expr<int>) =
if n = 0 then z
else
let v = new Var("x", typeof<int>)
let ve = Expr.Var(v)
Expr.Cast<int>
(Expr.Application( Expr.Lambda(v, h (n - 1) <@ %%ve + %z @>),
Expr.Value(n)))
However, this is quite artificial example (to demonstrate variable capturing in MetaOCaml, which isn't available in F#). 但是,这是一个非常人为的示例(以演示MetaOCaml中的变量捕获,F#中不提供此功能)。 It just generates expression like
(2 + (1 + ...))
. 它只是生成像
(2 + (1 + ...))
表达式。 You can get the same result by writing something like this: 您可以通过编写类似以下内容来获得相同的结果:
let rec h n (z:Expr<int>) =
if n = 0 then z
else h (n - 1) <@ n + %z @>
Or even better: 甚至更好:
[ 1 .. 4 ] |> List.fold (fun st n -> <@ n + %st @>) <@ 0 @>
I also came accross this limitation in F# quotations and it would be nice if this was supported. 我也遇到了F#引号中的此限制,如果支持此功能会很好。 However, I don't think it is such a big problem in practice, because F# quotations are not used for staged meta-programming.
但是,我认为实际上这不是一个大问题,因为F#引号未用于分阶段的元编程。 They are more useful for analyzing existing F# code than for generating code.
它们对于分析现有的F#代码比生成代码更有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.