Answers http://stackoverflow.com/questions/10373318/mixing-in-a-trait-dynamically.
Compile as follows: scalac Common_1.scala Macros_2.scala scalac Common_1.scala Test_3.scala -cp
Tested in 2.10.0
===Common_1.scala===
trait Persisted {
def id: Long
}
===Macros_2.scala===
import language.experimental.macros
import scala.reflect.macros.Context
object Macros {
def toPersisted
[ T ]
( instance : T, id : Long )
: T with Persisted
= macro toPersistedMacro[T]
def toPersistedMacro
[ T : c.WeakTypeTag ]
( c : Context )
( instance : c.Expr[T],
id : c.Expr[Long] )
: c.Expr[T with Persisted]
= {
import c.universe._
val t = weakTypeOf[T]
val s = t.typeSymbol.asClass
if (!s.isCaseClass)
c.abort(c.enclosingPosition, "toPersisted only accepts case classes, you provided %s".format(t))
val accessors = t.members.sorted.collect{ case x : TermSymbol if x.isCaseAccessor && x.isMethod => x }
val fieldNames = accessors.map(_.name)
// some of the missing parts of the reflection API
// we should be able to come up with something better in future point releases
val PARAMACCESSOR = (1L << 29).asInstanceOf[FlagSet]
val instanceParam = ValDef(NoMods, newTermName("instance"), TypeTree(s.toType), EmptyTree)
val idParam = ValDef(Modifiers(PARAMACCESSOR), newTermName("id"), Ident(definitions.LongClass), EmptyTree)
val superArgs = fieldNames.map{ fieldName => Select(Ident(instanceParam.name), fieldName) }
val body = Apply(Select(Super(This(newTypeName("")), newTypeName("")), nme.CONSTRUCTOR), superArgs)
val constructor = DefDef(NoMods, nme.CONSTRUCTOR, Nil, List(List(instanceParam, idParam)), TypeTree(), Block(List(body), Literal(Constant(()))))
val idVal = idParam.duplicate
val tmpl = Template(List(Ident(s), Ident(c.mirror.staticClass("Persisted"))), emptyValDef, List(idVal, constructor))
val classDef = ClassDef(NoMods, newTypeName(c.fresh(s.name + "$Persisted")), Nil, tmpl)
val init = New(Ident(classDef.name), List(List(instance.tree, id.tree)))
c.Expr[T with Persisted](Block(classDef, init))
}
}
===Test_3.scala===
object Test extends App {
import Macros._
case class Person(first: String, last: String)
val p = toPersisted(Person("hello", "world"), 42)
println(p.first)
println(p.last)
println(p.id)
}
===Output===
C:\Projects\Kepler\sandbox>scalac Common_1.scala Macros_2.scala
<scalac has exited with code 0>
C:\Projects\Kepler\sandbox>scalac -Ymacro-debug-lite Common_1.scala Test_3.scala -cp .
typechecking macro expansion Macros.toPersisted[Test.Person](Test.this.Person.apply("hello", "world"), 42L)
at source-C:\Projects\Kepler\sandbox\Test_3.scala,line-4,offset=114
{
class Person$Persisted1 extends Person with Persisted {
val id: Long = _;
def <init>(instance: Test.Person, id: Long) = {
super.<init>(instance.first, instance.last);
()
}
};
new Person$Persisted1(Test.this.Person.apply("hello", "world"), 42L)
}
*snip*
<scalac has exited with code 0>
C:\Projects\Kepler\sandbox>scala Test
hello
world
42