Mixing in a trait dynamically

Nikita Volkov picture Nikita Volkov · Apr 29, 2012 · Viewed 11k times · Source

Having a trait

trait Persisted {
  def id: Long
}

how do I implement a method that accepts an instance of any case class and returns its copy with the trait mixed in?

The signature of the method looks like:

def toPersisted[T](instance: T, id: Long): T with Persisted

Answer

Eugene Burmako picture Eugene Burmako · Apr 30, 2012

This can be done with macros (that are officially a part of Scala since 2.10.0-M3). Here's a gist example of what you are looking for.

1) My macro generates a local class that inherits from the provided case class and Persisted, much like new T with Persisted would do. Then it caches its argument (to prevent multiple evaluations) and creates an instance of the created class.

2) How did I know what trees to generate? I have a simple app, parse.exe that prints the AST that results from parsing input code. So I just invoked parse class Person$Persisted1(first: String, last: String) extends Person(first, last) with Persisted, noted the output and reproduced it in my macro. parse.exe is a wrapper for scalac -Xprint:parser -Yshow-trees -Ystop-after:parser. There are different ways to explore ASTs, read more in "Metaprogramming in Scala 2.10".

3) Macro expansions can be sanity-checked if you provide -Ymacro-debug-lite as an argument to scalac. In that case all expansions will be printed out, and you'll be able to detect codegen errors faster.

edit. Updated the example for 2.10.0-M7