In this article we present several use cases of the new Scala Reflection API introduced in version 2.10.0 of the Scala software distribution.
That new mirror-based reflection API (see [2]) for the Scala programming language gives access to meta-level informations about a Scala program.
A Scala manifest is an opaque type descriptor used by the Scala compiler to preserve run-time type information (RTTI). The class hierarchy looks as follows:
OptManifest <— ClassManifest <— Manifest <— AnyValManifest \ \— NoManifest
getMethods
) without
the need for instanciating a value of the instrumented type:
object methods extends App { def printMethods[T](t: T) { // requires instance val meths = t.getClass.getMethods println(meths take 5 mkString "\n") } def printMethods1(name: String) { // low-level val meths = Class.forName(name).getMethods println(meths take 5 mkString "\n") } def printMethods2[T: Manifest] { // no instance val meths = manifest[T].erasure.getMethods println(meths take 5 mkString "\n") } printMethods(Some("")) printMethods1("scala.Some") printMethods2[Some[_]] }
object subtypes extends App { def printSubtype[T, U](t: T, u: U) { val mt = Manifest.classType(t.getClass) val mu = Manifest.classType(u.getClass) println(mt <:< mu) } def printSubtype2[T: Manifest, U: Manifest] { println(manifest[T] <:< manifest[U]) } // arrays are invariant printSubtype(Array(0), Array[AnyVal](1)) // false printSubtype(List(""), List[AnyRef]("")) // true printSubtype((Seq(0), 1), (Seq[AnyVal](), 2)) // true printSubtype2[Array[Int], Array[AnyVal]] printSubtype2[List[String], List[AnyRef]] printSubtype2[(Seq[Int], Int), (Seq[AnyVal], Int)] }
object arrays extends App { class Side(n: Int) { override def toString = "Side "+n } class Shape[T: Manifest](n: Int) { val sides = new Array[T](n) try { val ctr = manifest[T].erasure.getConstructor(classOf[Int]) for (i <- 0 until n) sides(i) = ctr.newInstance(i:java.lang.Integer).asInstanceOf[T] } catch { case _ => } } val square = new Shape[Side](4) println(square.sides mkString "\n") }
Compiler internal symbols
package examples.ex1 class Foo(x: Int, val y: Int) { def this(x: Int) = this(x, 0) def <<(z: Int) {} } object Main extends App with SymbolPrinter { printSymbol(classOf[Foo]) printSymbol(classOf[scala.Option[_]]) printSymbol("scala.Symbol") }
package examples.ex1 import scala.reflect.api.Modifier import scala.reflect.runtime.Mirror trait SymbolPrinter { import Mirror._, Mirror.definitions._ def printSymbol[T](clazz: Class[T]) { printSymbol(classToSymbol(clazz)) } def printSymbol(name: String) { printSymbol(classWithName(name)) } def printSymbol(sym: Symbol) { val buf = new StringBuilder() var indent = 0 // ... def traverse(sym: Symbol) { sym.tpe match { case MethodType(pt, rt) => addFlags(sym) buf append "def " append symbolName(sym) addParameters(pt) buf append ": " append rt // ... } } traverse(sym) println(buf.toString) } }
class Foo(x: Int, val y: Int) { def Foo(x: Int): examples.ex1.Foo def <<(z: Int): Unit } class Option extends Product with Serializable { abstract def isEmpty: Boolean def isDefined: Boolean abstract def get: A // ... } object Option extends Serializable { implicit def option2Iterable[A](xo: Option[A]): Iterable[A] def apply[A](x: A): Option[A] private def readResolve(): Object } class Symbol private (val name: String) extends Serializable { override def toString(): String @throws(classOf[java.io.ObjectStreamException]) private def readResolve(): Any override def hashCode(): Int override def equals(other: Any): Boolean } object Symbol extends UniquenessCache[String,Symbol] with Serializable { protected def valueFromKey(name: String): Symbol protected def keyFromValue(sym: Symbol): Option[String] private def readResolve(): Object }
package examples.ex2 import scala.reflect.Code object Main extends App with SourcePrinter { printSource(Code.lift { 5 }.tree) var y = 3 printSource(Code.lift { 5+y }.tree) printSource(Code.lift { if (y < 0) -y else y }.tree) printSource(Code.lift { if (y < 0) -y else { val z = 5+y; y-z } }.tree) printSource(Code.lift { (x: Int) => x + y + 1 }.tree) printSource(Code.lift { (x: Int) => { var z = 0 while (z < y) z += 1 x + z + 1 } }.tree) }
package examples.ex2 import scala.reflect.api.Modifier import scala.reflect.mirror._ import scala.reflect.runtime.Mirror.ToolBox trait SourcePrinter { private val toolbox = new ToolBox() def printSource(tree: Tree) { val ttree = toolbox.typeCheck(tree) val buf = new StringBuilder // ... var indent = 0 def traverse(tree: Tree) { tree match { case Apply(fun, args) => traverse(fun) if (args.length == 1) { buf append " " traverse(args.head) } else { buf append "(" args foreach traverse buf append ")" } case Assign(lhs, rhs) => traverse(lhs) buf append " = " traverse(rhs) case Block(List(), expr) => traverse(expr) // ... } } traverse(ttree) println(buf.toString) } }
5 5 + Main.y if (Main.y < 0) -Main.y else Main.y if (Main.y < 0) -Main.y else { val z: Int = 5 + Main.y Main.y - z } (x: Int) => x + Main.y + 1 (x: Int) => { var z: Int = 0 /*label*/def while$1(): Unit = if (z < Main.y) { z = z + 1 while$1() } else () while$1() x + z + 1 }