@@ -72,13 +72,18 @@ The two types can be defined in package `scala.quoted` as follows:
72
72
73
73
package scala.quoted
74
74
75
- abstract class Expr[T] {
75
+ sealed abstract class Expr[T] {
76
76
def unary_~: T // splice operation
77
77
}
78
- class Type[T] {
78
+ sealed abstract class Type[T] {
79
79
type unary_~ = T // splice type
80
80
}
81
81
82
+ Both ` Expr ` and ` Type ` are abstract and sealed, so all constructors for
83
+ these types are provided by the system. One way to construct values of
84
+ these types is by quoting, the other is by type-specific lifting
85
+ operations that will be discussed later on.
86
+
82
87
### The Phase Consistency Principle
83
88
84
89
A fundamental * phase consistency principle* (PCP) regulates accesses
@@ -163,14 +168,57 @@ quote but no splice between the parameter binding of `T` and its
163
168
usage. But the code can be made phase correct by adding a binding
164
169
of a ` Type[T] ` tag:
165
170
166
- def reflect[T, U](f: Expr[T] => Expr[U]): Expr[T => U] = {
167
- val Ttag = new Type[T]
168
- ’{ (x: ~Ttag) => ~f(’(x))
169
- }
171
+ def reflect[T, U](f: Expr[T] => Expr[U])(implicit t: Type[T]): Expr[T => U] =
172
+ ’{ (x: ~t) => ~f(’(x))
173
+
174
+ In this version of ` reflect ` , the type of ` x ` is now the result of
175
+ splicing the ` Type ` value ` t ` . This operation _ is_ splice correct -- there
176
+ is one quote and one splice between the use of ` t ` and its definition.
177
+
178
+ To avoid clutter, the Scala implementation tries to convert any phase-incorrect
179
+ reference to a type ` T ` to a type-splice, by rewriting ` T ` to ` ~implicitly[Type[T]] ` .
180
+ For instance, the user-level definition of ` reflect `
181
+
182
+ def reflect[T: Type, U](f: Expr[T] => Expr[U]) Expr[T => U] =
183
+ ’{ (x: T) => ~f(’(x)) }
184
+
185
+ would be rewritten to
186
+
187
+ def reflect[T: Type, U](f: Expr[T] => Expr[U]) Expr[T => U] =
188
+ ’{ (x: ~implicitly[Type[T]]) => ~f(’(x)) }
189
+
190
+ The ` implicitly ` query succeeds because there is an implicit value of
191
+ type ` Type[T] ` available (namely the evidence parameter corresponding
192
+ to the context bound ` : Type ` ), and the reference to that value is
193
+ phase-correct. If that was not the case, the phase inconsistency for
194
+ ` T ` would be reported as an error.
195
+
196
+ ### Lifting Types
197
+
198
+ The previous section has shown that the metaprogramming framework has
199
+ to be able to take a type ` T ` and convert it to a type tree of type
200
+ ` Type[T] ` that can be reified. This means that all free variables of
201
+ the type tree refer to types and values defined in the current stage.
202
+
203
+ For a reference to a global class, this is easy: Just issue the fully
204
+ qualified name of the class. Members of reifiable types are handled by
205
+ just reifying the containing type together with the member name. But
206
+ what to do for references to type parameters or local type definitions
207
+ that are not defined in the current stage? Here, we cannot construct
208
+ the ` Type[T] ` tree directly, so we need to get it from a recursive
209
+ implicit search. For instance, to implemenent
210
+
211
+ implicitly[Type[List[T]]]
212
+
213
+ where ` T ` is not defined in the current stage, we construct the type constructor
214
+ of ` List ` applied to the splice of the result of searching for an implicit ` Type[T] ` :
215
+
216
+ '[List[~implicitly[Type[T]]]]
170
217
171
- To avoid clutter, the Scala implementation will add these tags
172
- automatically in the case of a PCP violation involving types. As a consequence,
173
- types can be effectively ignored for phase consistency checking.
218
+ This is in exactly the algorithm that Scala 2 uses to search for type tags.
219
+ In fact Scala 2's type tag feature can be understood as a more ad-hoc version of
220
+ ` quoted.Type ` . As was the case for type tags, the implicit search for a ` quoted.Type `
221
+ is handled by the compiler, using the algorithm sketched above.
174
222
175
223
### Example Expansion
176
224
@@ -529,7 +577,9 @@ a `List` is liftable if its element type is:
529
577
530
578
In the end, ` Liftable ` resembles very much a serialization
531
579
framework. Like the latter it can be derived systematically for all
532
- collections, case classes and enums.
580
+ collections, case classes and enums. Note also that the implicit synthesis
581
+ of "type-tag" values of type ` Type[T] ` is essentially the type-level
582
+ analogue of lifting.
533
583
534
584
Using lifting, we can now give the missing definition of ` showExpr ` in the introductory example:
535
585
0 commit comments