8000 Adds 2.10 reflection guide in its entirety by heathermiller · Pull Request #162 · scala/docs.scala-lang · GitHub
[go: up one dir, main page]

Skip to content

Adds 2.10 reflection guide in its entirety #162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 15, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Adds 2.10 reflection guide in its entirety
  • Loading branch information
heathermiller committed Jan 15, 2013
commit 4e3dc07fd1793119485d41b772398bcdff9e87c3
2 changes: 1 addition & 1 deletion overviews/core/_posts/2012-12-20-macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ title: Macros
disqus: true
partof: macros
overview: macros
label-color: warning
label-color: important
label-text: Experimental
---
2 changes: 1 addition & 1 deletion overviews/core/_posts/2013-01-02-reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ title: Reflection
disqus: true
partof: reflection
overview: reflection
label-color: warning
label-color: important
label-text: Experimental
---

Large diffs are not rendered by default.

194 changes: 187 additions & 7 deletions overviews/reflection/environment-universes-mirrors.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,193 @@ num: 2
outof: 6
---

<span class="label warning" style="float: right;">EXPERIMENTAL</span>
<span class="label important" style="float: right;">EXPERIMENTAL</span>

This document is in progress and should be ready soon.
## Environment

In the meanwhile, to get an overview of reflection please follow our slides at [http://scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf](http://scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf).
The reflection environment differs based on whether the reflective task is to
be done at run time or at compile time. The distinction between an environment to be used at
run time or compile time is encapsulated in a so-called *universe*. Another
important aspect of the reflective environment is the set of entities that we
have reflective access to. This set of entities is determined by a so-called
*mirror*.

Scaladoc pages relevant to this topic:
* [Universe](http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Universe)
* [Mirror](http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Mirror)
* [Mirrors](http://www.scala-lang.org/api/current/index.html#scala.reflect.api.Mirrors)
For example, the entities accessible through runtime
reflection are made available by a `ClassloaderMirror`. This mirror provides
only access to entities (packages, types, and members) loaded by a specific
classloader.

Mirrors not only determine the set of entities that can be accessed
reflectively. They also provide reflective operations to be performed on those
entities. For example, in runtime reflection an *invoker mirror* can be used
to invoke a method or constructor of a class.

## Universes

There are two principal
types of universes-- since there exists both runtime and compile-time
reflection capabilities, one must use the universe that corresponds to
whatever the task is at hand. Either:

- `scala.reflect.runtime.universe` for **runtime reflection**, or
- `scala.reflect.macros.Universe` for **compile-time reflection**.

A universe provides an interface to all the principal concepts used in
reflection, such as `Types`, `Trees`, and `Annotations`.

## Mirrors

All information provided by
reflection is made accessible through *mirrors*. Depending on
the type of information to be obtained, or the reflective action to be taken,
different flavors of mirrors must be used. *Classloader mirrors* can be used to obtain representations of types and
members. From a classloader mirror, it's possible to obtain more specialized *invoker mirrors* (the most commonly-used mirrors), which implement reflective
invocations, such as method or constructor calls and field accesses.

Summary:

- **"Classloader" mirrors**.
These mirrors translate names to symbols (via methods `staticClass`/`staticModule`/`staticPackage`).

- **"Invoker" mirrors**.
These mirrors implement reflective invocations (via methods `MethodMirror.apply`, `FieldMirror.get`, etc.). These "invoker" mirrors are the types of mirrors that are most commonly used.

### Runtime Mirrors

The entry point to mirrors for use at runtime is via `ru.runtimeMirror(<classloader>)`, where `ru` is `scala.reflect.runtime.universe`.

The result of a `scala.reflect.api.JavaMirrors#runtimeMirror` call is a classloader mirror, of type `scala.reflect.api.Mirrors#ReflectiveMirror`, which can load symbols by name.

A classloader mirror can create invoker mirrors (including `scala.reflect.api.Mirrors#InstanceMirror`, `scala.reflect.api.Mirrors#MethodMirror`, `scala.reflect.api.Mirrors#FieldMirror`, `scala.reflect.api.Mirrors#ClassMirror`, and `scala.reflect.api.Mirrors#ModuleMirror`).

Examples of how these two types of mirrors interact are available below.

### Types of Mirrors, Their Use Cases & Examples

A `ReflectiveMirror` is used for loading symbols by name, and as an entry point into invoker mirrors. Entry point: `val m = ru.runtimeMirror(<classloader>)`. Example:

scala> val ru = scala.reflect.runtime.universe
ru: scala.reflect.api.JavaUniverse = ...

scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror ...

An `InstanceMirror` is used for creating invoker mirrors for methods and fields and for inner classes and inner objects (modules). Entry point: `val im = m.reflect(<value>)`. Example:

scala> class C { def x = 2 }
defined class C

scala> val im = m.reflect(new C)
im: reflect.runtime.universe.InstanceMirror = instance mirror for C@3442299e

A `MethodMirror` is used for invoking instance methods (Scala only has instance methods-- methods of objects are instance methods of object instances, obtainable via `ModuleMirror.instance`). Entry point: `val mm = im.reflectMethod(<method symbol>)`. Example:

scala> val methodX = typeOf[C].declaration(newTermName("x")).asMethod
methodX: reflect.runtime.universe.MethodSymbol = method x

scala> val mm = im.reflectMethod(methodX)
mm: reflect.runtime.universe.MethodMirror = method mirror for C.x: scala.Int (bound to C@3442299e)

scala> mm()
res0: Any = 2

A `FieldMirror` is used for getting/setting instance fields (like methods, Scala only has instance fields, see above). Entry point: `val fm = im.reflectMethod(<field or accessor symbol>)`. Example:

scala> class C { val x = 2; val y = 3 }
defined class C

scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror ...

scala> val im = m.reflect(new C)
im: reflect.runtime.universe.InstanceMirror = instance mirror for C@5f0c8ac1

scala> val fieldX = typeOf[C].declaration(newTermName("x")).asTerm.accessed.asTerm
fieldX: reflect.runtime.universe.TermSymbol = value x
scala> val fmX = im.reflectField(fieldX)
fmX: reflect.runtime.universe.FieldMirror = field mirror for C.x (bound to C@5f0c8ac1)

scala> fmX.get
res0: Any = 2

scala> fmX.set(3)
scala.ScalaReflectionException: cannot set an immutable field x
...

scala> val fieldY = typeOf[C].declaration(newTermName("y")).asTerm.accessed.asTerm
fieldY: reflect.runtime.universe.TermSymbol = variable y

scala> val fmY = im.reflectField(fieldY)
fmY: reflect.runtime.universe.FieldMirror = field mirror for C.y (bound to C@5f0c8ac1)

scala> fmY.get
res1: Any = 3

scala> fmY.set(4)

scala> fmY.get
res2: Any = 4

A `ClassMirror` is used for creating invoker mirrors for constructors. Entry points: for static classes `val cm1 = m.reflectClass(<class symbol>)`, for inner classes `val mm2 = im.reflectClass(<module symbol>)`. Example:

scala> case class C(x: Int)
defined class C

scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror ...

scala> val classC = typeOf[C].typeSymbol.asClass

classC: reflect.runtime.universe.Symbol = class C

scala> val cm = m.reflectClass(classC)
cm: reflect.runtime.universe.ClassMirror = class mirror for C (bound to null)

scala> val ctorC = typeOf[C].declaration(ru.nme.CONSTRUCTOR).asMethod
ctorC: reflect.runtime.universe.MethodSymbol = constructor C

scala> val ctorm = cm.reflectConstructor(ctorC)
ctorm: reflect.runtime.universe.MethodMirror = constructor mirror for C.<init>(x: scala.Int): C (bound to null)

scala> ctorm(2)
res0: Any = C(2)

A `ModuleMirror` is used for accessing instances of singleton objects. Entry points: for static objects `val mm1 = m.reflectModule(<module symbol>)`, for inner objects `val mm2 = im.reflectModule(<module symbol>)`. Example:

scala> object C { def x = 2 }
defined module C

scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror ...

scala> val objectC = typeOf[C.type].termSymbol.asModule
objectC: reflect.runtime.universe.ModuleSymbol = object C

scala> val mm = m.reflectModule(objectC)
mm: reflect.runtime.universe.ModuleMirror = module mirror for C (bound to null)

scala> val obj = mm.instance
obj: Any = C$@1005ec04

### Compile-Time Mirrors

Compile-time mirrors make use of only classloader mirrors to load symbols by name.

The entry point to classloader mirrors is via `scala.reflect.macros.Context#mirror`. Typical methods which use classloader mirrors include `scala.reflect.api.Mirror#staticClass`, `scala.reflect.api.Mirror#staticModule`, and `scala.reflect.api.Mirror#staticPackage`. For example:

import scala.reflect.macros.Context

case class Location(filename: String, line: Int, column: Int)

object Macros {
def currentLocation: Location = macro impl

def impl(c: Context): c.Expr[Location] = {
import c.universe._
val pos = c.macroApplication.pos
val clsLocation = c.mirror.staticModule("Location") // get symbol of "Location" object
c.Expr(Apply(Ident(clsLocation), List(Literal(Constant(pos.source.path)), Literal(Constant(pos.line)), Literal(Constant(pos.column)))))
}
}

*Of note:* There are several high-level alternatives that one can use to avoid having to manually lookup symbols. For example, `typeOf[Location.type].termSymbol` (or `typeOf[Location].typeSymbol` if we needed a `ClassSymbol`), which are typesafe since we don’t have to use strings to lookup the symbol.
29 changes: 0 additions & 29 deletions overviews/reflection/names-exprs-scopes-more.md

This file was deleted.

Loading
0