Skip to content

Commit 6fbc7e5

Browse files
committed
addressed comments
1 parent 35f7f97 commit 6fbc7e5

File tree

1 file changed

+41
-0
lines changed

1 file changed

+41
-0
lines changed

docs/docs/reference/metaprogramming/macros.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,14 @@ takes expressions of type `Expr[T]` to expressions of type `T` and it
8787
takes expressions of type `Type[T]` to types `T`.
8888

8989
The two types can be defined in package `scala.quoted` as follows:
90+
9091
```scala
9192
package scala.quoted
9293

9394
sealed abstract class Expr[+T]
9495
sealed abstract class Type[T]
9596
```
97+
9698
Both `Expr` and `Type` are abstract and sealed, so all constructors for
9799
these types are provided by the system. One way to construct values of
98100
these types is by quoting, the other is by type-specific lifting
@@ -165,16 +167,19 @@ f2('{2}) // '{ ((x: Int) => x.toString)(2) }
165167
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) }`.
166168
In some cases we want to remove the lambda from the code, for this we provide the method `Expr.betaReduce` that turns a tree
167169
describing a function into a function mapping trees to trees.
170+
168171
```scala
169172
object Expr {
170173
...
171174
def betaReduce[...](...)(...): ... = ...
172175
}
173176
```
177+
174178
The 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
176180
result 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
179184
Expr.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.
188193
Indeed, the definition of `to` above uses `T` in the next stage, there is a
189194
quote but no splice between the parameter binding of `T` and its
190195
usage. But the code can be rewritten by adding a binding of a `Type[T]` tag:
196+
191197
```scala
192198
def to[T, R](f: Expr[T] => Expr[R])(using Type[T], Type[R], Quotes): Expr[T => R] =
193199
'{ (x: T) => ${ f('x) } }
194200
```
201+
195202
In this version of `to`, the type of `x` is now the result of
196203
splicing the `Type` value `t`. This operation _is_ splice correct -- there
197204
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
222229

223230
Consider the following implementation of a staged interpreter that implements
224231
a compiler through staging.
232+
225233
```scala
226234
import scala.quoted._
227235

@@ -232,15 +240,19 @@ enum Exp {
232240
case Let(x: String, e: Exp, in: Exp)
233241
}
234242
```
243+
235244
The 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
238248
val exp = Plus(Plus(Num(2), Var("x")), Num(4))
239249
val letExp = Let("x", Num(3), exp)
240250
```
251+
241252
Here’s a compiler that maps an expression given in the interpreted
242253
language to quoted Scala code of type `Expr[Int]`.
243254
The compiler takes an environment that maps variable names to Scala `Expr`s.
255+
244256
```scala
245257
import 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+
258271
Running `compile(letExp, Map())` would yield the following Scala code:
272+
259273
```scala
260274
'{ val y = 3; (2 + y) + 4 }
261275
```
276+
262277
The body of the first clause, `case Num(n) => Expr(n)`, looks suspicious. `n`
263278
is declared as an `Int`, yet it is converted to an `Expr[Int]` with `Expr()`.
264279
Shouldn’t `n` be quoted? In fact this would not
265280
work since replacing `n` by `'n` in the clause would not be phase
266281
correct.
267282

268283
The `Expr.apply` method is defined in package `quoted`:
284+
269285
```scala
270286
package quoted
271287

@@ -275,6 +291,7 @@ object Expr {
275291
...
276292
}
277293
```
294+
278295
This method says that values of types implementing the `ToExpr` type class can be
279296
converted to `Expr` values using `Expr.apply`.
280297

@@ -287,15 +304,18 @@ efficiency. But the `ToExpr` instances are nevertheless not _magic_
287304
in the sense that they could all be defined in a user program without
288305
knowing anything about the representation of `Expr` trees. For
289306
instance, here is a possible instance of `ToExpr[Boolean]`:
307+
290308
```scala
291309
given ToExpr[Boolean] {
292310
def toExpr(b: Boolean) =
293311
if (b) '{ true } else '{ false }
294312
}
295313
```
314+
296315
Once we can lift bits, we can work our way up. For instance, here is a
297316
possible implementation of `ToExpr[Int]` that does not use the underlying
298317
tree machinery:
318+
299319
```scala
300320
given ToExpr[Int] {
301321
def toExpr(n: Int) = n match {
@@ -307,28 +327,33 @@ given ToExpr[Int] {
307327
}
308328
}
309329
```
330+
310331
Since `ToExpr` is a type class, its instances can be conditional. For example,
311332
a `List` is liftable if its element type is:
333+
312334
```scala
313335
given [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+
319342
In the end, `ToExpr` resembles very much a serialization
320343
framework. Like the latter it can be derived systematically for all
321344
collections, case classes and enums. Note also that the synthesis
322345
of _type-tag_ values of type `Type[T]` is essentially the type-level
323346
analogue of lifting.
324347

325348
Using lifting, we can now give the missing definition of `showExpr` in the introductory example:
349+
326350
```scala
327351
def showExpr[T](expr: Expr[T])(using Quotes): Expr[String] = {
328352
val code: String = expr.show
329353
Expr(code)
330354
}
331355
```
356+
332357
That is, the `showExpr` method converts its `Expr` argument to a string (`code`), and lifts
333358
the 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
346371
that are not defined in the current stage? Here, we cannot construct
347372
the `Type[T]` tree directly, so we need to get it from a recursive
348373
implicit search. For instance, to implement
374+
349375
```scala
350376
summon[Type[List[T]]]
351377
```
378+
352379
where `T` is not defined in the current stage, we construct the type constructor
353380
of `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+
357386
This is exactly the algorithm that Scala 2 uses to search for type tags.
358387
In 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+
389419
Inlining the `assert` function would give the following program:
420+
390421
```scala
391422
val program = {
392423
val x = 1
393424
${ Macros.assertImpl('{ x != 0) } }
394425
}
395426
```
427+
396428
The example is only phase correct because `Macros` is a global value and
397429
as such not subject to phase consistency checking. Conceptually that’s
398430
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
408440
definitions, reflecting the fact that macros have to be defined and
409441
compiled before they are used. Hence, conceptually the program part
410442
should be treated by the compiler as if it was quoted:
443+
411444
```scala
412445
val program = '{
413446
val x = 1
414447
${ Macros.assertImpl('{ x != 0 }) }
415448
}
416449
```
450+
417451
If `program` is treated as a quoted expression, the call to
418452
`Macro.assertImpl` becomes phase correct even if macro library and
419453
program are conceptualized as local definitions.
@@ -438,6 +472,7 @@ expression contains value. Otherwise it will retrun `None` (or emit an error).
438472
To avoid having incidental val bindings generated by the inlining of the `def`
439473
it is recommended to use an inline parameter. To illustrate this, consider an
440474
implementation of the `power` function that makes use of a statically known exponent:
475+
441476
```scala
442477
inline 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+
485521
This is again phase correct, but will lead us into trouble. Indeed, evaluating
486522
the splice will reduce the expression `run('x)` to `x`. But then the result
487523

@@ -566,6 +602,7 @@ sum
566602
```
567603

568604
Finally cleanups and dead code elimination:
605+
569606
```scala
570607
val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*)
571608
var 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

639676
These could be used in the following way to optimize any call to `sum` that has statically known values.
677+
640678
```scala
641679
inline def sum(inline args: Int*): Int = ${ sumExpr('args) }
642680
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
659697
Patterns `'{ ... }` can be placed in any location where Scala expects a pattern.
660698

661699
For example
700+
662701
```scala
663702
optimize {
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

748787
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+
749789
```scala
750790
case '{ val y: Int = $x; $body(y): Int } =>
751791
```
792+
752793
Here `$x` will match any closed expression while `$body(y)` will match an expression that is closed under `y`. Then
753794
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.
754795

0 commit comments

Comments
 (0)