reflection in scala whats, whys and hows - walter cazzola (dipartimento di informatica - università...

43
Reflection in Scala 2.10+ Walter Cazzola Reflection introduction properties example Scala Reflection trees universes examples Macros References Slide 1 of 22 Reflection in Scala 2.10+ Whats, Whys and Hows Walter Cazzola Dipartimento di Informatica Università degli Studi di Milano e-mail: [email protected]

Upload: scala-italy

Post on 10-May-2015

779 views

Category:

Technology


9 download

DESCRIPTION

Scala meetup - Milan, 25 May 2013 Computational reflection is a mechanism that permits to do computations on the computation itself. Scala before version 2.10 was relying on the Java implementation for all the reflective computations but this has some limitations especially on the support of types proper of Scala. In Scala 2.10 reflection becomes a native concept and covers the whole spectrum of Scala concepts; it also introduces some specific reflective mechanisms as macros. In this talk, we will explore what reflection is, why it is a desirable mechanism for modern programming languages and to what extent and how Scala 2.10 supports it.

TRANSCRIPT

Page 1: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 1 of 22

Reflection in Scala 2.10+

Whats, Whys and Hows

Walter Cazzola

Dipartimento di Informatica

Università degli Studi di Milano

e-mail: [email protected]

Page 2: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 2 of 22

Computational Reflection.General Definitions.

Computational reflection can be intuitively defined as:

}the activity done by a SW system to represent and

manipulate its own structure and behavior~. [1]

The reflective activity is done analogously to the usual system

activity.

[1] D. Bobrow, R. G. Gabriel and J. L. White. CLOS in Context.

In OOP: the CLOS Perspective. MIT Press, 1993.

Page 3: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 3 of 22

Computational Reflection.Characterization of a Reflective System.

Structural and Behavioral Reflection

– the behavioral reflection allows the program of monitoring and

manipulating its own computation;

– the structural reflection allows the program of inspecting and al-

tering its own structure

Introspection and Intercession.

– introspection permits to observe the structure and behavior of

the application, whereas

– intercession permits to alter its structure and behavior

When the meta-level entities exist:

– compile-time: the causal connection is implicit, base-level and meta-

levels are merged together during a preprocessing phase;

– load-time: in this case the causal connection behaves as in the case,

reflection takes place at compile-time.

– run-time: the causal connection is explicit and must be maintained

by an entities super-parties, e.g., by the virtual machine or by the

run-time environment;

Page 4: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 3 of 22

Computational Reflection.Characterization of a Reflective System.

Structural and Behavioral Reflection

– the behavioral reflection allows the program of monitoring and

manipulating its own computation;

– the structural reflection allows the program of inspecting and al-

tering its own structure

Introspection and Intercession.

– introspection permits to observe the structure and behavior of

the application, whereas

– intercession permits to alter its structure and behavior

When the meta-level entities exist:

– compile-time: the causal connection is implicit, base-level and meta-

levels are merged together during a preprocessing phase;

– load-time: in this case the causal connection behaves as in the case,

reflection takes place at compile-time.

– run-time: the causal connection is explicit and must be maintained

by an entities super-parties, e.g., by the virtual machine or by the

run-time environment;

Page 5: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 4 of 22

Computational Reflection.Es. To Enrich the Behavior of a Method Call.

Each method call is logged into a file.

Hello logMetadoLog()doInvoke()

logMeta

hello

sayHello() invoke(sayHello)

meta

meta

Page 6: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 4 of 22

Computational Reflection.Es. To Enrich the Behavior of a Method Call.

Each method call is logged into a file.

Hello logMetadoLog()doInvoke()

logMeta

hello

sayHello() invoke(sayHello)

meta

meta

sayHello()

Page 7: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 4 of 22

Computational Reflection.Es. To Enrich the Behavior of a Method Call.

Each method call is logged into a file.

Hello logMetadoLog()doInvoke()

logMeta

hello

sayHello() invoke(sayHello)

meta

metainvoke()

sayHello()

Page 8: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 4 of 22

Computational Reflection.Es. To Enrich the Behavior of a Method Call.

Each method call is logged into a file.

å

logFile

Hello logMetadoLog()doInvoke()

logMeta

hello

sayHello() invoke(sayHello)

meta

doLog()

metainvoke()

sayHello()

Method sayHello on Hello object

Page 9: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 4 of 22

Computational Reflection.Es. To Enrich the Behavior of a Method Call.

Each method call is logged into a file.

å

logFile

console

Hello logMetadoLog()doInvoke()

logMeta

hello

sayHello() invoke(sayHello)

meta

doLog()

metainvoke()

sayHello()

doInvoke()

Method sayHello on Hello object

Hello World!!!

Page 10: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 5 of 22

Scala Reflection.The Java Case.

Java provides reflection via the java.lang.reflection library

– pretty good for introspection activities but

– quite limited on intercession (only behavioral manipulation)

Scala <2.10 inherits reflection from Java.

So why should we need an ad hoc implementation?

– some types are wrongly reified due to type erasure

scala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])true

scala> println(A[String].isInstanceOf[A[Int]])true

– the whole spectrum of types, modifiers and constructs is not cov-

ered (e.g., implicit, path dependent and abstract types)

– limited intercession, no structural reflection at all.

Page 11: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 5 of 22

Scala Reflection.The Java Case.

Java provides reflection via the java.lang.reflection library

– pretty good for introspection activities but

– quite limited on intercession (only behavioral manipulation)

Scala <2.10 inherits reflection from Java.

So why should we need an ad hoc implementation?

– some types are wrongly reified due to type erasure

scala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])true

scala> println(A[String].isInstanceOf[A[Int]])true

– the whole spectrum of types, modifiers and constructs is not cov-

ered (e.g., implicit, path dependent and abstract types)

– limited intercession, no structural reflection at all.

Page 12: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 5 of 22

Scala Reflection.The Java Case.

Java provides reflection via the java.lang.reflection library

– pretty good for introspection activities but

– quite limited on intercession (only behavioral manipulation)

Scala <2.10 inherits reflection from Java.

So why should we need an ad hoc implementation?

– some types are wrongly reified due to type erasure

scala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])true

scala> println(A[String].isInstanceOf[A[Int]])true

– the whole spectrum of types, modifiers and constructs is not cov-

ered (e.g., implicit, path dependent and abstract types)

– limited intercession, no structural reflection at all.

Page 13: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 6 of 22

Reflection in ScalaCore Data Structures

Who can access to the necessary data?

The compiler.

Trees, Symbols and Types

[17:52]cazzola@surtur:~>scalac -Xshow-phasesphase name id description---------- -- -----------

parser 1 parse source into ASTs, perform simple desugaringnamer 2 resolve names, attach symbols to named treestyper 4 the meat and potatoes: type the trees

pickler 8 serialize symbol tables

Reflection in Scala regards

– ASTs and types

– all the information about them can be provided by the compiler

These are just few out the 30 phases the compiler performs.

Page 14: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 6 of 22

Reflection in ScalaCore Data Structures

Who can access to the necessary data? The compiler.

Trees, Symbols and Types

[17:52]cazzola@surtur:~>scalac -Xshow-phasesphase name id description---------- -- -----------

parser 1 parse source into ASTs, perform simple desugaringnamer 2 resolve names, attach symbols to named treestyper 4 the meat and potatoes: type the trees

pickler 8 serialize symbol tables

Reflection in Scala regards

– ASTs and types

– all the information about them can be provided by the compiler

These are just few out the 30 phases the compiler performs.

Page 15: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 7 of 22

Scala Reflection-Yshow-trees

object Test {println("Hello World!")}

[18:05]cazzola@surtur:~>scalac -Xprint:parser -Yshow-trees helloworld.scala[[syntax trees at end of parser]] // Scala source: helloworld.scalaPackageDef("<empty>"ModuleDef(0 "Test"Template("scala"."AnyRef" // parentsValDef(private "_" <tpt> <empty>)DefDef(0 "<init>" [] List(Nil) <tpt>Block(Apply(super."<init>"Nil

)())

)Apply("println""Hello World!"

))

))

Page 16: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 8 of 22

Scala ReflectionshowRaw

scala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}

scala> ru.reify{ object Test { println("Hello World!") } }res0: reflect.runtime.universe.Expr[Unit] =Expr[Unit]({object Test extends AnyRef {def <init>() = {super.<init>();()

};Predef.println("Hello World!")

};()

})

scala> ru.showRaw(res0.tree)res1: String = Block(List(ModuleDef(Modifiers(), newTermName("Test"),

Template(List(Ident(newTypeName("AnyRef"))), emptyValDef,List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(),

Block(List(Apply(

Select(Super(This(tpnme.EMPTY), tpnme.EMPTY),nme.CONSTRUCTOR), List())), Literal(Constant(()))

)), Apply(Select(Ident(scala.Predef),newTermName("println")), List(Literal(Constant("Hello World!"))))

)))), Literal(Constant(())))

Page 17: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 9 of 22

Reflection in ScalaSummary of the Trees

Trees are created naked by Parser.

Both definitions and references (expressed as ASTs) get their

symbols filled in by Namer (tree.symbol).

When creating symbols, Namer also creates their completers,

lazy thunks that know how to populate symbol types (symbol.info).

Typer inspects trees, uses their symbols to transform trees and

assign types to them (tree.tpe).

Shortly afterwards Pickler kicks in and serializes reachable sym-

bols along with their types into ScalaSignature annotations.

Page 18: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 10 of 22

Scala ReflectionUniverses & Mirrors

Universes are environments that pack together trees, symbolsand their types.

– Compiler (scala.tools.nsc.Global) is a universe.

– Reflection runtime (scala.reflect.runtime.universe) is a universe

too.

– Macro context (scala.reflect.macros.Context) holds a reference

to a universe.

Mirrors abstract population of symbol tables.

Each universe can have multiple mirrors, which can share symbolswith each other within their parent universe.

– Compiler loads symbols from pickles using its own *.class parser. It

has only one mirror, the rootMirror.

– Reflective mirror uses Java reflection to load and parse ScalaSigna-tures. Every classloader corresponds to its own mirror created

with ru.runtimeMirror(classloader).

– Macro context refers to the compiler’s symbol table.

Page 19: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 10 of 22

Scala ReflectionUniverses & Mirrors

Universes are environments that pack together trees, symbolsand their types.

– Compiler (scala.tools.nsc.Global) is a universe.

– Reflection runtime (scala.reflect.runtime.universe) is a universe

too.

– Macro context (scala.reflect.macros.Context) holds a reference

to a universe.

Mirrors abstract population of symbol tables.

Each universe can have multiple mirrors, which can share symbolswith each other within their parent universe.

– Compiler loads symbols from pickles using its own *.class parser. It

has only one mirror, the rootMirror.

– Reflective mirror uses Java reflection to load and parse ScalaSigna-tures. Every classloader corresponds to its own mirror created

with ru.runtimeMirror(classloader).

– Macro context refers to the compiler’s symbol table.

Page 20: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 11 of 22

Scala ReflectionEntry Points

Using a universe depends on your scenario.

– You can play with compiler’s universe (aka global) in REPL’s :powermode.

– With runtime reflection you typically go through the Mirror in-

terface, e.g. scala.reflect.runtime.currentMirror, then cm.reflectand then you can get/set fields, invoke methods, etc.

– In a macro context, you import c.universe._ and can use imported

factories to create trees and types (avoid to create symbols).

All universe artifacts are path-dependent on their universe.

scala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}

scala> ru.reify(2.toString)res1: reflect.runtime.universe.Expr[String] = Expr[String](2.toString())

With runtime reflection, there is only one universe.

With macros it is more complicated. To pass artifacts around is

necessary to piggyback the universe as well.

Page 21: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 11 of 22

Scala ReflectionEntry Points

Using a universe depends on your scenario.

– You can play with compiler’s universe (aka global) in REPL’s :powermode.

– With runtime reflection you typically go through the Mirror in-

terface, e.g. scala.reflect.runtime.currentMirror, then cm.reflectand then you can get/set fields, invoke methods, etc.

– In a macro context, you import c.universe._ and can use imported

factories to create trees and types (avoid to create symbols).

All universe artifacts are path-dependent on their universe.

scala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}

scala> ru.reify(2.toString)res1: reflect.runtime.universe.Expr[String] = Expr[String](2.toString())

With runtime reflection, there is only one universe.

With macros it is more complicated. To pass artifacts around is

necessary to piggyback the universe as well.

Page 22: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 12 of 22

Scala ReflectionInspect Members

scala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}

scala> trait X { def foo: String }defined trait X

scala> ru.typeOf[X]res0: reflect.runtime.universe.Type = X

scala> res0.membersres1: reflect.runtime.universe.MemberScope =

Scopes(method $asInstanceOf, method $isInstanceOf, method synchronized,method ##, method !=, method ==, method ne, method eq, constructor Object,method notifyAll, method notify, method clone, method getClass,method hashCode, method toString, method equals, method wait,method finalize, method asInstanceOf, method isInstanceOf, method !=,method ==, method foo)

Page 23: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 12 of 22

Scala ReflectionInspect Members

scala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}

scala> trait X { def foo: String }defined trait X

scala> ru.typeOf[X]res0: reflect.runtime.universe.Type = X

scala> res0.membersres1: reflect.runtime.universe.MemberScope =

Scopes(method $asInstanceOf, method $isInstanceOf, method synchronized,method ##, method !=, method ==, method ne, method eq, constructor Object,method notifyAll, method notify, method clone, method getClass,method hashCode, method toString, method equals, method wait,method finalize, method asInstanceOf, method isInstanceOf, method !=,method ==, method foo)

Page 24: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 12 of 22

Scala ReflectionInspect Members

scala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}

scala> trait X { def foo: String }defined trait X

scala> ru.typeOf[X]res0: reflect.runtime.universe.Type = X

scala> res0.membersres1: reflect.runtime.universe.MemberScope =

Scopes(method $asInstanceOf, method $isInstanceOf, method synchronized,method ##, method !=, method ==, method ne, method eq, constructor Object,method notifyAll, method notify, method clone, method getClass,method hashCode, method toString, method equals, method wait,method finalize, method asInstanceOf, method isInstanceOf, method !=,method ==, method foo)

Page 25: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 13 of 22

Scala ReflectionAnalyze and Invoke Members

Get the type, the constructor and its parameters

scala> case class Person(name: String, age: Int)defined class Person

scala> val personType = typeOf[Person]personType: reflect.runtime.universe.Type = Person

scala> val ctor = personType.member(nme.CONSTRUCTOR)ctor: reflect.runtime.universe.Symbol = constructor Person

scala> val args = ctor.asMethod.paramss.head map {p=>(p.name.decoded, p.typeSignature)}args:List[(String, reflect.runtime.universe.Type)]=List((name,String), (age,scala.Int))

Create a new instance (via the mirror)

scala> val classMirror = currentMirror.reflectClass(personType.typeSymbol.asClass)classMirror: reflect.runtime.universe.ClassMirror = class mirror for Person

scala> val ctor = personType.declaration(nme.CONSTRUCTOR).asMethodctor: reflect.runtime.universe.MethodSymbol = constructor Person

scala> classMirror.reflectConstructor(ctor).apply("Joe", 73)res1: Any = Person(Joe,73)

Page 26: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 13 of 22

Scala ReflectionAnalyze and Invoke Members

Get the type, the constructor and its parameters

scala> case class Person(name: String, age: Int)defined class Person

scala> val personType = typeOf[Person]personType: reflect.runtime.universe.Type = Person

scala> val ctor = personType.member(nme.CONSTRUCTOR)ctor: reflect.runtime.universe.Symbol = constructor Person

scala> val args = ctor.asMethod.paramss.head map {p=>(p.name.decoded, p.typeSignature)}args:List[(String, reflect.runtime.universe.Type)]=List((name,String), (age,scala.Int))

Create a new instance (via the mirror)

scala> val classMirror = currentMirror.reflectClass(personType.typeSymbol.asClass)classMirror: reflect.runtime.universe.ClassMirror = class mirror for Person

scala> val ctor = personType.declaration(nme.CONSTRUCTOR).asMethodctor: reflect.runtime.universe.MethodSymbol = constructor Person

scala> classMirror.reflectConstructor(ctor).apply("Joe", 73)res1: Any = Person(Joe,73)

Page 27: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 13 of 22

Scala ReflectionAnalyze and Invoke Members

Get the type, the constructor and its parameters

scala> case class Person(name: String, age: Int)defined class Person

scala> val personType = typeOf[Person]personType: reflect.runtime.universe.Type = Person

scala> val ctor = personType.member(nme.CONSTRUCTOR)ctor: reflect.runtime.universe.Symbol = constructor Person

scala> val args = ctor.asMethod.paramss.head map {p=>(p.name.decoded, p.typeSignature)}args:List[(String, reflect.runtime.universe.Type)]=List((name,String), (age,scala.Int))

Create a new instance (via the mirror)

scala> val classMirror = currentMirror.reflectClass(personType.typeSymbol.asClass)classMirror: reflect.runtime.universe.ClassMirror = class mirror for Person

scala> val ctor = personType.declaration(nme.CONSTRUCTOR).asMethodctor: reflect.runtime.universe.MethodSymbol = constructor Person

scala> classMirror.reflectConstructor(ctor).apply("Joe", 73)res1: Any = Person(Joe,73)

Page 28: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 14 of 22

Scala ReflectionDefeat Type Erasure

The compiler implements the type erasure

scala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])true

scala> println(A[String].isInstanceOf[A[Int]])true

Solution: to pass through the reflective TypeTag

scala> import scala.reflect.runtime.universe._

import scala.reflect.runtime.universe._

scala> case class B[T: TypeTag] { val tpe = typeOf[T] }defined class B

scala> println(B[String].tpe == typeOf[String])truescala> println(B[String].tpe == typeOf[Int])false

scala> println(B[List[String]].tpe == typeOf[List[String]])truescala> println(B[List[String]].tpe == typeOf[List[Int]])false

Page 29: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 14 of 22

Scala ReflectionDefeat Type Erasure

The compiler implements the type erasure

scala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])true

scala> println(A[String].isInstanceOf[A[Int]])true

Solution: to pass through the reflective TypeTag

scala> import scala.reflect.runtime.universe._

import scala.reflect.runtime.universe._

scala> case class B[T: TypeTag] { val tpe = typeOf[T] }defined class B

scala> println(B[String].tpe == typeOf[String])truescala> println(B[String].tpe == typeOf[Int])false

scala> println(B[List[String]].tpe == typeOf[List[String]])truescala> println(B[List[String]].tpe == typeOf[List[Int]])false

Page 30: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 14 of 22

Scala ReflectionDefeat Type Erasure

The compiler implements the type erasure

scala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])true

scala> println(A[String].isInstanceOf[A[Int]])true

Solution: to pass through the reflective TypeTag

scala> import scala.reflect.runtime.universe._

import scala.reflect.runtime.universe._

scala> case class B[T: TypeTag] { val tpe = typeOf[T] }defined class B

scala> println(B[String].tpe == typeOf[String])truescala> println(B[String].tpe == typeOf[Int])false

scala> println(B[List[String]].tpe == typeOf[List[String]])truescala> println(B[List[String]].tpe == typeOf[List[Int]])false

Page 31: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 14 of 22

Scala ReflectionDefeat Type Erasure

The compiler implements the type erasure

scala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])true

scala> println(A[String].isInstanceOf[A[Int]])true

Solution: to pass through the reflective TypeTag

scala> import scala.reflect.runtime.universe._

import scala.reflect.runtime.universe._

scala> case class B[T: TypeTag] { val tpe = typeOf[T] }defined class B

scala> println(B[String].tpe == typeOf[String])truescala> println(B[String].tpe == typeOf[Int])false

scala> println(B[List[String]].tpe == typeOf[List[String]])truescala> println(B[List[String]].tpe == typeOf[List[Int]])false

Page 32: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 15 of 22

Scala ReflectionCompilation at Run-Time

scala> import scala.reflect.runtime.universe._

import scala.reflect.runtime.universe._

scala> import scala.tools.reflect.ToolBoximport scala.tools.reflect.ToolBox

scala> val tree = Apply(Select(Literal(Constant(40)), newTermName("$plus")), List(Literal(Constant(2))))

tree: reflect.runtime.universe.Apply = 40.$plus(2)

scala> val cm = scala.reflect.runtime.universe.runtimeMirror(getClass.getClassLoader)cm: reflect.runtime.universe.Mirror = JavaMirror with ...scala> println(cm.mkToolBox().eval(tree))42

Toolbox is a full-fledged compiler.

– Unlike the regular compiler, it uses Java reflection encapsulated in

the provided mirror to populate its symbol table.

– Toolbox wraps the input AST, sets its phase to Namer (skipping

Parser) and performs the compilation into an in-memory directory.

– After the compilation is finished, toolbox fires up a classloader that

loads and lauches the code.

Page 33: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 16 of 22

Scala ReflectionCompile-Time Reflection or Structural Reflection

MacroIt is a pattern or a rule that specifies how a given input se-

quence should be mapped into another.

. . . but what is a macro, really?A regular scala function that transforms an AST into another

AST and it is called by the compiler.

E.g.,

if (1 ==1) { true } else { false }

if (1.$eq$eq(1)) { true } else { false }

Page 34: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 16 of 22

Scala ReflectionCompile-Time Reflection or Structural Reflection

MacroIt is a pattern or a rule that specifies how a given input se-

quence should be mapped into another.

. . . but what is a macro, really?A regular scala function that transforms an AST into another

AST and it is called by the compiler.

E.g.,

if (1 ==1) { true } else { false }

if (1.$eq$eq(1)) { true } else { false }

Page 35: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 16 of 22

Scala ReflectionCompile-Time Reflection or Structural Reflection

MacroIt is a pattern or a rule that specifies how a given input se-

quence should be mapped into another.

. . . but what is a macro, really?A regular scala function that transforms an AST into another

AST and it is called by the compiler.

E.g.,

if (1 ==1) { true } else { false }

if (1.$eq$eq(1)) { true } else { false }

Page 36: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 16 of 22

Scala ReflectionCompile-Time Reflection or Structural Reflection

MacroIt is a pattern or a rule that specifies how a given input se-

quence should be mapped into another.

. . . but what is a macro, really?A regular scala function that transforms an AST into another

AST and it is called by the compiler.

E.g.,

if (1 ==1) { true } else { false }

if (1.$eq$eq(1)) { true } else { false }

Page 37: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 17 of 22

Scala ReflectionAbstract Syntax Tree (AST)

if (1.$eq$eq(1)) {true} else {false}

If

Apply

Select

Literal

1

"$eq$eq"

List

Literal

1

Literal

true

Literal

false

Page 38: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 18 of 22

Scala ReflectionApplications of the Macros

Macros permits to transform an AST into a new AST at com-

pile time.

. . . but what we can do wth that?E.g.,

– zero-overhead assertions, logging or bounds checking;

– static loops unroll or other kind of optimization; or

– static DSL transformation

Page 39: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 19 of 22

Scala ReflectionMacros in Action

import scala.language.experimental.macrosimport scala.reflect.macros.Contextimport scala.collection.mutable.{ListBuffer, Stack}

object Macros {def printf(format: String, params: Any*): Unit = macro printf_impldef printf_impl(c:Context)(format:c.Expr[String], params:c.Expr[Any]*):c.Expr[Unit]={import c.universe._

val Literal(Constant(s_format: String)) = format.treeval evals = ListBuffer[ValDef]()

def precompute(value: Tree, tpe: Type): Ident = {val freshName = newTermName(c.fresh("eval$"))evals += ValDef(Modifiers(), freshName, TypeTree(tpe), value)Ident(freshName)

}

val paramsStack = Stack[Tree]((params map (_.tree)): _*)val refs = s_format.split("(?<=%[\\w%])|(?=%[\\w%])") map {case "%d" => precompute(paramsStack.pop, typeOf[Int])case "%s" => precompute(paramsStack.pop, typeOf[String])case "%%" => Literal(Constant("%"))case part => Literal(Constant(part))

}

val stats = evals ++ refs.map(ref => reify(print(c.Expr[Any](ref).splice)).tree)c.Expr[Unit](Block(stats.toList, Literal(Constant(()))))

}}

Page 40: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 20 of 22

Scala ReflectionMacros in Action

object Test extends App {import Macros._

printf("hello %s!", "world")}

[22:18]cazzola@surtur:~>scalac Macros.scala[22:19]cazzola@surtur:~>scalac Test.scala[22:20]cazzola@surtur:~>scala Testhello world!

Note that,

– macros must be compiled separately from where they should used,

i.e., no recursive macros are possible;

– macros work on compile time information, i.e., they reify, manipulate

and return an AST.

Page 41: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 21 of 22

Scala ReflectionConclusions

In Scala 2.10+ you can have all the information about your pro-

gram that the compiler has (well, almost).

– this includes trees, symbols and types and more.

– you can reflect at run-time (scala.reflect.runtime.universe) or at

compile-time (macros).

But (Devil’s Advocate) . . .

– we are still far from C++ Template Programming, e.g., no factorial

at compile time.

– Java reflection, even if limited, is easier to use.

Page 42: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 21 of 22

Scala ReflectionConclusions

In Scala 2.10+ you can have all the information about your pro-

gram that the compiler has (well, almost).

– this includes trees, symbols and types and more.

– you can reflect at run-time (scala.reflect.runtime.universe) or at

compile-time (macros).

But (Devil’s Advocate) . . .

– we are still far from C++ Template Programming, e.g., no factorial

at compile time.

– Java reflection, even if limited, is easier to use.

Page 43: Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)

Reflection in

Scala 2.10+

Walter Cazzola

Reflection

introduction

properties

example

ScalaReflection

trees

universes

examples

Macros

References

Slide 22 of 22

References

I Martin Odersky.

Reflection and Compilers.

Keynote at Lang.NEXT, April 2012.

I Eugene Burmako.

Macros.

Scala 2.10 documentation, École Polytechnique Fédérale de Lausanne,

2013.

I Heather Miller, Eugene Burmako, and Philipp Haller.

Scala Reflection 2.10.

Scala 2.10 documentation, École Polytechnique Fédérale de Lausanne,

2013.