Quotes F #: a variable may go out of scope

I have this bit of code:

let rec hnz = if n = 0 then z else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @> 

converted from MetaOcaml example to http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf

The article explains that the above example will give the following 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, x is replaced with x_1 , x_2 , etc., because x otherwise only refers to x to the innermost fun .

But in F # this is unacceptable. I get a compile-time error: "The variable" x "is bound in the quote, but is used as part of the spliced ​​expression. This is unacceptable as it may go out of scope." So the question is: how can this be changed so that it compiles and has the same semantics as the output of MetaOcaml?

Update comment: I use PowerPack to actually rate the quote. But I do not think that this is due to this, because the error is during compilation. Currently running QuotationEvaluation. However, I know that this is not the most efficient implementation.

Update to Thomas' answer: I really don't want x be global, or to avoid scope. But I want it equivalent

 let rec hnz = if n = 0 then z else (fun x -> (h (n - 1) (x + z))) n 

with quotes. Your answer gives (h 3 <@ 1 @>).Eval() = 4 , where the above gives h 3 1 = 7 . And here I want 7 be the answer.

+5
f # metaprogramming metaocaml
source share
1 answer

The F # quote syntax does not support variables that could potentially go out of scope, so you will need to construct the tree explicitly using Expr operations. Something like this should do the trick:

 open Microsoft.FSharp.Quotations let rec hn (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 a rather artificial example (to demonstrate the capture of variables in MetaOCaml, which is not available in F #). It just generates an expression like (2 + (1 + ...)) . You can get the same result by writing something like this:

 let rec hn (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 took advantage of this limitation in F # quotes, and it would be nice if that were supported. However, I don't think this is such a big problem in practice, because F # quotes are not used for stepwise metaprogramming. They are more useful for analyzing existing F # code than for generating code.

+6
source share

All Articles