コンパイラがScalaで何をしているのかを理解する方法として、-print
またはjavap
の代替案を検討しています。新しいリフレクション/マクロライブラリでは、レトロニムの macrocosm のreify
に示されているように、desugar
がその候補として適しているようです。それは、M4以前のそれをどのように行っていたかを示しています。
だから問題は、ScalaのREPLで、式のASTを取得するために、Scala 2.10.0以降)に入力できる最短/最も簡単なものは何ですか? M4?
以前にパッケージscala.reflect.mirror
で定義されていた多くのものが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"))))
さらに、いくつかのScalaコードを含む文字列が有効なScala式であるかどうかを確認し、さらに良いことに、いくつかの評価を行うことができます。
編集。 2.10.0-RC1では、ToolBox
のいくつかのメソッドの名前が変更されました。 parseExpr
はparse
になり、runExpr
は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)
ここで最も複雑なのは、式の生のツリー表現です。マクロを使用する場合、マクロはshowRaw
で示されるのと同じ方法で定義する必要があります。しかし、いくつかのヘルパーメソッドを使用すると、それほど見苦しくないマクロ実装を定義することができます。
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
しかし、パス依存型で問題が発生しました。インポートしない場合は、パスを明示的に記述する必要があります。
新しいScalaバージョンの改善があります
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)