How to access parent class params literals?

Viewed 58

Using Scala 2.x macros, how to access literals passed to parent classes when defining subclasses, typically with ADTs such as the following:

sealed abstract class Base(val s: String, val i: Int)
object Base {
  case object A extends Base("a", 1)
  case object B extends Base("b", 2)
  case class C(override val s: String) extends Base(s, 3)
}

For object A, I would like to know parameter values are literals "a" and 1.
For object B, I would like to know parameter values are literals "b" and 2.
For case class C, I would like to know the second parameter is literal 3.

How can I accomplish this with Scala 2.x macros?

1 Answers

After much investigation and getting inspiration from bwmcadams / supreme-macro-adventure, it looks like one solution is based on the combination of Macro Paradise and the creation of an @ADT annotation.

Macro paradise provides a mechanism to expand annotations on ADTs to rewrite them, thanks to the macroTransform function.

import scala.annotation.{compileTimeOnly, StaticAnnotation}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context

@compileTimeOnly("enable macro paradise to expand macro annotations")
class ADT extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro ADT.impl
}

object ADT {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    val trees = annottees.map(_.tree)
    val rewritten = trees match {
      case (cd: ClassDef) :: q"object $name { ..$body }" :: Nil =>
        $body match {
          case q"case object $name extends $_($literal, $_)" :: tail =>
            [...]
        }
      case _ =>
        trees
    }

    c.Expr[Any](q"{..$rewritten}")
  }
}

With the help of quasiquotes, one can navigate $body and pattern match the different cases to access the arguments passed to the parent class (here $literal). Finally, one returns the rewritten ADT.

Related