Skip to content

Commit 5617e46

Browse files
committed
Merge remote-tracking branch 'ingo/topic/reflection-docs' into topic/reflection
2 parents 447ccd9 + 3c50534 commit 5617e46

File tree

1 file changed

+32
-198
lines changed

1 file changed

+32
-198
lines changed

src/reflect/scala/reflect/api/Symbols.scala

Lines changed: 32 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -3,205 +3,19 @@ package api
33

44
/** This trait defines symbols and operations on them.
55
*
6-
* See the [[docs.scala-lang.org/overviews/reflection/overview.html Reflection Guide]] for a description of symbols
7-
* and infomation on getting started with Scala reflection API.
6+
* Symbols are used to establish bindings between a name and the entity it refers to, such as a class or a method.
7+
* Anything you define and can give a name to in Scala has a symbol.
88
*
9-
* === Symbols from a compile-time perspective ===
9+
* Certain types of tree nodes, such as [[scala.reflect.api.Trees#Ident Ident]] (references to identifiers) and
10+
* [[scala.reflect.api.Trees#Select Select]] (references to members) expose method [[Trees.SymTreeApi.symbol]]
11+
* to obtain the symbol that represents their declaration.
1012
*
11-
* Anything you define in Scala has a symbol. If you give something a name, then it has a symbol associated with it.
12-
* If you didn't give it a name, but you could have, then it has a symbol.
13+
* See the [[docs.scala-lang.org/overviews/reflection/overview.html Reflection Guide]] for more details and
14+
* intricacies about symbols.
1315
*
14-
* Symbols are used by the Scala compiler to establish bindings. When typechecking a Scala program,
15-
* the compiler populates [[scala.reflect.api.Trees#RefTrees ref trees]], such as [[scala.reflect.api.Trees#Ident Ident]]
16-
* (references to identifiers) and [[scala.reflect.api.Trees#Select Select]] (references to members)
17-
* with symbols that represent the declarations being referred to. Populating means setting the `symbol`
18-
* field to a non-empty value.
19-
*
20-
* Here's an example of how trees look after the `typer` phase of the Scala compiler (this phase performs the typechecking).
21-
* {{{
22-
* >cat Test.scala
23-
* def foo[T: TypeTag](x: Any) = x.asInstanceOf[T]
24-
*
25-
* >scalac -Xprint:typer -uniqid Test.scala
26-
* [[syntax trees at end of typer]]// Scala source: Test.scala
27-
* def foo#8339
28-
* [T#8340 >: Nothing#4658 <: Any#4657]
29-
* (x#9529: Any#4657)
30-
* (implicit evidence$1#9530: TypeTag#7861[T#8341])
31-
* : T#8340 =
32-
* x#9529.asInstanceOf#6023[T#8341];
33-
* }}}
34-
*
35-
* Shortly put, we write a small snippet and then compile it with scalac, asking the compiler to dump the trees
36-
* after the typer phase, printing unique ids of the symbols assigned to trees (if any).
37-
*
38-
* The resulting printout shows that identifiers have been linked to corresponding definitions.
39-
* For example, on the one hand, the `ValDef("x", ...)`, which represents the parameter of the method `foo`,
40-
* defines a method symbol with `id=9529`. On the other hand, the `Ident("x")` in the body of the method
41-
* got its `symbol` field set to the same symbol, which establishes the binding.
42-
*
43-
* In the light of this discussion, it might come as a surprise that the definition of the type parameter `T`
44-
* has a symbol with `id=8340`, whereas references to this type parameter all have a symbol with `id=8341`.
45-
* This is the only exception from the general principe outlined above. It happens because the Scala compiler
46-
* skolemizes type parameters (creates new symbols very similar to the original ones) before entering scopes
47-
* that define these parameters. This is an advanced feature of Scala, and the knowledge of it is needed only
48-
* when writing complex macros, but symbols in the macro universe [[scala.reflect.macros.Universe]] have the
49-
* `deskolemize` method, which goes back from skolems to the originating type parameters.
50-
*
51-
* === Symbols from a runtime perspective ===
52-
*
53-
* From the point of view of a runtime reflection framework, symbols are akin to [[java.lang.reflect.Member]] from Java
54-
* and [[System.Reflection.MemberInfo]] from .NET. But not only they represent members - they also represent
55-
* classes, objects and even packages.
56-
*
57-
* Also similarly to the base classes in the reflection facilities of JVM and .NET, Scala symbols have subclasses
58-
* that describe particular flavors of definitions. [[scala.reflect.api.Symbols#TermSymbol]] models term definitions
59-
* (such as lazy and eager vals, vars and parameters of methods). Its subclasses are [[scala.reflect.api.Symbols#MethodSymbol]]
60-
* and [[scala.reflect.api.Symbols#ModuleSymbol]] (representing "modules", which in Scala compiler speak mean "objects").
61-
* [[scala.reflect.api.Symbols#TypeSymbol]] along with its subclass [[scala.reflect.api.Symbols#ClassSymbol]]
62-
* describes type definitions in Scala (type aliases, type members, type parameters, classes and traits).
63-
*
64-
* Most reflection APIs that return symbols return non-specific [[scala.reflect.api.Symbols#Symbol]], because upon failure
65-
* they don't raise exceptions, but rather produce `NoSymbol`, a special singleton, which is a null object for symbols.
66-
* Therefore to use such APIs one has to first check whether a callee returned a valid symbol and, if yes, then perform
67-
* a cast using one of the `asTerm`, `asMethod`, `asModule`, `asType` or `asClass` methods. This is arguably inconvenient
68-
* and might be improved in the future.
69-
*
70-
* Unlike [[scala.reflect.api.Trees trees]] and [[scala.reflect.api.Types types]], symbols should not be created directly.
71-
* Instead one should load the symbols from the global symbol table maintained by the compiler.
72-
* To get a symbol that corresponds to a top-level class or object, one can use the `staticClass` and `staticModule`
73-
* methods of [[scala.reflect.api.Mirror]]. To get a symbol that corresponds to a certain member, there are `members`
74-
* and `declarations` methods of [[scala.reflect.api.Types#Type]], which brings the discussion to the next point: type signatures.
75-
*
76-
* Each symbol has a type signature, which describes its type and is available via the `typeSignature` method
77-
* on [[scala.reflect.api.Symbols#Symbol]]. Classes have signatures of the [[scala.reflect.api.Types#ClassInfoType]] type,
78-
* which knows the list of its members and declarations. Modules per se don't have interesting signatures. To access members
79-
* of modules, one first has to obtain a module class (using the `moduleClass` method) and then inspect its signature.
80-
* Members have type signatures of their own: method signatures feature information about parameters and result types,
81-
* type member signatures store upper and lower bounds and so on.
82-
*
83-
* One thing to know about type signatures is that `typeSignature` method always returns signatures in the most generic
84-
* way possible, even if the underlying symbol is obtained from an instantiation of a generic type. For example, signature
85-
* of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
86-
* will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
87-
* with type parameters appropriately instantiated, one should use `typeSignatureIn`.
88-
*
89-
* Symbols are at the heart of the reflection API. Along with the type signatures, which are arguably the most important
90-
* use of reflection, they provide comprehensive information about the underlying definitions. This includes various
91-
* `isXXX` test methods such as `isPublic` or `isFinal`, `params` and `returnType` methods for method symbols,
92-
* `baseClasses` for class symbols and so on. Be prepared - some of these methods don't make sense on the ultimate
93-
* base class Symbol, so they are declared in subclasses.
94-
*
95-
* === Exploring symbols ===
96-
*
97-
* In this example we'll try to get a hold on a symbol that represents the `map` method of `List`,
98-
* and then do something interesting with it.
99-
*
100-
* First of all, to obtain a symbol, one needs to load its enclosing top-level class or module.
101-
* There are two ways of doing that. The first one is getting a symbol by its name using a mirror
102-
* (refer to [[scala.reflect.api.package the reflection overview]] for information about mirrors).
103-
* Another one is getting a type with [[scaa.reflect.api.Types#typeOf]] and using its `typeSymbol` method.
104-
* The second approach is preferable, because it's typesafe, but sometimes it's unavailable.
105-
*
106-
* {{{
107-
* scala> import scala.reflect.runtime.universe._
108-
* import scala.reflect.runtime.universe._
109-
*
110-
* scala> val cm = runtimeMirror(getClass.getClassLoader)
111-
* cm: reflect.runtime.universe.Mirror = JavaMirror with ...
112-
*
113-
* scala> val list = cm.staticClass("scala.List")
114-
* list: reflect.runtime.universe.ClassSymbol = class List
115-
*
116-
* scala> val list = typeOf[List[_]].typeSymbol
117-
* list: reflect.runtime.universe.Symbol = class List
118-
* }}}
119-
*
120-
* Now when the enclosing class is obtained, there's a straight path to getting its member
121-
* using `typeSignature` and `member` methods discussed above:
122-
*
123-
* {{{
124-
* scala> val map = list.typeSignature.member("map": TermName).asMethod
125-
* map: reflect.runtime.universe.MethodSymbol = method map
126-
*
127-
* scala> map.typeSignature
128-
* res0: reflect.runtime.universe.Type = [B, That](f: A => B)(implicit bf:
129-
* scala.collection.generic.CanBuildFrom[Repr,B,That])That
130-
*
131-
* scala> map.typeSignatureIn(typeOf[List[Int]])
132-
* res1: reflect.runtime.universe.Type = [B, That](f: Int => B)(implicit bf:
133-
* scala.collection.generic.CanBuildFrom[List[Int],B,That])That
134-
*
135-
* scala> map.params
136-
* res2: List[List[reflect.runtime.universe.Symbol]] = List(List(value f), List(value bf))
137-
*
138-
* scala> val filter = map.params(0)(0)
139-
* filter: reflect.runtime.universe.Symbol = value f
140-
*
141-
* scala> filter.name
142-
* res3: reflect.runtime.universe.Name = f
143-
*
144-
* scala> filter.typeSignature
145-
* res4: reflect.runtime.universe.Type = A => B
146-
* }}}
147-
*
148-
* === Gotcha #1: Overloaded methods ===
149-
*
150-
* Be careful though, because overloaded methods are represented as instances of TermSymbol
151-
* with multiple `alternatives` that have to be resolved manually. For example, a lookup
152-
* for a member named `mkString` will produce not a MethodSymbol, but a TermSymbol:
153-
*
154-
* {{{
155-
* scala> list.typeSignature.member("mkString": TermName)
156-
* res1: reflect.runtime.universe.Symbol = value mkString
157-
*
158-
* scala> val mkString = list.typeSignature.member("mkString": TermName).asTerm
159-
* mkString: reflect.runtime.universe.TermSymbol = value mkString
160-
*
161-
* scala> mkString.isMethod
162-
* res0: Boolean = false
163-
*
164-
* scala> mkString.alternatives
165-
* res1: List[reflect.runtime.universe.Symbol] = List(method mkString, method mkString, method mkString)
166-
*
167-
* scala> mkString.alternatives foreach println
168-
* method mkString
169-
* method mkString
170-
* method mkString
171-
*
172-
* scala> mkString.alternatives foreach (alt => println(alt.typeSignature))
173-
* => String
174-
* (sep: String)String
175-
* (start: String, sep: String, end: String)String
176-
* }}}
177-
*
178-
* Once one has a symbol, that symbol can be used for reflective invocations. For example,
179-
* having a TermSymbol corresponding to a field it's possible to get or set a value of that field.
180-
* Having a MethodSymbol makes it possible to invoke the corresponding methods. ClassSymbols
181-
* can be instantiated. ModuleSymbols can provide corresponding singleton instances. This is described
182-
* in detail on [[scala.reflect.api.package the reflection overview page]].
183-
*
184-
* === Gotcha #2: Module classes ===
185-
*
186-
* Internally the Scala compiler represents objects with two symbols: a module symbol and a module class symbol.
187-
* The former is a term symbol, used everywhere a module is referenced (e.g. in singleton types or in expressions),
188-
* while the latter is a type symbol, which carries the type signature (i.e. the member list) of the module.
189-
* This implementation detail can be easily seen by compiling a trivial snippet of code. Invoking the Scala
190-
* compiler on `object C` will generate C$.class. That's exactly the module class.
191-
*
192-
* Note that module classes are different from companion classes. Say, for `case class C`, the compiler
193-
* will generate three symbols: `type C`, `term C` and (another one) `type C`, where the first type `C`
194-
* represents the class `C` (which contains auto-generated `copy`, `productPrefix`, `productArity` etc) and
195-
* the second type `C` represents the signature of object `C` (which contains auto-generated factory,
196-
* extractor etc). There won't be any name clashes, because the module class isn't added to the symbol table
197-
* directly and is only available through `<module>.moduleClass`. For the sake of completeness, it is possible
198-
* to go back from a module class to a module via `<module class>.module`.
199-
*
200-
* Separation between modules and module classes is something that we might eliminate in the future, but for now
201-
* this obscure implementation detail has to be taken into account when working with reflection. On the one hand,
202-
* it is necessary to go to a module class to get a list of members for an object. On the other hand, it is
203-
* necessary to go from a module class back to a module to get a singleton instance of an object. The latter
204-
* scenario is described at Stack Overflow: [[http://stackoverflow.com/questions/12128783 How can I get the actual object referred to by Scala 2.10 reflection?]].
16+
* @define SYM_ACCESSORS Class [[Symbol]] defines `isXXX` test methods such as `isPublic` or `isFinal`, `params` and
17+
* `returnType` methods for method symbols, `baseClasses` for class symbols and so on. Some of these methods don't
18+
* make sense for certain subclasses of `Symbol` and return `NoSymbol`, `Nil` or other empty values.
20519
*/
20620
trait Symbols { self: Universe =>
20721

@@ -278,6 +92,8 @@ trait Symbols { self: Universe =>
27892

27993
/** The API of symbols.
28094
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
95+
*
96+
* $SYM_ACCESSORS
28197
*/
28298
trait SymbolApi { this: Symbol =>
28399

@@ -430,8 +246,12 @@ trait Symbols { self: Universe =>
430246
def typeSignatureIn(site: Type): Type
431247

432248
/** The type signature of this symbol.
433-
* Note if the symbol is a member of a class, one almost always is interested
434-
* in `typeSignatureIn` with a site type instead.
249+
*
250+
* This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
251+
* instantiation of a generic type. For example, signature
252+
* of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
253+
* will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
254+
* with type parameters appropriately instantiated, one should use `typeSignatureIn`.
435255
*/
436256
def typeSignature: Type
437257

@@ -579,6 +399,8 @@ trait Symbols { self: Universe =>
579399

580400
/** The API of term symbols.
581401
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
402+
*
403+
* $SYM_ACCESSORS
582404
*/
583405
trait TermSymbolApi extends SymbolApi { this: TermSymbol =>
584406
/** Term symbols have their names of type `TermName`.
@@ -662,6 +484,8 @@ trait Symbols { self: Universe =>
662484

663485
/** The API of type symbols.
664486
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
487+
*
488+
* $SYM_ACCESSORS
665489
*/
666490
trait TypeSymbolApi extends SymbolApi { this: TypeSymbol =>
667491
/** Type symbols have their names of type `TypeName`.
@@ -729,6 +553,8 @@ trait Symbols { self: Universe =>
729553

730554
/** The API of method symbols.
731555
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
556+
*
557+
* $SYM_ACCESSORS
732558
*/
733559
trait MethodSymbolApi extends TermSymbolApi { this: MethodSymbol =>
734560
final override def isMethod = true
@@ -765,6 +591,8 @@ trait Symbols { self: Universe =>
765591

766592
/** The API of module symbols.
767593
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
594+
*
595+
* $SYM_ACCESSORS
768596
*/
769597
trait ModuleSymbolApi extends TermSymbolApi { this: ModuleSymbol =>
770598
/** The class implicitly associated with the object definition.
@@ -780,6 +608,8 @@ trait Symbols { self: Universe =>
780608

781609
/** The API of class symbols.
782610
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
611+
*
612+
* $SYM_ACCESSORS
783613
*/
784614
trait ClassSymbolApi extends TypeSymbolApi { this: ClassSymbol =>
785615
final override def isClass = true
@@ -848,6 +678,8 @@ trait Symbols { self: Universe =>
848678

849679
/** The API of free term symbols.
850680
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
681+
*
682+
* $SYM_ACCESSORS
851683
*/
852684
trait FreeTermSymbolApi extends TermSymbolApi { this: FreeTermSymbol =>
853685
final override def isFreeTerm = true
@@ -862,6 +694,8 @@ trait Symbols { self: Universe =>
862694

863695
/** The API of free type symbols.
864696
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
697+
*
698+
* $SYM_ACCESSORS
865699
*/
866700
trait FreeTypeSymbolApi extends TypeSymbolApi { this: FreeTypeSymbol =>
867701
final override def isFreeType = true

0 commit comments

Comments
 (0)