Scalaの抽象構文木(abstract syntax tree、AST)をグラフィカルに表示するオプション
ということで、ほとんどの言語はプログラムの内部表現として抽象構文木(abstract syntax tree、AST)を作ると思います。Scalaももちろんコンパイルするときに作ります。
で、Scalaコンパイラが生成してるASTを見るオプションがあります。
"-Xprint:<phase>"オプションと"-Ybrowse:<phase>"オプションです。
こんなコードがあったとして、
case class Cell[T](v:T) { // TがInt型の場合にのみ呼び出せる def increment(implicit ev:T =:= Int ):Cell[Int] = Cell( v + 1 ) import java.text.SimpleDateFormat import java.util.Date // TがDateまたはそのサブタイプのときに呼び出せる def formatDate(implicit ev: T <:< java.util.Date ) = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(v) import scala.collection.immutable.WrappedString // Tがimplicit conversion等でWrappedStringと見なせる場合に呼び出せる // toIntはWrappedStringが持つメソッドだが、コンパイルは通る def asInt(implicit ev: T <%< WrappedString) = v.toInt } object Main { def main(args:Array[String]) = { println( Cell(99).increment ) println( Cell( new java.util.Date).formatDate ) println( Cell( new java.sql.Timestamp( System.currentTimeMillis)).formatDate ) println( Cell("123").asInt ) } }
こいつを"scalac -Xprint:typer Cell.scala"と-Xprintオプションをつけてコンパイルしてみると、このようにコンパイラがどんな変換をしているのかがわかるというわけです。
ozaki@yuroyoro-MacBook $ scalac -Xprint:typer Cell.scala [~/sandbox/.../work/implicit] Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8 [[syntax trees at end of typer]]// Scala source: Cell.scala package <empty> { @serializable case class Cell[T >: Nothing <: Any] extends java.lang.Object with ScalaObject with Product { <synthetic> def copy$default$1[T >: Nothing <: Any]: T @scala.annotation.unchecked.uncheckedVariance = Cell.this.v; <caseaccessor> <paramaccessor> private[this] val v: T = _; <stable> <caseaccessor> <accessor> <paramaccessor> def v: T = Cell.this.v; def this(v: T): Cell[T] = { Cell.super.this(); () }; def increment(implicit ev: =:=[T,Int]): Cell[Int] = Cell.apply[Int](ev.apply(Cell.this.v).+(1)); import java.text.SimpleDateFormat; import java.util.Date; def formatDate(implicit ev: <:<[T,java.util.Date]): java.lang.String = new java.text.SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Cell.this.v); import scala.collection.immutable.WrappedString; def asInt(implicit ev: <%<[T,scala.collection.immutable.WrappedString]): Int = ev.apply(Cell.this.v).toInt; <synthetic> def copy[T >: Nothing <: Any](v: T = v): Cell[T] = new Cell[T](v); override def hashCode(): Int = ScalaRunTime.this._hashCode(Cell.this); override def toString(): String = ScalaRunTime.this._toString(Cell.this); override def equals(x$1: Any): Boolean = Cell.this.eq(x$1).||(x$1 match { case (v: Any)Cell[Any]((v$1 @ _)) if v$1.==(v) => x$1.asInstanceOf[Cell[T]].canEqual(Cell.this) case _ => false }); override def productPrefix: java.lang.String = "Cell"; override def productArity: Int = 1; override def productElement(x$1: Int): Any = x$1 match { case 0 => v case _ => throw new java.lang.IndexOutOfBoundsException(x$1.toString()) }; override def canEqual(x$1: Any): Boolean = x$1.$isInstanceOf[Cell[T]]() }; final object Main extends java.lang.Object with ScalaObject { def this(): object Main = { Main.super.this(); () }; def main(args: Array[String]): Unit = { scala.this.Predef.println(Cell.apply[Int](99).increment(scala.this.Predef.=:=.tpEquals[Int])); scala.this.Predef.println(Cell.apply[java.util.Date](new java.util.Date()).formatDate(scala.this.Predef.conforms[java.util.Date])); scala.this.Predef.println(Cell.apply[java.sql.Timestamp](new java.sql.Timestamp(java.this.lang.System.currentTimeMillis())).formatDate(scala.this.Predef.conforms[java.sql.Timestamp])); scala.this.Predef.println(Cell.apply[java.lang.String]("123").asInt(scala.this.Predef.<%<.conformsOrViewsAs[java.lang.String, scala.collection.immutable.WrappedString]({ ((s: String) => scala.this.Predef.wrapString(s)) }))) } }; final <synthetic> object Cell extends java.lang.Object with ScalaObject { def this(): object Cell = { Cell.super.this(); () }; case <synthetic> def unapply[T >: Nothing <: Any](x$0: Cell[T]): Option[T] = if (x$0.==(null)) scala.this.None else scala.Some.apply[T](x$0.v); case <synthetic> def apply[T >: Nothing <: Any](v: T): Cell[T] = new Cell[T](v) } }
"-Ybrowse"オプションだと、ASTをGUIで見ることができます。"scalac -Ybrowse:typer Cell.scala "とすると、
オプションで指定する<phase>は、"scalac -Xshow-phases"で見ることができます。
scalac -Xshow-phases parser namer packageobjects typer superaccessors pickler refchecks selectiveanf liftcode selectivecps uncurry tailcalls specialize explicitouter erasure lazyvals lambdalift constructors flatten mixin cleanup icode inliner closelim dce jvm terminal
typerは、型付けが終わった段階で、たいていはこのくらいで充分でしょう。implicit conversionや型推論がどう解決されているか見るのに便利ですね。
scalaのコンパイラについてはこの死霊に詳しい解説があります。
http://www.sts.tu-harburg.de/people/mi.garcia/ScalaCompilerCorner/