5

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.

Lasse Espeholt
  • 17,622
  • 5
  • 63
  • 99
  • I don't know MeyaOCaml, but it seems like you are trying to use a variable from a quotation lambda in real F# code. Quotations aren't real F# code, and for that reason you also cannot evaluate them (except with the LINQ evaluator, which isn't perfect). – Ramon Snir Jun 20 '11 at 16:16

1 Answers1

6

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:

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#). It just generates expression like (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. 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.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • You seem like a competent guy :) But this is not entirely what I want. I may I've failed to be clear enough in my question, so there will be an update of the question in 2 secs. – Lasse Espeholt Jun 20 '11 at 16:36
  • @lasseespeholt: I changed the version to use `new Var` instead of `Var.Global` (which was just wrong). This should now declare a new variable in every iteration (they'll have the same _name_, but will be _different_ variables). – Tomas Petricek Jun 20 '11 at 16:45
  • @Tomas Thanks :) works as expected. However, I guess I'll fallback to MetaOcaml then because I wanted to use it for multi staged programming. – Lasse Espeholt Jun 20 '11 at 16:49
  • 2
    @lasseespeholt - MetaOCaml is definitely better for that purpose. BTW: I'd be quite interested to know what is your scenario (just to better understand practical uses of multi-stage programming). – Tomas Petricek Jun 20 '11 at 16:52
  • 1
    @Tomas I've looked at a couple of areas because I want to find a project for the summer. Multi-stage-programming seems interesting because one of the goals is to raise abstraction without a high performance penalty. In many newer languages we have abstractions you eventually have to break down to yield the appropiate performance in a high performance application. (Again, I know - based on another SO question - that F# PowerPack Eval doesn't perform well) – Lasse Espeholt Jun 20 '11 at 20:35
  • So far F# have managed to compile the more interesting examples of the given paper :) – Lasse Espeholt Jun 20 '11 at 20:48
  • @lasseepeholt - It is definitely an interesting technology. I would expect that usual implementations of multi-stage programming do not store complete expression trees, but use some partly-compiled representation (so programs don't need to come with complete compiler). To some point, it feels like a very sophisticated version of `inline` annotations though :-) – Tomas Petricek Jun 20 '11 at 23:36