5

I'm experimenting with the scala 2.10 macro features. I have trouble using LabelDef in some cases, though. To some extent I peeked in the compiler's code, read excerpts of Miguel Garcia's papers but I'm still stuck.

If my understanding is correct, a pseudo-definition would be:
LabelDef(labelName, listOfParameters, stmsAndApply) where the 3 arguments are Trees and:
- labelNameis the identifier of the label $L being defined
- listOfParameters correspond to the arguments passed when label-apply occurs, as in $L(a1,...,an), and can be empty
- stmsAndApplycorresponds to the block of statements (possibly none) and final apply-expression
label-apply meaning more-or-less a GOTO to a label

For instance, in the case of a simple loop, a LabelDef can eventually apply itself:
LabelDef($L, (), {...; $L()})

Now, if I want to define 2 LabelDef that jump to each other:

...
LabelDef($L1, (), $L2())
...
LabelDef($L2, (), $L1())
...

The 2nd LabelDef is fine, but the compiler outputs an error on the 1st, "not found: value $L2". I guess that is because $L2 isn't yet defined while there is an attempt to apply it. This is a tree being constructed so that would make sense to me. Is my understanding correct so far? Because if no error is expected, that means my macro implementation is probably buggy.

Anyway, I believe there must be a way to apply $L2 (i.e. Jumping to $L2) from $L1, somehow, but I just have no clue how to do it. Does someone have an example of doing that, or any pointer?


Other unclear points (but less of a concern right now) about using LabelDef in macros are:
-what the 2nd argument is, concretely, how is it used when non-empty? In other words, what are the mechanisms of a label-apply with parameters?
-is it valid to put in the 3rd argument's final expression anything else than a label-apply? (not that I can't try, but macros are still experimental)
-is it possible to perform a forwarding label-apply outside a LabelDef? (maybe this is a redundant question)

Any macro implementation example in the answer is, of course, very welcome!
Cheers,

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
eruve
  • 661
  • 5
  • 12
  • It might be a good idea to ask this at http://groups.google.com/group/scala-internals – Eugene Burmako Jul 26 '12 at 21:49
  • @Eugene I eventually had to read the related *scala-internal* threads. However it appears that dropping `LabelDef` from Scala 2.11 has already been decided (and implemented). While I understand the motivations from an internal standpoint, DSLs with `GOTO`-ish features based on `LabelDef` would be impacted, I believe (e.g. instead of a JVM `goto` instruction, a bunch of workaround instructions). Do you have any input on that? – eruve Dec 01 '12 at 04:39
  • It's definitely not implemented yet, since LabelDefs are still around in the 2.11 branch: https://github.com/scala/scala/blob/master/src/reflect/scala/reflect/internal/Trees.scala. I've asked a question at http://groups.google.com/group/scala-internals/browse_thread/thread/50c8643af1fe0973 - please, join and share your concerns. – Eugene Burmako Dec 02 '12 at 11:18
  • @Eugene Cheers. I thought a contributor already implemented the changes in his private clone repository, I must have misread. My Scala skill-set doesn't include the internals, hence my hesitation in asking at that discussion group. But I'll take your advice and will do. So far I had just asked from the compiler end-user's (i.e. programmer's) perspective, since the macros API is meant to be publicly available (although experimental). – eruve Dec 04 '12 at 05:21

1 Answers1

1

Because if no error is expected, that means my macro implementation is probably buggy.
Yes, it seems that was a bug (^^; Although I'm not sure whether or not the limitation with the Block/LabelDef combination exists on purpose.

def EVIL_LABELS_MACRO = macro EVIL_LABELS_MACRO_impl
def EVIL_LABELS_MACRO_impl(c:Context):c.Expr[Unit] = { // fails to expand
  import c.universe._
  val lt1 = newTermName("$L1"); val lt2 = newTermName("$L2")
  val ld1 = LabelDef(lt1, Nil, Block(c.reify{println("$L1")}.tree, Apply(Ident(lt2), Nil)))
  val ld2 = LabelDef(lt2, Nil, Block(c.reify{println("$L2")}.tree, Apply(Ident(lt1), Nil)))
  c.Expr( Block(ld1, c.reify{println("ignored")}.tree, ld2) )
}

def FINE_LABELS_MACRO = macro FINE_LABELS_MACRO_impl
def FINE_LABELS_MACRO_impl(c:Context):c.Expr[Unit] = { // The End isn't near
  import c.universe._
  val lt1 = newTermName("$L1"); val lt2 = newTermName("$L2")
  val ld1 = LabelDef(lt1, Nil, Block(c.reify{println("$L1")}.tree, Apply(Ident(lt2), Nil)))
  val ld2 = LabelDef(lt2, Nil, Block(c.reify{println("$L2")}.tree, Apply(Ident(lt1), Nil)))
  c.Expr( Block(ld1, c.reify{println("ignored")}.tree, ld2, c.reify{println("The End")}.tree) )
}

I think a Block is parsed into { statements; expression } thus the last argument is the expression. If a LabelDef "falls in" expression, e.g. the EVIL_LABELS_MACRO pattern, its expansion won't be visible in statements; hence error "not found: value $L2".

So it's better to make sure all LabelDef "fall in" statements. FINE_LABELS_MACRO does that and expands to:

{
  $L1(){
    scala.this.Predef.println("$L1");
    $L2()
  };
  scala.this.Predef.println("ignored");
  $L2(){
    scala.this.Predef.println("$L2");
    $L1()
  };
  scala.this.Predef.println("The End")
}
eruve
  • 661
  • 5
  • 12
  • Can it end with an `EmptyTree`? – Daniel C. Sobral Jul 31 '12 at 19:27
  • I think `EmptyTree` has no type attached, _even not_ a `scala.Nothing`, thus is not applicable to _expression_. In my example the end is never reached, but the `c.Expr`'s `Block` could end with a `Literal(Constant(()))` instead, since the macro must yield a `Unit`. – eruve Aug 01 '12 at 06:19
  • @daniel: if you meant `LabelDef`, I _believe_ the 3rd argument's tree as well should yield an _expression_, so no `EmptyTree`. I don't know if that will remain _valid_ in the future but the 3rd arg is not necessarily a _label-apply_ apparently. – eruve Aug 01 '12 at 07:39