Your SlideShare is downloading. ×
Reflection in Scala Whats, Whys and Hows - Walter Cazzola (Dipartimento di Informatica - Università degli Studi di Milano)
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

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

737
views

Published on

Scala meetup - Milan, 25 May 2013 …

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.

Published in: Technology, News & Politics

1 Comment
4 Likes
Statistics
Notes
No Downloads
Views
Total Views
737
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
33
Comments
1
Likes
4
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 1 of 22Reflection in Scala 2.10+Whats, Whys and HowsWalter CazzolaDipartimento di InformaticaUniversità degli Studi di Milanoe-mail: cazzola@di.unimi.it
  • 2. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 2 of 22Computational Reflection.General Definitions.Computational reflection can be intuitively defined as:cthe activity done by a SW system to represent andmanipulate its own structure and behaviord. [1]The reflective activity is done analogously to the usual systemactivity.[1] D. Bobrow, R. G. Gabriel and J. L. White. CLOS in Context.In OOP: the CLOS Perspective. MIT Press, 1993.
  • 3. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 3 of 22Computational Reflection.Characterization of a Reflective System.Structural and Behavioral Reflection– the behavioral reflection allows the program of monitoring andmanipulating its own computation;– the structural reflection allows the program of inspecting and al-tering its own structureIntrospection and Intercession.– introspection permits to observe the structure and behavior ofthe application, whereas– intercession permits to alter its structure and behavior
  • 4. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 3 of 22Computational Reflection.Characterization of a Reflective System.Structural and Behavioral Reflection– the behavioral reflection allows the program of monitoring andmanipulating its own computation;– the structural reflection allows the program of inspecting and al-tering its own structureIntrospection and Intercession.– introspection permits to observe the structure and behavior ofthe application, whereas– intercession permits to alter its structure and behaviorWhen 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 maintainedby an entities super-parties, e.g., by the virtual machine or by therun-time environment;
  • 5. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 4 of 22Computational Reflection.Es. To Enrich the Behavior of a Method Call.Each method call is logged into a file.Hello logMetadoLog()doInvoke()logMetahellosayHello() invoke(sayHello)metameta
  • 6. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 4 of 22Computational Reflection.Es. To Enrich the Behavior of a Method Call.Each method call is logged into a file.Hello logMetadoLog()doInvoke()logMetahellosayHello() invoke(sayHello)metametasayHello()
  • 7. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 4 of 22Computational Reflection.Es. To Enrich the Behavior of a Method Call.Each method call is logged into a file.Hello logMetadoLog()doInvoke()logMetahellosayHello() invoke(sayHello)metametainvoke()sayHello()
  • 8. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 4 of 22Computational Reflection.Es. To Enrich the Behavior of a Method Call.Each method call is logged into a file.¯logFileHello logMetadoLog()doInvoke()logMetahellosayHello() invoke(sayHello)metadoLog()metainvoke()sayHello()Method sayHello on Hello object
  • 9. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 4 of 22Computational Reflection.Es. To Enrich the Behavior of a Method Call.Each method call is logged into a file.¯logFileconsoleHello logMetadoLog()doInvoke()logMetahellosayHello() invoke(sayHello)metadoLog()metainvoke()sayHello()doInvoke()Method sayHello on Hello objectHello World!!!
  • 10. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 5 of 22Scala 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.
  • 11. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 5 of 22Scala 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?
  • 12. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 5 of 22Scala 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 erasurescala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])truescala> 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.
  • 13. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 6 of 22Reflection in ScalaCore Data StructuresWho can access to the necessary data?
  • 14. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 6 of 22Reflection in ScalaCore Data StructuresWho 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 treespickler 8 serialize symbol tablesReflection in Scala regards– ASTs and types– all the information about them can be provided by the compilerThese are just few out the 30 phases the compiler performs.
  • 15. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 7 of 22Scala Reflection-Yshow-treesobject 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!"))))
  • 16. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 8 of 22Scala ReflectionshowRawscala> 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(())))
  • 17. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 9 of 22Reflection in ScalaSummary of the TreesTrees are created naked by Parser.Both definitions and references (expressed as ASTs) get theirsymbols 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 andassign types to them (tree.tpe).Shortly afterwards Pickler kicks in and serializes reachable sym-bols along with their types into ScalaSignature annotations.
  • 18. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 10 of 22Scala ReflectionUniverses & MirrorsUniverses 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 universetoo.– Macro context (scala.reflect.macros.Context) holds a referenceto a universe.
  • 19. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 10 of 22Scala ReflectionUniverses & MirrorsUniverses 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 universetoo.– Macro context (scala.reflect.macros.Context) holds a referenceto 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. Ithas only one mirror, the rootMirror.– Reflective mirror uses Java reflection to load and parse ScalaSigna-tures. Every classloader corresponds to its own mirror createdwith ru.runtimeMirror(classloader).– Macro context refers to the compiler’s symbol table.
  • 20. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 11 of 22Scala ReflectionEntry PointsUsing 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 importedfactories to create trees and types (avoid to create symbols).
  • 21. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 11 of 22Scala ReflectionEntry PointsUsing 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 importedfactories 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 isnecessary to piggyback the universe as well.
  • 22. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 12 of 22Scala ReflectionInspect Membersscala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}scala> trait X { def foo: String }defined trait X
  • 23. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 12 of 22Scala ReflectionInspect Membersscala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}scala> trait X { def foo: String }defined trait Xscala> ru.typeOf[X]res0: reflect.runtime.universe.Type = X
  • 24. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 12 of 22Scala ReflectionInspect Membersscala> import scala.reflect.runtime.{universe => ru}import scala.reflect.runtime.{universe=>ru}scala> trait X { def foo: String }defined trait Xscala> ru.typeOf[X]res0: reflect.runtime.universe.Type = Xscala> 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)
  • 25. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 13 of 22Scala ReflectionAnalyze and Invoke MembersGet the type, the constructor and its parametersscala> case class Person(name: String, age: Int)defined class Personscala> val personType = typeOf[Person]personType: reflect.runtime.universe.Type = Person
  • 26. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 13 of 22Scala ReflectionAnalyze and Invoke MembersGet the type, the constructor and its parametersscala> case class Person(name: String, age: Int)defined class Personscala> val personType = typeOf[Person]personType: reflect.runtime.universe.Type = Personscala> val ctor = personType.member(nme.CONSTRUCTOR)ctor: reflect.runtime.universe.Symbol = constructor Personscala> 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))
  • 27. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 13 of 22Scala ReflectionAnalyze and Invoke MembersGet the type, the constructor and its parametersscala> case class Person(name: String, age: Int)defined class Personscala> val personType = typeOf[Person]personType: reflect.runtime.universe.Type = Personscala> val ctor = personType.member(nme.CONSTRUCTOR)ctor: reflect.runtime.universe.Symbol = constructor Personscala> 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 Personscala> val ctor = personType.declaration(nme.CONSTRUCTOR).asMethodctor: reflect.runtime.universe.MethodSymbol = constructor Personscala> classMirror.reflectConstructor(ctor).apply("Joe", 73)res1: Any = Person(Joe,73)
  • 28. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 14 of 22Scala ReflectionDefeat Type ErasureThe compiler implements the type erasurescala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])truescala> println(A[String].isInstanceOf[A[Int]])true
  • 29. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 14 of 22Scala ReflectionDefeat Type ErasureThe compiler implements the type erasurescala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])truescala> println(A[String].isInstanceOf[A[Int]])trueSolution: to pass through the reflective TypeTagscala> import scala.reflect.runtime.universe._import scala.reflect.runtime.universe._scala> case class B[T: TypeTag] { val tpe = typeOf[T] }defined class B
  • 30. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 14 of 22Scala ReflectionDefeat Type ErasureThe compiler implements the type erasurescala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])truescala> println(A[String].isInstanceOf[A[Int]])trueSolution: to pass through the reflective TypeTagscala> import scala.reflect.runtime.universe._import scala.reflect.runtime.universe._scala> case class B[T: TypeTag] { val tpe = typeOf[T] }defined class Bscala> println(B[String].tpe == typeOf[String])truescala> println(B[String].tpe == typeOf[Int])false
  • 31. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 14 of 22Scala ReflectionDefeat Type ErasureThe compiler implements the type erasurescala> case class A[T]scala> println(A[String].isInstanceOf[A[String]])truescala> println(A[String].isInstanceOf[A[Int]])trueSolution: to pass through the reflective TypeTagscala> import scala.reflect.runtime.universe._import scala.reflect.runtime.universe._scala> case class B[T: TypeTag] { val tpe = typeOf[T] }defined class Bscala> println(B[String].tpe == typeOf[String])truescala> println(B[String].tpe == typeOf[Int])falsescala> println(B[List[String]].tpe == typeOf[List[String]])truescala> println(B[List[String]].tpe == typeOf[List[Int]])false
  • 32. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 15 of 22Scala ReflectionCompilation at Run-Timescala> import scala.reflect.runtime.universe._import scala.reflect.runtime.universe._scala> import scala.tools.reflect.ToolBoximport scala.tools.reflect.ToolBoxscala> 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))42Toolbox is a full-fledged compiler.– Unlike the regular compiler, it uses Java reflection encapsulated inthe provided mirror to populate its symbol table.– Toolbox wraps the input AST, sets its phase to Namer (skippingParser) and performs the compilation into an in-memory directory.– After the compilation is finished, toolbox fires up a classloader thatloads and lauches the code.
  • 33. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 16 of 22Scala ReflectionCompile-Time Reflection or Structural ReflectionMacroIt is a pattern or a rule that specifies how a given input se-quence should be mapped into another.
  • 34. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 16 of 22Scala ReflectionCompile-Time Reflection or Structural ReflectionMacroIt 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 anotherAST and it is called by the compiler.
  • 35. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 16 of 22Scala ReflectionCompile-Time Reflection or Structural ReflectionMacroIt 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 anotherAST and it is called by the compiler.E.g.,if (1 ==1) { true } else { false }
  • 36. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 16 of 22Scala ReflectionCompile-Time Reflection or Structural ReflectionMacroIt 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 anotherAST and it is called by the compiler.E.g.,if (1 ==1) { true } else { false }if (1.$eq$eq(1)) { true } else { false }
  • 37. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 17 of 22Scala ReflectionAbstract Syntax Tree (AST)if (1.$eq$eq(1)) {true} else {false}IfApplySelectLiteral1"$eq$eq"ListLiteral1LiteraltrueLiteralfalse
  • 38. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 18 of 22Scala ReflectionApplications of the MacrosMacros 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
  • 39. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 19 of 22Scala ReflectionMacros in Actionimport 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(()))))}}
  • 40. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 20 of 22Scala ReflectionMacros in Actionobject 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, manipulateand return an AST.
  • 41. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 21 of 22Scala ReflectionConclusionsIn 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 atcompile-time (macros).
  • 42. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 21 of 22Scala ReflectionConclusionsIn 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 atcompile-time (macros).But (Devil’s Advocate) . . .– we are still far from C++ Template Programming, e.g., no factorialat compile time.– Java reflection, even if limited, is easier to use.
  • 43. Reflection inScala 2.10+Walter CazzolaReflectionintroductionpropertiesexampleScala ReflectiontreesuniversesexamplesMacrosReferencesSlide 22 of 22ReferencesMartin Odersky.Reflection and Compilers.Keynote at Lang.NEXT, April 2012.Eugene Burmako.Macros.Scala 2.10 documentation, École Polytechnique Fédérale de Lausanne,2013.Heather Miller, Eugene Burmako, and Philipp Haller.Scala Reflection 2.10.Scala 2.10 documentation, École Polytechnique Fédérale de Lausanne,2013.