@@ -87,12 +87,14 @@ takes expressions of type `Expr[T]` to expressions of type `T` and it
87
87
takes expressions of type ` Type[T] ` to types ` T ` .
88
88
89
89
The two types can be defined in package ` scala.quoted ` as follows:
90
+
90
91
``` scala
91
92
package scala .quoted
92
93
93
94
sealed abstract class Expr [+ T ]
94
95
sealed abstract class Type [T ]
95
96
```
97
+
96
98
Both ` Expr ` and ` Type ` are abstract and sealed, so all constructors for
97
99
these types are provided by the system. One way to construct values of
98
100
these types is by quoting, the other is by type-specific lifting
@@ -165,16 +167,19 @@ f2('{2}) // '{ ((x: Int) => x.toString)(2) }
165
167
One limitation of ` from ` is that it does not β-reduce when a lambda is called immediately, as evidenced in the code ` { ((x: Int) => x.toString)(2) } ` .
166
168
In some cases we want to remove the lambda from the code, for this we provide the method ` Expr.betaReduce ` that turns a tree
167
169
describing a function into a function mapping trees to trees.
170
+
168
171
``` scala
169
172
object Expr {
170
173
...
171
174
def betaReduce [...](...)(...): ... = ...
172
175
}
173
176
```
177
+
174
178
The definition of ` Expr.betaReduce(f)(x) ` is assumed to be functionally the same as
175
179
` '{($f)($x)} ` , however it should optimize this call by returning the
176
180
result of beta-reducing ` f(x) ` if ` f ` is a known lambda expression.
177
181
` Expr.betaReduce ` distributes applications of ` Expr ` over function arrows:
182
+
178
183
``` scala
179
184
Expr .betaReduce(_): Expr [(T1 , ..., Tn ) => R ] => ((Expr [T1 ], ..., Expr [Tn ]) => Expr [R ])
180
185
```
@@ -188,10 +193,12 @@ The resulting value of `Type` will be subject to PCP.
188
193
Indeed, the definition of ` to ` above uses ` T ` in the next stage, there is a
189
194
quote but no splice between the parameter binding of ` T ` and its
190
195
usage. But the code can be rewritten by adding a binding of a ` Type[T] ` tag:
196
+
191
197
``` scala
192
198
def to [T , R ](f : Expr [T ] => Expr [R ])(using Type [T ], Type [R ], Quotes ): Expr [T => R ] =
193
199
' { (x : T ) => $ { f(' x ) } }
194
200
```
201
+
195
202
In this version of ` to ` , the type of ` x ` is now the result of
196
203
splicing the ` Type ` value ` t ` . This operation _ is_ splice correct -- there
197
204
is one quote and one splice between the use of ` t ` and its definition.
@@ -222,6 +229,7 @@ phase-correct. If that was not the case, the phase inconsistency for
222
229
223
230
Consider the following implementation of a staged interpreter that implements
224
231
a compiler through staging.
232
+
225
233
``` scala
226
234
import scala .quoted ._
227
235
@@ -232,15 +240,19 @@ enum Exp {
232
240
case Let (x : String , e : Exp , in : Exp )
233
241
}
234
242
```
243
+
235
244
The interpreted language consists of numbers ` Num ` , addition ` Plus ` , and variables
236
245
` Var ` which are bound by ` Let ` . Here are two sample expressions in the language:
246
+
237
247
``` scala
238
248
val exp = Plus (Plus (Num (2 ), Var (" x" )), Num (4 ))
239
249
val letExp = Let (" x" , Num (3 ), exp)
240
250
```
251
+
241
252
Here’s a compiler that maps an expression given in the interpreted
242
253
language to quoted Scala code of type ` Expr[Int] ` .
243
254
The compiler takes an environment that maps variable names to Scala ` Expr ` s.
255
+
244
256
``` scala
245
257
import scala .quoted ._
246
258
@@ -255,17 +267,21 @@ def compile(e: Exp, env: Map[String, Expr[Int]])(using Quotes): Expr[Int] = e ma
255
267
' { val y = $ { compile(e, env) }; $ { compile(body, env + (x -> ' y )) } }
256
268
}
257
269
```
270
+
258
271
Running ` compile(letExp, Map()) ` would yield the following Scala code:
272
+
259
273
``` scala
260
274
' { val y = 3 ; (2 + y) + 4 }
261
275
```
276
+
262
277
The body of the first clause, ` case Num(n) => Expr(n) ` , looks suspicious. ` n `
263
278
is declared as an ` Int ` , yet it is converted to an ` Expr[Int] ` with ` Expr() ` .
264
279
Shouldn’t ` n ` be quoted? In fact this would not
265
280
work since replacing ` n ` by ` 'n ` in the clause would not be phase
266
281
correct.
267
282
268
283
The ` Expr.apply ` method is defined in package ` quoted ` :
284
+
269
285
``` scala
270
286
package quoted
271
287
@@ -275,6 +291,7 @@ object Expr {
275
291
...
276
292
}
277
293
```
294
+
278
295
This method says that values of types implementing the ` ToExpr ` type class can be
279
296
converted to ` Expr ` values using ` Expr.apply ` .
280
297
@@ -287,15 +304,18 @@ efficiency. But the `ToExpr` instances are nevertheless not _magic_
287
304
in the sense that they could all be defined in a user program without
288
305
knowing anything about the representation of ` Expr ` trees. For
289
306
instance, here is a possible instance of ` ToExpr[Boolean] ` :
307
+
290
308
``` scala
291
309
given ToExpr [Boolean ] {
292
310
def toExpr (b : Boolean ) =
293
311
if (b) ' { true } else ' { false }
294
312
}
295
313
```
314
+
296
315
Once we can lift bits, we can work our way up. For instance, here is a
297
316
possible implementation of ` ToExpr[Int] ` that does not use the underlying
298
317
tree machinery:
318
+
299
319
``` scala
300
320
given ToExpr [Int ] {
301
321
def toExpr (n : Int ) = n match {
@@ -307,28 +327,33 @@ given ToExpr[Int] {
307
327
}
308
328
}
309
329
```
330
+
310
331
Since ` ToExpr ` is a type class, its instances can be conditional. For example,
311
332
a ` List ` is liftable if its element type is:
333
+
312
334
``` scala
313
335
given [T : ToExpr : Type ]: ToExpr [List [T ]] with
314
336
def toExpr (xs : List [T ]) = xs match {
315
337
case head :: tail => ' { $ { Expr (head) } :: $ { toExpr(tail) } }
316
338
case Nil => ' { Nil : List [T ] }
317
339
}
318
340
```
341
+
319
342
In the end, ` ToExpr ` resembles very much a serialization
320
343
framework. Like the latter it can be derived systematically for all
321
344
collections, case classes and enums. Note also that the synthesis
322
345
of _ type-tag_ values of type ` Type[T] ` is essentially the type-level
323
346
analogue of lifting.
324
347
325
348
Using lifting, we can now give the missing definition of ` showExpr ` in the introductory example:
349
+
326
350
``` scala
327
351
def showExpr [T ](expr : Expr [T ])(using Quotes ): Expr [String ] = {
328
352
val code : String = expr.show
329
353
Expr (code)
330
354
}
331
355
```
356
+
332
357
That is, the ` showExpr ` method converts its ` Expr ` argument to a string (` code ` ), and lifts
333
358
the result back to an ` Expr[String] ` using ` Expr.apply ` .
334
359
@@ -346,14 +371,18 @@ what to do for references to type parameters or local type definitions
346
371
that are not defined in the current stage? Here, we cannot construct
347
372
the ` Type[T] ` tree directly, so we need to get it from a recursive
348
373
implicit search. For instance, to implement
374
+
349
375
``` scala
350
376
summon[Type [List [T ]]]
351
377
```
378
+
352
379
where ` T ` is not defined in the current stage, we construct the type constructor
353
380
of ` List ` applied to the splice of the result of searching for a given instance for ` Type[T] ` :
381
+
354
382
``` scala
355
383
' [ List [ $ { summon[Type [T ]] } ] ]
356
384
```
385
+
357
386
This is exactly the algorithm that Scala 2 uses to search for type tags.
358
387
In fact Scala 2's type tag feature can be understood as a more ad-hoc version of
359
388
` quoted.Type ` . As was the case for type tags, the implicit search for a ` quoted.Type `
@@ -386,13 +415,16 @@ object App {
386
415
}
387
416
}
388
417
```
418
+
389
419
Inlining the ` assert ` function would give the following program:
420
+
390
421
``` scala
391
422
val program = {
392
423
val x = 1
393
424
$ { Macros .assertImpl(' { x != 0 ) } }
394
425
}
395
426
```
427
+
396
428
The example is only phase correct because ` Macros ` is a global value and
397
429
as such not subject to phase consistency checking. Conceptually that’s
398
430
a bit unsatisfactory. If the PCP is so fundamental, it should be
@@ -408,12 +440,14 @@ macros would be to have the user program be in a phase after the macro
408
440
definitions, reflecting the fact that macros have to be defined and
409
441
compiled before they are used. Hence, conceptually the program part
410
442
should be treated by the compiler as if it was quoted:
443
+
411
444
``` scala
412
445
val program = ' {
413
446
val x = 1
414
447
$ { Macros .assertImpl(' { x != 0 }) }
415
448
}
416
449
```
450
+
417
451
If ` program ` is treated as a quoted expression, the call to
418
452
` Macro.assertImpl ` becomes phase correct even if macro library and
419
453
program are conceptualized as local definitions.
@@ -438,6 +472,7 @@ expression contains value. Otherwise it will retrun `None` (or emit an error).
438
472
To avoid having incidental val bindings generated by the inlining of the ` def `
439
473
it is recommended to use an inline parameter. To illustrate this, consider an
440
474
implementation of the ` power ` function that makes use of a statically known exponent:
475
+
441
476
``` scala
442
477
inline def power (x : Double , inline n : Int ) = $ { powerCode(' x , ' n ) }
443
478
@@ -482,6 +517,7 @@ that invokation of `run` in splices. Consider the following expression:
482
517
``` scala
483
518
' { (x : Int ) => $ { run(' x ); 1 } }
484
519
```
520
+
485
521
This is again phase correct, but will lead us into trouble. Indeed, evaluating
486
522
the splice will reduce the expression ` run('x) ` to ` x ` . But then the result
487
523
566
602
```
567
603
568
604
Finally cleanups and dead code elimination:
605
+
569
606
``` scala
570
607
val arr : Array [Int ] = Array .apply(1 , [2 ,3 : Int ]: Int * )
571
608
var sum = 0
@@ -637,6 +674,7 @@ It is possible to deconstruct or extract values out of `Expr` using pattern matc
637
674
- ` scala.quoted.Varargs ` : matches an explicit sequence of expressions and returns them. These sequences are useful to get individual ` Expr[T] ` out of a varargs expression of type ` Expr[Seq[T]] ` .
638
675
639
676
These could be used in the following way to optimize any call to ` sum ` that has statically known values.
677
+
640
678
``` scala
641
679
inline def sum (inline args : Int * ): Int = $ { sumExpr(' args ) }
642
680
private def sumExpr (argsExpr : Expr [Seq [Int ]])(using Quotes ): Expr [Int ] = argsExpr match {
@@ -659,6 +697,7 @@ Quoted pattens allow deconstructing complex code that contains a precise structu
659
697
Patterns ` '{ ... } ` can be placed in any location where Scala expects a pattern.
660
698
661
699
For example
700
+
662
701
``` scala
663
702
optimize {
664
703
sum(sum(1 , a, 2 ), 3 , b)
@@ -746,9 +785,11 @@ then the rest of the quote can refer to this definition.
746
785
```
747
786
748
787
To match such a term we need to match the definition and the rest of the code, but we need to explicitly state that the rest of the code may refer to this definition.
788
+
749
789
``` scala
750
790
case ' { val y : Int = $x; $body(y): Int } =>
751
791
```
792
+
752
793
Here ` $x ` will match any closed expression while ` $body(y) ` will match an expression that is closed under ` y ` . Then
753
794
the subexpression of type ` Expr[Int] ` is bound to ` body ` as an ` Expr[Int => Int] ` . The extra argument represents the references to ` y ` . Usually this expression is used in combination with ` Expr.betaReduce ` to replace the extra argument.
754
795
0 commit comments