summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoringoem <ingoem@gmail.com>2012-10-09 19:21:17 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-10-11 20:07:51 +0200
commitf73b7fed37c12e09a278bf0913ed8a01503d5b5d (patch)
tree2b9b2f5453ac9decded0ccdfcfd848e6d1c5e717
parenta6d61e2ef8e62b802af606da34002e9b9e9e9b31 (diff)
downloadscala-f73b7fed37c12e09a278bf0913ed8a01503d5b5d.tar.gz
scala-f73b7fed37c12e09a278bf0913ed8a01503d5b5d.tar.bz2
scala-f73b7fed37c12e09a278bf0913ed8a01503d5b5d.zip
Symbols docs cleanup, mostly moved to guide
-rw-r--r--src/reflect/scala/reflect/api/Symbols.scala230
1 files changed, 32 insertions, 198 deletions
diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala
index 063e12f49c..edb81695f7 100644
--- a/src/reflect/scala/reflect/api/Symbols.scala
+++ b/src/reflect/scala/reflect/api/Symbols.scala
@@ -3,205 +3,19 @@ package api
/** This trait defines symbols and operations on them.
*
- * See the [[docs.scala-lang.org/overviews/reflection/overview.html Reflection Guide]] for a description of symbols
- * and infomation on getting started with Scala reflection API.
+ * Symbols are used to establish bindings between a name and the entity it refers to, such as a class or a method.
+ * Anything you define and can give a name to in Scala has a symbol.
*
- * === Symbols from a compile-time perspective ===
+ * Certain types of tree nodes, such as [[scala.reflect.api.Trees#Ident Ident]] (references to identifiers) and
+ * [[scala.reflect.api.Trees#Select Select]] (references to members) expose method [[Trees.SymTreeApi.symbol]]
+ * to obtain the symbol that represents their declaration.
*
- * Anything you define in Scala has a symbol. If you give something a name, then it has a symbol associated with it.
- * If you didn't give it a name, but you could have, then it has a symbol.
+ * See the [[docs.scala-lang.org/overviews/reflection/overview.html Reflection Guide]] for more details and
+ * intricacies about symbols.
*
- * Symbols are used by the Scala compiler to establish bindings. When typechecking a Scala program,
- * the compiler populates [[scala.reflect.api.Trees#RefTrees ref trees]], such as [[scala.reflect.api.Trees#Ident Ident]]
- * (references to identifiers) and [[scala.reflect.api.Trees#Select Select]] (references to members)
- * with symbols that represent the declarations being referred to. Populating means setting the `symbol`
- * field to a non-empty value.
- *
- * Here's an example of how trees look after the `typer` phase of the Scala compiler (this phase performs the typechecking).
- * {{{
- * >cat Test.scala
- * def foo[T: TypeTag](x: Any) = x.asInstanceOf[T]
- *
- * >scalac -Xprint:typer -uniqid Test.scala
- * [[syntax trees at end of typer]]// Scala source: Test.scala
- * def foo#8339
- * [T#8340 >: Nothing#4658 <: Any#4657]
- * (x#9529: Any#4657)
- * (implicit evidence$1#9530: TypeTag#7861[T#8341])
- * : T#8340 =
- * x#9529.asInstanceOf#6023[T#8341];
- * }}}
- *
- * Shortly put, we write a small snippet and then compile it with scalac, asking the compiler to dump the trees
- * after the typer phase, printing unique ids of the symbols assigned to trees (if any).
- *
- * The resulting printout shows that identifiers have been linked to corresponding definitions.
- * For example, on the one hand, the `ValDef("x", ...)`, which represents the parameter of the method `foo`,
- * defines a method symbol with `id=9529`. On the other hand, the `Ident("x")` in the body of the method
- * got its `symbol` field set to the same symbol, which establishes the binding.
- *
- * In the light of this discussion, it might come as a surprise that the definition of the type parameter `T`
- * has a symbol with `id=8340`, whereas references to this type parameter all have a symbol with `id=8341`.
- * This is the only exception from the general principe outlined above. It happens because the Scala compiler
- * skolemizes type parameters (creates new symbols very similar to the original ones) before entering scopes
- * that define these parameters. This is an advanced feature of Scala, and the knowledge of it is needed only
- * when writing complex macros, but symbols in the macro universe [[scala.reflect.macros.Universe]] have the
- * `deskolemize` method, which goes back from skolems to the originating type parameters.
- *
- * === Symbols from a runtime perspective ===
- *
- * From the point of view of a runtime reflection framework, symbols are akin to [[java.lang.reflect.Member]] from Java
- * and [[System.Reflection.MemberInfo]] from .NET. But not only they represent members - they also represent
- * classes, objects and even packages.
- *
- * Also similarly to the base classes in the reflection facilities of JVM and .NET, Scala symbols have subclasses
- * that describe particular flavors of definitions. [[scala.reflect.api.Symbols#TermSymbol]] models term definitions
- * (such as lazy and eager vals, vars and parameters of methods). Its subclasses are [[scala.reflect.api.Symbols#MethodSymbol]]
- * and [[scala.reflect.api.Symbols#ModuleSymbol]] (representing "modules", which in Scala compiler speak mean "objects").
- * [[scala.reflect.api.Symbols#TypeSymbol]] along with its subclass [[scala.reflect.api.Symbols#ClassSymbol]]
- * describes type definitions in Scala (type aliases, type members, type parameters, classes and traits).
- *
- * Most reflection APIs that return symbols return non-specific [[scala.reflect.api.Symbols#Symbol]], because upon failure
- * they don't raise exceptions, but rather produce `NoSymbol`, a special singleton, which is a null object for symbols.
- * Therefore to use such APIs one has to first check whether a callee returned a valid symbol and, if yes, then perform
- * a cast using one of the `asTerm`, `asMethod`, `asModule`, `asType` or `asClass` methods. This is arguably inconvenient
- * and might be improved in the future.
- *
- * Unlike [[scala.reflect.api.Trees trees]] and [[scala.reflect.api.Types types]], symbols should not be created directly.
- * Instead one should load the symbols from the global symbol table maintained by the compiler.
- * To get a symbol that corresponds to a top-level class or object, one can use the `staticClass` and `staticModule`
- * methods of [[scala.reflect.api.Mirror]]. To get a symbol that corresponds to a certain member, there are `members`
- * and `declarations` methods of [[scala.reflect.api.Types#Type]], which brings the discussion to the next point: type signatures.
- *
- * Each symbol has a type signature, which describes its type and is available via the `typeSignature` method
- * on [[scala.reflect.api.Symbols#Symbol]]. Classes have signatures of the [[scala.reflect.api.Types#ClassInfoType]] type,
- * which knows the list of its members and declarations. Modules per se don't have interesting signatures. To access members
- * of modules, one first has to obtain a module class (using the `moduleClass` method) and then inspect its signature.
- * Members have type signatures of their own: method signatures feature information about parameters and result types,
- * type member signatures store upper and lower bounds and so on.
- *
- * One thing to know about type signatures is that `typeSignature` method always returns signatures in the most generic
- * way possible, even if the underlying symbol is obtained from an instantiation of a generic type. For example, signature
- * of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
- * will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
- * with type parameters appropriately instantiated, one should use `typeSignatureIn`.
- *
- * Symbols are at the heart of the reflection API. Along with the type signatures, which are arguably the most important
- * use of reflection, they provide comprehensive information about the underlying definitions. This includes various
- * `isXXX` test methods such as `isPublic` or `isFinal`, `params` and `returnType` methods for method symbols,
- * `baseClasses` for class symbols and so on. Be prepared - some of these methods don't make sense on the ultimate
- * base class Symbol, so they are declared in subclasses.
- *
- * === Exploring symbols ===
- *
- * In this example we'll try to get a hold on a symbol that represents the `map` method of `List`,
- * and then do something interesting with it.
- *
- * First of all, to obtain a symbol, one needs to load its enclosing top-level class or module.
- * There are two ways of doing that. The first one is getting a symbol by its name using a mirror
- * (refer to [[scala.reflect.api.package the reflection overview]] for information about mirrors).
- * Another one is getting a type with [[scaa.reflect.api.Types#typeOf]] and using its `typeSymbol` method.
- * The second approach is preferable, because it's typesafe, but sometimes it's unavailable.
- *
- * {{{
- * scala> import scala.reflect.runtime.universe._
- * import scala.reflect.runtime.universe._
- *
- * scala> val cm = runtimeMirror(getClass.getClassLoader)
- * cm: reflect.runtime.universe.Mirror = JavaMirror with ...
- *
- * scala> val list = cm.staticClass("scala.List")
- * list: reflect.runtime.universe.ClassSymbol = class List
- *
- * scala> val list = typeOf[List[_]].typeSymbol
- * list: reflect.runtime.universe.Symbol = class List
- * }}}
- *
- * Now when the enclosing class is obtained, there's a straight path to getting its member
- * using `typeSignature` and `member` methods discussed above:
- *
- * {{{
- * scala> val map = list.typeSignature.member("map": TermName).asMethod
- * map: reflect.runtime.universe.MethodSymbol = method map
- *
- * scala> map.typeSignature
- * res0: reflect.runtime.universe.Type = [B, That](f: A => B)(implicit bf:
- * scala.collection.generic.CanBuildFrom[Repr,B,That])That
- *
- * scala> map.typeSignatureIn(typeOf[List[Int]])
- * res1: reflect.runtime.universe.Type = [B, That](f: Int => B)(implicit bf:
- * scala.collection.generic.CanBuildFrom[List[Int],B,That])That
- *
- * scala> map.params
- * res2: List[List[reflect.runtime.universe.Symbol]] = List(List(value f), List(value bf))
- *
- * scala> val filter = map.params(0)(0)
- * filter: reflect.runtime.universe.Symbol = value f
- *
- * scala> filter.name
- * res3: reflect.runtime.universe.Name = f
- *
- * scala> filter.typeSignature
- * res4: reflect.runtime.universe.Type = A => B
- * }}}
- *
- * === Gotcha #1: Overloaded methods ===
- *
- * Be careful though, because overloaded methods are represented as instances of TermSymbol
- * with multiple `alternatives` that have to be resolved manually. For example, a lookup
- * for a member named `mkString` will produce not a MethodSymbol, but a TermSymbol:
- *
- * {{{
- * scala> list.typeSignature.member("mkString": TermName)
- * res1: reflect.runtime.universe.Symbol = value mkString
- *
- * scala> val mkString = list.typeSignature.member("mkString": TermName).asTerm
- * mkString: reflect.runtime.universe.TermSymbol = value mkString
- *
- * scala> mkString.isMethod
- * res0: Boolean = false
- *
- * scala> mkString.alternatives
- * res1: List[reflect.runtime.universe.Symbol] = List(method mkString, method mkString, method mkString)
- *
- * scala> mkString.alternatives foreach println
- * method mkString
- * method mkString
- * method mkString
- *
- * scala> mkString.alternatives foreach (alt => println(alt.typeSignature))
- * => String
- * (sep: String)String
- * (start: String, sep: String, end: String)String
- * }}}
- *
- * Once one has a symbol, that symbol can be used for reflective invocations. For example,
- * having a TermSymbol corresponding to a field it's possible to get or set a value of that field.
- * Having a MethodSymbol makes it possible to invoke the corresponding methods. ClassSymbols
- * can be instantiated. ModuleSymbols can provide corresponding singleton instances. This is described
- * in detail on [[scala.reflect.api.package the reflection overview page]].
- *
- * === Gotcha #2: Module classes ===
- *
- * Internally the Scala compiler represents objects with two symbols: a module symbol and a module class symbol.
- * The former is a term symbol, used everywhere a module is referenced (e.g. in singleton types or in expressions),
- * while the latter is a type symbol, which carries the type signature (i.e. the member list) of the module.
- * This implementation detail can be easily seen by compiling a trivial snippet of code. Invoking the Scala
- * compiler on `object C` will generate C$.class. That's exactly the module class.
- *
- * Note that module classes are different from companion classes. Say, for `case class C`, the compiler
- * will generate three symbols: `type C`, `term C` and (another one) `type C`, where the first type `C`
- * represents the class `C` (which contains auto-generated `copy`, `productPrefix`, `productArity` etc) and
- * the second type `C` represents the signature of object `C` (which contains auto-generated factory,
- * extractor etc). There won't be any name clashes, because the module class isn't added to the symbol table
- * directly and is only available through `<module>.moduleClass`. For the sake of completeness, it is possible
- * to go back from a module class to a module via `<module class>.module`.
- *
- * Separation between modules and module classes is something that we might eliminate in the future, but for now
- * this obscure implementation detail has to be taken into account when working with reflection. On the one hand,
- * it is necessary to go to a module class to get a list of members for an object. On the other hand, it is
- * necessary to go from a module class back to a module to get a singleton instance of an object. The latter
- * 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?]].
+ * @define SYM_ACCESSORS Class [[Symbol]] defines `isXXX` test methods such as `isPublic` or `isFinal`, `params` and
+ * `returnType` methods for method symbols, `baseClasses` for class symbols and so on. Some of these methods don't
+ * make sense for certain subclasses of `Symbol` and return `NoSymbol`, `Nil` or other empty values.
*/
trait Symbols { self: Universe =>
@@ -278,6 +92,8 @@ trait Symbols { self: Universe =>
/** The API of symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait SymbolApi { this: Symbol =>
@@ -430,8 +246,12 @@ trait Symbols { self: Universe =>
def typeSignatureIn(site: Type): Type
/** The type signature of this symbol.
- * Note if the symbol is a member of a class, one almost always is interested
- * in `typeSignatureIn` with a site type instead.
+ *
+ * This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
+ * instantiation of a generic type. For example, signature
+ * of the method `def map[B](f: (A) ⇒ B): List[B]`, which refers to the type parameter `A` of the declaring class `List[A]`,
+ * will always feature `A`, regardless of whether `map` is loaded from the `List[_]` or from `List[Int]`. To get a signature
+ * with type parameters appropriately instantiated, one should use `typeSignatureIn`.
*/
def typeSignature: Type
@@ -579,6 +399,8 @@ trait Symbols { self: Universe =>
/** The API of term symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait TermSymbolApi extends SymbolApi { this: TermSymbol =>
/** Term symbols have their names of type `TermName`.
@@ -662,6 +484,8 @@ trait Symbols { self: Universe =>
/** The API of type symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait TypeSymbolApi extends SymbolApi { this: TypeSymbol =>
/** Type symbols have their names of type `TypeName`.
@@ -729,6 +553,8 @@ trait Symbols { self: Universe =>
/** The API of method symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait MethodSymbolApi extends TermSymbolApi { this: MethodSymbol =>
final override def isMethod = true
@@ -766,6 +592,8 @@ trait Symbols { self: Universe =>
/** The API of module symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait ModuleSymbolApi extends TermSymbolApi { this: ModuleSymbol =>
/** The class implicitly associated with the object definition.
@@ -781,6 +609,8 @@ trait Symbols { self: Universe =>
/** The API of class symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait ClassSymbolApi extends TypeSymbolApi { this: ClassSymbol =>
final override def isClass = true
@@ -849,6 +679,8 @@ trait Symbols { self: Universe =>
/** The API of free term symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait FreeTermSymbolApi extends TermSymbolApi { this: FreeTermSymbol =>
final override def isFreeTerm = true
@@ -863,6 +695,8 @@ trait Symbols { self: Universe =>
/** The API of free type symbols.
* The main source of information about symbols is the [[scala.reflect.api.Symbols]] page.
+ *
+ * $SYM_ACCESSORS
*/
trait FreeTypeSymbolApi extends TypeSymbolApi { this: FreeTypeSymbol =>
final override def isFreeType = true