diff options
author | ingoem <ingoem@gmail.com> | 2012-10-09 19:21:17 +0200 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-10-11 20:07:51 +0200 |
commit | f73b7fed37c12e09a278bf0913ed8a01503d5b5d (patch) | |
tree | 2b9b2f5453ac9decded0ccdfcfd848e6d1c5e717 | |
parent | a6d61e2ef8e62b802af606da34002e9b9e9e9b31 (diff) | |
download | scala-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.scala | 230 |
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 |