I’ve been working with Scala Macros and have the following code in the macro:
val fieldMemberType = fieldMember.typeSignatureIn(objectType) match {
case NullaryMethodType(tpe) => tpe
case _ => doesntCompile(s"$propertyName isn't a field, it must be another thing")
}
reify{
new TypeBuilder() {
type fieldType = fieldMemberType.type
}
}
As you can see, I’ve managed to get a c.universe.Type fieldMemberType. This represents the type of certain field in the object. Once I get that, I want to create a new TypeBuilder object in the reify. TypeBuilder is an abstract class with an abstract parameter. This abstract parameter is fieldType. I want this fieldType to be the type that I’ve found before.
Running the code shown here returns me a fieldMemberType not found. Is there any way that I can get the fieldMemberType to work inside the reify clause?
The problem is that the code you pass to
reifyis essentially going to be placed verbatim at the point where the macro is being expanded, andfieldMemberTypeisn’t going to mean anything there.In some cases you can use
spliceto sneak an expression that you have at macro-expansion time into the code you’re reifying. For example, if we were trying to create an instance of this trait:And had this variable at macro-expansion time:
We could write the following:
That’s not going to work here, which means you’re going to have to forget about nice little
reifyand write out the AST by hand. You’ll find this happens a lot, unfortunately. My standard approach is to start a new REPL and type something like this:This will spit out several lines of AST, which you can then cut and paste into your macro definition as a starting point. Then you fiddle with it, replacing things like this:
With this:
And
FINALwithFlag.FINAL, and so on. I wish thetoStringmethods for the AST types corresponded more exactly to the code it takes to build them, but you’ll pretty quickly get a sense of what you need to change. You’ll end up with something like this:Where
anonis a type name you’ve created in advance for your anonymous class, andconstructoris a convenience method I use to make this kind of thing a little less hideous (you can find its definition at the end of this complete working example).Now if we wrap this expression up in something like this, we can write the following:
So it works. We’ve taken a
c.universe.Type(which I get here from theWeakTypeTagof the type parameter onbuilderWithType, but it will work in exactly the same way with any oldType) and used it to define the type member of ourTypeBuildertrait.