How to use Type calculated in Scala Macro in a reify clause?
scala, scala-2.10, scala-macros
Solution
The problem is that the code you pass to `reify` is essentially going to be placed verbatim at the point where the macro is being expanded, and `fieldMemberType` isn't going to mean anything there.
In some cases you can use `splice` to 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:
trait Foo { def i: Int }
And had this variable at macro-expansion time:
val myInt = 10
We could write the following:
reify { new Foo { def i = c.literal(myInt).splice } }
That's not going to work here, which means you're going to have to forget about nice little `reify` and 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:
import scala.reflect.runtime.universe._
trait TypeBuilder { type fieldType }
showRaw(reify(new TypeBuilder { type fieldType = String }))
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:
Ident(TypeBuilder)
With this:
Ident(newTypeName("TypeBuilder"))
And `FINAL` with `Flag.FINAL`, and so on. I wish the `toString` methods 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:
c.Expr(
Block(
ClassDef(
Modifiers(Flag.FINAL),
anon,
Nil,
Template(
Ident(newTypeName("TypeBuilder")) :: Nil,
emptyValDef,
List(
constructor(c),
TypeDef(
Modifiers(),
newTypeName("fieldType"),
Nil,
TypeTree(fieldMemberType)
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
)
)
Where `anon` is a type name you've created in advance for your anonymous class, and `constructor` is 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:
scala> TypeMemberExample.builderWithType[String]
res0: TypeBuilder{type fieldType = String} = $1$$1@fb3f1f3
So it works. We've taken a `c.universe.Type` (which I get here from the `WeakTypeTag` of the type parameter on `builderWithType`, but it will work in exactly the same way with any old `Type`) and used it to define the type member of our `TypeBuilder` trait.
Problem
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?