views:

168

answers:

1
[<ReflectedDefinition>]
let rec x = (fun() -> x + "abc") ()

The sample code with the recursive value above produces the following F# compiler error:

error FS0432: [<ReflectedDefinition>] terms cannot contain uses of the prefix splice operator '%'

I can't see any slicing operator usage in the code above, looks like a bug... :)

Looks like this is the problem with the quotation via ReflectedDefinitionAttribute only, normal quotation works well:

let quotation =
    <@ let rec x = (fun() -> x + "abc") () in x @>

produces expected result with the hidden Lazy.create and Lazy.force usages:

val quotation : Quotations.Expr<string> =
   LetRecursive
   ([(x, Lambda (unitVar,
        Application
        (Lambda (unitVar0,
            Call (None,
            String op_Addition[String,String,String](String, String),
            [Call (None,
                String Force[String](Lazy`1[System.String]), // `
                [x]), Value ("abc")])),
        Value (<null>)))),
   (x, Call (None, Lazy`1[String] Create[String](FSharpFunc`2[Unit,String]), [x])),
   (x, Call (None, String Force[String](Lazy`1[String]), [x]))], x) // `

So the question is: is this an F# compiler bug or not?

+4  A: 

I'd think that this may be caused by the treatment of recursive values in F#. As a workaround, you can turn the recursive reference into a parameter:

[<ReflectedDefinition>] 
let foo x = (fun() -> x + "abc") ()

// To construct the recursive value, you'd write:
let rec x = foo x

The last line is of course invalid (just like your original code), because you're creating an immediate recursive reference, but it should give you the idea - in reality, you'd probably enclose x in a lambda function.


EDIT Originally, I thought that the problem may be as below, but I'm not sure now (see comments).

It looks more like a (probably known) limitation to me than an unexpected bug. There is an important difference between the two versions of the code you wrote - in the first case, you're binding a public value (visible to .NET) named x while in the second case, x is just a symbol used only in the quotation.

The quotation that would have to be stored in the meta-data of the assembly would look like this:

let rec x = <@ (fun() -> %x + "abc") () @>

The body is quoted, but x is not a quoted symbol, so it needs to be spliced into the quotation (that is, it will be evaluated and the result will be used in its place). Note that this code will fail, because you're declaring a recursive value with immediate reference - x needs to be evaluated as part of its definition, so this won't work.

However, I think that % cannot appear in ReflectedDefinition quotations (that is, you cannot store the above in meta-data), because it involves some runtime aspects - you'd need to evaluate x when loading the meta-data.

Tomas Petricek
Thank you for your answer, Tomas!I'm don't agree with you that when I'm writing the `[<ReflectedDefinition>]` version that metadata should looks like `<@ (fun() -> %x + "abc") () @>`. Inside quotation the `x` name should be bound to the public .NET value, not the quotation slice! If it would behavior like as you say, the code like this: `[<ReflectedDefinition>] let rec f () = (fun() -> f () + "abc") ()` should produce the same expecption too, but it doesn't.
ControlFlow
@ControlFlow: I think your point makes sense. Maybe this has to do something with recursive values which are generally quite complicated thing in F#.
Tomas Petricek
@Tomas Petricek: I think so too, handling recursive values is non trivial task for eager languages like F#... Maybe compiler should simply restrict [<ReflectedDefinition>] usages like this.
ControlFlow