public
Last active

Mixing in a trait dynamically

  • Download Gist
gistfile1.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
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 <path to the result of the previous compilation>
 
Tested in 2.10.0-M3, will most likely not compile by the time 2.10.0 final is released, because we're actively rehashing the API.
However the principles will remain the same in the final release, so the concept itself is okay.
upd. Code updated for 2.10.0-M7.
upd. Code updated for 2.10.0-RC1.
 
===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 impl[T]
 
def impl[T: c.AbsTypeTag](c: Context)(instance: c.Expr[T], id: c.Expr[Long]) = {
import c.universe._
import Flag._
 
val t = implicitly[c.AbsTypeTag[T]].tpe // in RC1 this will become c.absTypeOf[T]
val u = t.typeSymbol.asClass
if (!u.isCaseClass)
c.abort(c.enclosingPosition, "toPersisted only accepts case classes, you provided %s".format(t))
 
// use reflection API to get the list of all declared fields
// more info here: http://scalamacros.org/documentation.html
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]
 
// how did I know what trees to generate?
// read up the docs at http://scalamacros.org/documentation.html
val instanceParam = ValDef(NoMods, newTermName("instance"), TypeTree(u.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 ctor = DefDef(NoMods, nme.CONSTRUCTOR, Nil, List(List(instanceParam, idParam)), TypeTree(), Block(List(body), Literal(Constant(()))))
val idVal = idParam.duplicate
val tmpl = Template(List(Ident(u), Ident(c.mirror.staticClass("Persisted"))), emptyValDef, List(idVal, ctor))
val cdef = ClassDef(NoMods, newTypeName(c.fresh(u.name + "$Persisted")), Nil, tmpl)
 
val init = New(Ident(cdef.name), List(List(instance.tree, id.tree)))
c.Expr(Block(cdef, 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

I followed all steps and double check it but still the Test outputs 0 as id, the debug shows the same output as the one provided here. Any thoughts?
scala 2.10.0-M7

Is this code still valid? I get this error:
type AbsTypeTag is not a member of
scala.reflect.macros.Context

"In 2.10.0-RC1 AbsTypeTag has been renamed to WeakTypeTag. Everything else about macros and type tags remains the same."

From: http://stackoverflow.com/questions/12018117/is-it-possible-to-write-a-scala-macro-whose-returntype-depends-on-argument

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.