@@ -87,12 +87,14 @@ takes expressions of type `Expr[T]` to expressions of type `T` and it
8787takes expressions of type ` Type[T] ` to types ` T ` .
8888
8989The two types can be defined in package ` scala.quoted ` as follows:
90+
9091``` scala
9192package scala .quoted
9293
9394sealed abstract class Expr [+ T ]
9495sealed abstract class Type [T ]
9596```
97+
9698Both ` Expr ` and ` Type ` are abstract and sealed, so all constructors for
9799these types are provided by the system. One way to construct values of
98100these types is by quoting, the other is by type-specific lifting
@@ -165,16 +167,19 @@ f2('{2}) // '{ ((x: Int) => x.toString)(2) }
165167One 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) } ` .
166168In some cases we want to remove the lambda from the code, for this we provide the method ` Expr.betaReduce ` that turns a tree
167169describing a function into a function mapping trees to trees.
170+
168171``` scala
169172object Expr {
170173 ...
171174 def betaReduce [...](...)(...): ... = ...
172175}
173176```
177+
174178The definition of ` Expr.betaReduce(f)(x) ` is assumed to be functionally the same as
175179` '{($f)($x)} ` , however it should optimize this call by returning the
176180result of beta-reducing ` f(x) ` if ` f ` is a known lambda expression.
177181` Expr.betaReduce ` distributes applications of ` Expr ` over function arrows:
182+
178183``` scala
179184Expr .betaReduce(_): Expr [(T1 , ..., Tn ) => R ] => ((Expr [T1 ], ..., Expr [Tn ]) => Expr [R ])
180185```
@@ -188,10 +193,12 @@ The resulting value of `Type` will be subject to PCP.
188193Indeed, the definition of ` to ` above uses ` T ` in the next stage, there is a
189194quote but no splice between the parameter binding of ` T ` and its
190195usage. But the code can be rewritten by adding a binding of a ` Type[T] ` tag:
196+
191197``` scala
192198def to [T , R ](f : Expr [T ] => Expr [R ])(using Type [T ], Type [R ], Quotes ): Expr [T => R ] =
193199 ' { (x : T ) => $ { f(' x ) } }
194200```
201+
195202In this version of ` to ` , the type of ` x ` is now the result of
196203splicing the ` Type ` value ` t ` . This operation _ is_ splice correct -- there
197204is 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
222229
223230Consider the following implementation of a staged interpreter that implements
224231a compiler through staging.
232+
225233``` scala
226234import scala .quoted ._
227235
@@ -232,15 +240,19 @@ enum Exp {
232240 case Let (x : String , e : Exp , in : Exp )
233241}
234242```
243+
235244The interpreted language consists of numbers ` Num ` , addition ` Plus ` , and variables
236245` Var ` which are bound by ` Let ` . Here are two sample expressions in the language:
246+
237247``` scala
238248val exp = Plus (Plus (Num (2 ), Var (" x" )), Num (4 ))
239249val letExp = Let (" x" , Num (3 ), exp)
240250```
251+
241252Here’s a compiler that maps an expression given in the interpreted
242253language to quoted Scala code of type ` Expr[Int] ` .
243254The compiler takes an environment that maps variable names to Scala ` Expr ` s.
255+
244256``` scala
245257import scala .quoted ._
246258
@@ -255,17 +267,21 @@ def compile(e: Exp, env: Map[String, Expr[Int]])(using Quotes): Expr[Int] = e ma
255267 ' { val y = $ { compile(e, env) }; $ { compile(body, env + (x -> ' y )) } }
256268}
257269```
270+
258271Running ` compile(letExp, Map()) ` would yield the following Scala code:
272+
259273``` scala
260274' { val y = 3 ; (2 + y) + 4 }
261275```
276+
262277The body of the first clause, ` case Num(n) => Expr(n) ` , looks suspicious. ` n `
263278is declared as an ` Int ` , yet it is converted to an ` Expr[Int] ` with ` Expr() ` .
264279Shouldn’t ` n ` be quoted? In fact this would not
265280work since replacing ` n ` by ` 'n ` in the clause would not be phase
266281correct.
267282
268283The ` Expr.apply ` method is defined in package ` quoted ` :
284+
269285``` scala
270286package quoted
271287
@@ -275,6 +291,7 @@ object Expr {
275291 ...
276292}
277293```
294+
278295This method says that values of types implementing the ` ToExpr ` type class can be
279296converted to ` Expr ` values using ` Expr.apply ` .
280297
@@ -287,15 +304,18 @@ efficiency. But the `ToExpr` instances are nevertheless not _magic_
287304in the sense that they could all be defined in a user program without
288305knowing anything about the representation of ` Expr ` trees. For
289306instance, here is a possible instance of ` ToExpr[Boolean] ` :
307+
290308``` scala
291309given ToExpr [Boolean ] {
292310 def toExpr (b : Boolean ) =
293311 if (b) ' { true } else ' { false }
294312}
295313```
314+
296315Once we can lift bits, we can work our way up. For instance, here is a
297316possible implementation of ` ToExpr[Int] ` that does not use the underlying
298317tree machinery:
318+
299319``` scala
300320given ToExpr [Int ] {
301321 def toExpr (n : Int ) = n match {
@@ -307,28 +327,33 @@ given ToExpr[Int] {
307327 }
308328}
309329```
330+
310331Since ` ToExpr ` is a type class, its instances can be conditional. For example,
311332a ` List ` is liftable if its element type is:
333+
312334``` scala
313335given [T : ToExpr : Type ]: ToExpr [List [T ]] with
314336 def toExpr (xs : List [T ]) = xs match {
315337 case head :: tail => ' { $ { Expr (head) } :: $ { toExpr(tail) } }
316338 case Nil => ' { Nil : List [T ] }
317339 }
318340```
341+
319342In the end, ` ToExpr ` resembles very much a serialization
320343framework. Like the latter it can be derived systematically for all
321344collections, case classes and enums. Note also that the synthesis
322345of _ type-tag_ values of type ` Type[T] ` is essentially the type-level
323346analogue of lifting.
324347
325348Using lifting, we can now give the missing definition of ` showExpr ` in the introductory example:
349+
326350``` scala
327351def showExpr [T ](expr : Expr [T ])(using Quotes ): Expr [String ] = {
328352 val code : String = expr.show
329353 Expr (code)
330354}
331355```
356+
332357That is, the ` showExpr ` method converts its ` Expr ` argument to a string (` code ` ), and lifts
333358the result back to an ` Expr[String] ` using ` Expr.apply ` .
334359
@@ -346,14 +371,18 @@ what to do for references to type parameters or local type definitions
346371that are not defined in the current stage? Here, we cannot construct
347372the ` Type[T] ` tree directly, so we need to get it from a recursive
348373implicit search. For instance, to implement
374+
349375``` scala
350376summon[Type [List [T ]]]
351377```
378+
352379where ` T ` is not defined in the current stage, we construct the type constructor
353380of ` List ` applied to the splice of the result of searching for a given instance for ` Type[T] ` :
381+
354382``` scala
355383' [ List [ $ { summon[Type [T ]] } ] ]
356384```
385+
357386This is exactly the algorithm that Scala 2 uses to search for type tags.
358387In fact Scala 2's type tag feature can be understood as a more ad-hoc version of
359388` quoted.Type ` . As was the case for type tags, the implicit search for a ` quoted.Type `
@@ -386,13 +415,16 @@ object App {
386415 }
387416}
388417```
418+
389419Inlining the ` assert ` function would give the following program:
420+
390421``` scala
391422val program = {
392423 val x = 1
393424 $ { Macros .assertImpl(' { x != 0 ) } }
394425}
395426```
427+
396428The example is only phase correct because ` Macros ` is a global value and
397429as such not subject to phase consistency checking. Conceptually that’s
398430a 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
408440definitions, reflecting the fact that macros have to be defined and
409441compiled before they are used. Hence, conceptually the program part
410442should be treated by the compiler as if it was quoted:
443+
411444``` scala
412445val program = ' {
413446 val x = 1
414447 $ { Macros .assertImpl(' { x != 0 }) }
415448}
416449```
450+
417451If ` program ` is treated as a quoted expression, the call to
418452` Macro.assertImpl ` becomes phase correct even if macro library and
419453program are conceptualized as local definitions.
@@ -438,6 +472,7 @@ expression contains value. Otherwise it will retrun `None` (or emit an error).
438472To avoid having incidental val bindings generated by the inlining of the ` def `
439473it is recommended to use an inline parameter. To illustrate this, consider an
440474implementation of the ` power ` function that makes use of a statically known exponent:
475+
441476``` scala
442477inline def power (x : Double , inline n : Int ) = $ { powerCode(' x , ' n ) }
443478
@@ -482,6 +517,7 @@ that invokation of `run` in splices. Consider the following expression:
482517``` scala
483518' { (x : Int ) => $ { run(' x ); 1 } }
484519```
520+
485521This is again phase correct, but will lead us into trouble. Indeed, evaluating
486522the splice will reduce the expression ` run('x) ` to ` x ` . But then the result
487523
566602```
567603
568604Finally cleanups and dead code elimination:
605+
569606``` scala
570607val arr : Array [Int ] = Array .apply(1 , [2 ,3 : Int ]: Int * )
571608var sum = 0
@@ -637,6 +674,7 @@ It is possible to deconstruct or extract values out of `Expr` using pattern matc
637674- ` 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]] ` .
638675
639676These could be used in the following way to optimize any call to ` sum ` that has statically known values.
677+
640678``` scala
641679inline def sum (inline args : Int * ): Int = $ { sumExpr(' args ) }
642680private 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
659697Patterns ` '{ ... } ` can be placed in any location where Scala expects a pattern.
660698
661699For example
700+
662701``` scala
663702optimize {
664703 sum(sum(1 , a, 2 ), 3 , b)
@@ -746,9 +785,11 @@ then the rest of the quote can refer to this definition.
746785```
747786
748787To 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+
749789``` scala
750790case ' { val y : Int = $x; $body(y): Int } =>
751791```
792+
752793Here ` $x ` will match any closed expression while ` $body(y) ` will match an expression that is closed under ` y ` . Then
753794the 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.
754795
0 commit comments