Automatically initialize an object in Scala

If you run this simple code, you will see the following:

object A {
  println("from A")
  var x = 0
}

object B {
  println("from B")
  A.x = 1
}

object Test extends App {
  println(A.x)
}

// Result:
// from A
// 0

      

As you can guess, scala initializes objects lazily. Object B is not initialized and does not work as expected. My question here is what tricks can I use to initialize object B without accessing it? The first trick I can use is to extend an object with some trait and use reflection to initialize the object that extends a specific trait. I think a more elegant way is to annotate an object with a macro:

@init
object B {
  println("from B")
  A.x = 1
}

class init extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro init.impl
}

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

    // what should i do here ?
  }
}

      

But I was a little confused. How do I call methods (for initialization) from an annotated object in a macro method impl

?

+3


source to share


1 answer


I found a solution:

Application:

object A {
  println("from A")
  var x = 0
}

@init
object B {
  println("from B")
  A.x = 1
}

@init
object C {
  println("from C")
  A.x = 2
}

object Test extends App {
  init()
  println(A.x)
}

      

Output:



from B
from A
from C
2

      

Macro implementation:

class init extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro init.impl
}

object init {
  private val objFullNames = new mutable.MutableList[String]()

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

    annottees.map(_.tree).toList match {
      case (obj: ModuleDef) :: Nil =>
        objFullNames += c.typecheck(obj).symbol.fullName
      case _ =>
        c.error(c.enclosingPosition, "@init annotation supports only objects")
    }

    annottees.head
  }

  def apply() = macro runImpl

  def runImpl(c: whitebox.Context)(): c.Expr[Any] = {
    import c.universe._
    val expr = objFullNames.map(name => q"${c.parse(name)}.##").toList
    val res = q"{..$expr}"
    c.Expr(res)
  }
}

      

+2


source







All Articles