What’s the easiest way to use reify (get an AST of) an expression in Scala?

A lot of things previously defined in package scala.reflect.mirror have moved to scala.reflect.runtime.universe:

scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}

scala> val expr = u reify { 1 to 3 map (_+1) }
expr: reflect.runtime.universe.Expr[scala.collection.immutable.IndexedSeq[Int]] = Expr[scala.collection.immutable.IndexedSeq[Int]](scala.this.Predef.intWrapper(1).to(3).map(((x$1) => x$1.$plus(1)))(immutable.this.IndexedSeq.canBuildFrom))

scala> u show expr.tree
res57: String = scala.this.Predef.intWrapper(1).to(3).map(((x$1) => x$1.$plus(1)))(immutable.this.IndexedSeq.canBuildFrom)

scala> u showRaw expr.tree
res58: String = Apply(Apply(Select(Apply(Select(Apply(Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("intWrapper")), List(Literal(Constant(1)))), newTermName("to")), List(Literal(Constant(3)))), newTermName("map")), List(Function(List(ValDef(Modifiers(<param> <synthetic>), newTermName("x$1"), TypeTree(), EmptyTree)), Apply(Select(Ident(newTermName("x$1")), newTermName("$plus")), List(Literal(Constant(1))))))), List(Select(Select(This(newTypeName("immutable")), newTermName("IndexedSeq")), newTermName("canBuildFrom"))))

Furthermore it is possible to check if a string containing some Scala code is a valid Scala expression and – even better – do some evaluation:

Edit. In 2.10.0-RC1 some methods of ToolBox have been renamed. parseExpr is now just parse, and runExpr is now called eval.

scala> import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBox

scala> import scala.reflect.runtime.{currentMirror => m}
import scala.reflect.runtime.{currentMirror=>m}

scala> val tb = m.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@9293709

scala> val tree = tb.parse("1 to 3 map (_+1)")
tree: tb.u.Tree = 1.to(3).map(((x$1) => x$1.$plus(1)))

scala> val eval = tb.eval(tree)
eval: Any = Vector(2, 3, 4)

The most complicated thing here is the raw tree representation of an expression. When one wants to use macros, the macros have to be defined the same way as shown by showRaw. But with some helper methods it is possible to define some not so ugly looking macro implementations:

object IntMacro {

  import language.experimental.macros
  import scala.reflect.makro.Context
  import scala.reflect.NameTransformer.encode

  def isEven(i: Int): Boolean = macro isEvenImpl

  def isEvenImpl(c: Context)(i: c.Expr[Int]): c.Expr[Boolean] = {
    import c.universe._
    implicit val cc: c.type = c

    val `x = i%2` = Apply(Select(i.tree, op("%")), const(2))
    val `x == 0` = Apply(Select(`x = i%2`, op("==")), const(0))

    c.Expr(`x == 0`)
  }

  def op(s: String)(implicit c: Context): c.universe.TermName =
    c.universe.newTermName(encode(s))

  def const(a: Any)(implicit c: Context): List[c.universe.Literal] =
    List(c.universe.Literal(c.universe.Constant(a)))
}

scala> import IntMacro._
import IntMacro._

scala> isEven(2)
res60: Boolean = true

scala> isEven(3)
res61: Boolean = false

But now we come in problems with path-dependent-types – we have to write their paths explicitly if we want not import them.

Leave a Comment