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
}