macros: how to read an annotation object

304 views
Skip to first unread message

Jaap Taal

unread,
Jan 4, 2014, 2:26:18 PM1/4/14
to scala-l...@googlegroups.com
I'm trying to get the annotation object. The annotation.scalaArgs is not very useful in some cases. I'd rather just get the annotation object.

My annotation looks like:

class extract(val name: String) extends StaticAnnotation

And I'm using it like this:

case class MainClass(@extract("strings") foo: String, bar: Int)

I'm trying to get the foo parameter Symbol because it has an @extract annotation (I also want to do something with the extract value):

val extrList = params.map { param: Symbol =>
  param.annotations.collect {
    case extr if extr.tpe  <:< c.weakTypeOf[extract] =>
      val args = extr.scalaArgs
      if (args.size != 1)
        abort("@extract() should have exactly 1 parameter")
      getExtractValue(args.head) -> param
  }
}

The signature of the getExtractValue method looks like this:

def getExtractValue(tree: Tree): String = ???

How do I get the value the @extract annotation object


furthermore, I'd like the user of my macro to be able to write this:

class commonlyUsed extends extract("commonName")
case class MainClass(@commonlyUsed bar: String, baz: Int)

This way the macro can be generic to just support extract, but the API can be customized.


I've also posted my original question on stackoverflow (there I ask how to get the name):

http://stackoverflow.com/questions/20908671/scala-macros-how-to-read-an-annotations-parameter

Jaap Taal

unread,
Jan 4, 2014, 2:31:20 PM1/4/14
to scala-l...@googlegroups.com
I've published my code to:


The code at that point doesn't work (the macro expansion throws an exception)

Eugene Burmako

unread,
Jan 4, 2014, 3:40:54 PM1/4/14
to scala-l...@googlegroups.com
Unfortunately, this is something that's currently very hard to do robustly. 

As you've already seen, c.eval doesn't work for trees that are already typechecked, and arguments of annotations are typechecked. Therefore, I would advise to write a mini-interpreter for typed trees that supports the shapes of expressions that you would care to put in the annotations. For instance, as suggested at StackOverflow, you could pattern-match on Literal(Constant(value)) trees and just extract values. If absolutely needed, you could even hardcode and support common functions like append, substring, etc.

WDYT?

Jaap Taal

unread,
Jan 5, 2014, 4:54:36 AM1/5/14
to scala-l...@googlegroups.com
Hi Eugene,

In general I think you matching proposal can work very well, because it would be very easy to give a useful compiler error when someone would use the attribute in a way the macro doesn't support.
I see now why the scala compiler can only give you a Tree during macro expansions: the annotation class might not be compiled itself (in my case, it is already compiled, but the Annotation type reflect that).


What I'm going to do is this: I'm going to drop trying to parse the parameter and use another piece of informatie that will get the job done.

I will put a marker trait in the macro library:
trait ExtractAnnotation extends StaticAnnotation
The macro can check for annotations that implement this trait and do whatever is necessary

The user of the macro should define an annotation per unique indentifier needed:

class strings extends ExtractAnnotation
where strings would the identifier, or
class assetIds extends ExtractAnnotation
where assetIds would be the identifier

Instead of applying @extract("assetIds") the user can apply @assetIds to a case class parameter. This is nicer in a lot of ways.  I always get a valid identifier (which previously I had to check manually).
I haven't tried yet, but I think getting the name of the class and use that as a string in the macro should be very easy.

Thank you for your response!

Jaap

Jaap Taal

unread,
Jan 5, 2014, 4:57:02 AM1/5/14
to scala-l...@googlegroups.com
I meant to write

in my case, it is already compiled, but the Annotation type cannot reflect that
Reply all
Reply to author
Forward
0 new messages