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

前端 未结 2 843
半阙折子戏
半阙折子戏 2020-11-29 17:58

I\'m looking at alternatives to -print or javap as a way of figuring out what the compiler is doing in Scala. With the new reflection/macros librar

相关标签:
2条回答
  • 2020-11-29 18:10

    There is improve of new Scala version

    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@78dc5f15
    
    scala> val tree = tb.parseExpr("1 to 3 map (_+1)")
    <console>:10: error: value parseExpr is not a member of scala.tools.reflect.ToolBox[reflect.runtime.universe.type]
           val tree = tb.parseExpr("1 to 3 map (_+1)")
                         ^
    
    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)
    
    0 讨论(0)
  • 2020-11-29 18:30

    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.

    0 讨论(0)
提交回复
热议问题