This proposal is outdated. Please, refer to the updated version at google docs and use Scala 2.10.0-M4 or later to see it in action.
Scala's reflection should achieve the analogue of Java's reflection, but with Scala's full types.
I.e., there should be a way to
- get the Scala runtime class from an instance,
- obtain Scala members from Scala classes, which carry their original Scala types,
- dereference Scala fields, and invoke Scala methods,
- query Scala types of these members, for subtype relationships and others.
In addition we want integration into manifests and lifted code:
- the type descriptor in a manifest should be a reflect.Type.
- the tree lifted in a CodeT expression should be a reflect.Tree.
From all this it is clear that the reflection library needs to use a lot of logic from the compiler. It uses isomorphic trees, types, and symbols, needs the same mechanism to deserialize pickle information, needs the same logic for subtype tests, as-seen-from, find-members, etc. It would be a shame to have to duplicate that logic. At the same time, the compiler also has many operations that do not make sense in a reflective setting. For instance, there are operations dealing with compiler phases, or source trees. Also, the number and complexity of compiler operations makes them unsuitable for a general library interface.
So, we need to re-use a lot of logic from the compiler, but need to hide it behind sparser and cleaner interfaces. I now present a design that achieves this.
The idea is to pack most logic into two packages:
reflect.internalcontains the implementations
reflect.apicontains the public interfaces.
(Question: Better name for reflect.api? reflect.public would be nice but that makes it inaccessible from Java).
Classes in both packages are arranged with the cake pattern, with a "Universe" class mixing in container traits such as Types, Symbols, Names, Trees, Positions, which contain the actual types and associated operations. reflect.internal classes inherit from their corresponding reflect.api classes. The reflect.api classes are mostly pure interfaces which contain abstract types and extractors. Here's an example: In reflect.internal there is a Symbols trait that
contains all symbol types. Its outline is as follows:
the API class abstracts Symbol into an abstract type:
If the concrete class is a case class we also add an extractor to the API trait.
We still need a concrete instantiation of a universe. This is done as follows. First, define
a class RunTimeUniverse that extends the internal universe (which is called SymbolTable).
Finally, define a concrete universe in reflect as follows:
reflect.universe inherits all mechanisms from reflect.internal.SymbolTable. But its exposed interface is just
reflect.api.Universe The reflect package object also defines convenient type aliases for Types, Symbols etc that all refer to the universe member types.
The main questions in this architecture concern the role of trees. We could treat them like the other types, i.e. abstract out all tree types with abstract types and all tree pattern matchings with extractors. But that would give us >40 abstract types and extractors, and pattern matching on abstract trees would become slow. At the same time, unlike Symbols and Types, there's hardly anything to hide in trees. They are all pretty canonical with very few extraneous methods and no extraneous fields.
So I think it would be easiest if the case class definitions of all trees were made concrete in reflect.api. (they would still refer to abstract Symbols, Types, Names, Positions). Compiler specific methods could be taken out of Trees and put in an implicit wrapper class.
There's only one thing that needs cleaning up: Currently trees display themselves via toString as source programs. This is convenient for composing error messages and compiler diagnostics. But it would be more canonical to have them represented as plain case classes in reflect.api. This means there should be some other method in the compiler that applies a treePrinter to a tree, and toString on a tree should expose the actual case classes. I think that is altogether a better design.
How to get there? reflect.internal is already done. reflect.api would resemble largely the current reflect.generic, but would drop all concrete implementations which were there so that we could do unpickling. Unpickling will be done like all other operations in reflect.internal. reflect.internal traits would now inherit from reflect.api (they did not use to inherit from reflect.generic).
- Access to named/default values of parameters
- Transparent handling of boxed/unboxed primitives
- Interop with Java reflection
- "collapsing" of type aliases
- Discovery of nested/inner objects and classes (also within defs?)
- Correct handling of dependent types
- Memory leaks from reflection wrappers held after they're needed