0
# Macro and Metaprogramming
1
2
The `scala.quoted` package provides Scala 3's powerful macro system using quotes and splices for compile-time code generation and analysis.
3
4
## Core Types
5
6
### Quotes Context
7
8
```scala { .api }
9
trait Quotes:
10
// Provides access to compiler internals and reflection API
11
type reflect: Reflection
12
```
13
14
The main context object providing access to the compiler's reflection API and utilities needed for macro programming.
15
16
### Quoted Expressions
17
18
```scala { .api }
19
abstract class Expr[+T]:
20
def show(using Quotes): String
21
def value(using Quotes): T // Only works for constant expressions
22
def asTerm(using Quotes): reflect.Term
23
```
24
25
Represents quoted expressions that can be spliced into code. `Expr[T]` contains code that, when executed, produces a value of type `T`.
26
27
### Quoted Types
28
29
```scala { .api }
30
abstract class Type[T <: AnyKind]:
31
def show(using Quotes): String
32
def asTerm(using Quotes): reflect.Term
33
```
34
35
Represents quoted types that can be used in type-level computations and spliced into contexts expecting types.
36
37
## Conversion Type Classes
38
39
### ToExpr - Value to Expression
40
41
```scala { .api }
42
trait ToExpr[T]:
43
def apply(x: T)(using Quotes): Expr[T]
44
45
object ToExpr:
46
given ToExpr[Boolean]
47
given ToExpr[Byte]
48
given ToExpr[Short]
49
given ToExpr[Int]
50
given ToExpr[Long]
51
given ToExpr[Float]
52
given ToExpr[Double]
53
given ToExpr[Char]
54
given ToExpr[String]
55
given [T: ToExpr]: ToExpr[List[T]]
56
given [T: ToExpr]: ToExpr[Seq[T]]
57
given [T: ToExpr]: ToExpr[Option[T]]
58
// ... and many more standard types
59
```
60
61
Type class for converting runtime values to quoted expressions.
62
63
### FromExpr - Expression to Value
64
65
```scala { .api }
66
trait FromExpr[T]:
67
def apply(expr: Expr[T])(using Quotes): Option[T]
68
69
object FromExpr:
70
given FromExpr[Boolean]
71
given FromExpr[Byte]
72
given FromExpr[Short]
73
given FromExpr[Int]
74
given FromExpr[Long]
75
given FromExpr[Float]
76
given FromExpr[Double]
77
given FromExpr[Char]
78
given FromExpr[String]
79
given [T: FromExpr]: FromExpr[List[T]]
80
given [T: FromExpr]: FromExpr[Option[T]]
81
// ... and many more standard types
82
```
83
84
Type class for extracting constant values from quoted expressions (when possible).
85
86
## Utility Types
87
88
### Variable Arguments
89
90
```scala { .api }
91
abstract class Varargs[T]:
92
def apply(using Quotes): Seq[Expr[T]]
93
```
94
95
Handles variable arguments in macro contexts, providing access to a sequence of expressions.
96
97
### Expression Transformation
98
99
```scala { .api }
100
class ExprMap(using Quotes):
101
def apply[T](expr: Expr[T]): Expr[T]
102
def transformChildren[T](expr: Expr[T]): Expr[T]
103
```
104
105
Utility class for recursively transforming expressions in macro code.
106
107
## Quote and Splice Syntax
108
109
### Basic Quote/Splice
110
111
```scala
112
import scala.quoted.*
113
114
// Quoting expressions - creates Expr[T]
115
'{ 1 + 2 } // Expr[Int]
116
'{ "hello" + "world" } // Expr[String]
117
'{ List(1, 2, 3) } // Expr[List[Int]]
118
119
// Splicing expressions - injects Expr[T] into quoted context
120
val expr: Expr[Int] = '{ 42 }
121
'{ $expr + 1 } // Expr[Int] representing 42 + 1
122
123
// Quoting types - creates Type[T]
124
'[Int] // Type[Int]
125
'[List[String]] // Type[List[String]]
126
```
127
128
### Nested Quotes and Splices
129
130
```scala
131
def makeAddition(x: Expr[Int], y: Expr[Int])(using Quotes): Expr[Int] =
132
'{ $x + $y }
133
134
def makeList[T: Type](elements: Expr[T]*)(using Quotes): Expr[List[T]] =
135
'{ List(${ Varargs(elements.toSeq) }*) }
136
```
137
138
## Usage Examples
139
140
### Simple Macro Definition
141
142
```scala
143
import scala.quoted.*
144
145
inline def debug[T](inline x: T): T =
146
${ debugImpl('x) }
147
148
def debugImpl[T: Type](x: Expr[T])(using Quotes): Expr[T] =
149
val code = x.show
150
'{
151
println(s"Debug: ${ Expr(code) } = ${$x}")
152
$x
153
}
154
155
// Usage
156
val result = debug(2 + 3 * 4)
157
// Prints: Debug: 2.+(3.*(4)) = 14
158
// Returns: 14
159
```
160
161
### Conditional Code Generation
162
163
```scala
164
import scala.quoted.*
165
166
inline def optimizedOperation[T](inline x: T, inline y: T): T =
167
${ optimizedOperationImpl('x, 'y) }
168
169
def optimizedOperationImpl[T: Type](x: Expr[T], y: Expr[T])(using Quotes): Expr[T] =
170
(x.value, y.value) match
171
case (Some(0), _) => y // 0 + y = y
172
case (_, Some(0)) => x // x + 0 = x
173
case (Some(a), Some(b)) => Expr(a + b) // Constant folding
174
case _ => '{ $x + $y } // Runtime addition
175
176
val result1 = optimizedOperation(0, 42) // Optimized to just 42
177
val result2 = optimizedOperation(10, 20) // Optimized to 30
178
val result3 = optimizedOperation(x, y) // Runtime addition
179
```
180
181
### Working with Types
182
183
```scala
184
import scala.quoted.*
185
186
inline def typeInfo[T]: String =
187
${ typeInfoImpl[T] }
188
189
def typeInfoImpl[T: Type](using Quotes): Expr[String] =
190
import quotes.reflect.*
191
192
val tpe = TypeRepr.of[T]
193
val typeName = tpe.show
194
val isGeneric = tpe.typeArgs.nonEmpty
195
val info = s"Type: $typeName, Generic: $isGeneric"
196
197
Expr(info)
198
199
val info1 = typeInfo[Int] // "Type: Int, Generic: false"
200
val info2 = typeInfo[List[String]] // "Type: List[String], Generic: true"
201
```
202
203
### Expression Analysis
204
205
```scala
206
import scala.quoted.*
207
208
inline def analyzeExpression[T](inline expr: T): String =
209
${ analyzeExpressionImpl('expr) }
210
211
def analyzeExpressionImpl[T: Type](expr: Expr[T])(using Quotes): Expr[String] =
212
import quotes.reflect.*
213
214
val term = expr.asTerm
215
val analysis = term match
216
case Literal(constant) => s"Literal: ${constant.show}"
217
case Ident(name) => s"Identifier: $name"
218
case Apply(fun, args) => s"Application: ${fun.show} with ${args.length} arguments"
219
case Select(qualifier, name) => s"Selection: $name from ${qualifier.show}"
220
case _ => s"Complex expression: ${term.show}"
221
222
Expr(analysis)
223
224
val analysis1 = analyzeExpression(42) // "Literal: 42"
225
val analysis2 = analyzeExpression("hello".length) // Complex analysis
226
val analysis3 = analyzeExpression(List(1, 2, 3)) // "Application: List with 3 arguments"
227
```
228
229
### Code Transformation
230
231
```scala
232
import scala.quoted.*
233
234
inline def logCalls[T](inline expr: T): T =
235
${ logCallsImpl('expr) }
236
237
def logCallsImpl[T: Type](expr: Expr[T])(using Quotes): Expr[T] =
238
import quotes.reflect.*
239
240
def transform(term: Term): Term = term match
241
case Apply(fun, args) =>
242
val transformedArgs = args.map(transform)
243
val loggedCall = '{
244
println(s"Calling: ${${Expr(fun.show)}}")
245
${Apply(fun, transformedArgs).asExprOf[Any]}
246
}.asTerm
247
loggedCall
248
case _ => term.changeOwner(Symbol.spliceOwner)
249
250
transform(expr.asTerm).asExprOf[T]
251
252
// Usage logs all method calls
253
val result = logCalls {
254
val x = List(1, 2, 3)
255
x.map(_ + 1).filter(_ > 2)
256
}
257
```
258
259
### Compile-Time Validation
260
261
```scala
262
import scala.quoted.*
263
264
inline def validateJson(inline json: String): String =
265
${ validateJsonImpl('json) }
266
267
def validateJsonImpl(json: Expr[String])(using Quotes): Expr[String] =
268
json.value match
269
case Some(jsonString) =>
270
// Validate JSON at compile time
271
try
272
// Assuming some JSON parser
273
parseJson(jsonString) // Throws if invalid
274
json
275
catch
276
case e: Exception =>
277
quotes.reflect.report.errorAndAbort(s"Invalid JSON: ${e.getMessage}")
278
case None =>
279
quotes.reflect.report.errorAndAbort("JSON string must be a compile-time constant")
280
281
val validJson = validateJson("""{"name": "Alice", "age": 30}""") // Compiles
282
// val invalidJson = validateJson("""{"name": "Alice", "age":}""") // Compile error
283
```
284
285
### Generic Macro Programming
286
287
```scala
288
import scala.quoted.*
289
290
inline def showStructure[T](inline value: T): String =
291
${ showStructureImpl('value) }
292
293
def showStructureImpl[T: Type](value: Expr[T])(using Quotes): Expr[String] =
294
import quotes.reflect.*
295
296
def analyzeType(tpe: TypeRepr): String = tpe match
297
case AppliedType(base, args) =>
298
s"${base.show}[${args.map(analyzeType).mkString(", ")}]"
299
case _ => tpe.show
300
301
def analyzeTerm(term: Term): String = term match
302
case Literal(constant) => s"Literal(${constant.show})"
303
case Ident(name) => s"Ident($name)"
304
case Apply(fun, args) =>
305
s"Apply(${analyzeTerm(fun)}, [${args.map(analyzeTerm).mkString(", ")}])"
306
case Select(qualifier, name) =>
307
s"Select(${analyzeTerm(qualifier)}, $name)"
308
case _ => s"Term(${term.show})"
309
310
val typeAnalysis = analyzeType(TypeRepr.of[T])
311
val termAnalysis = analyzeTerm(value.asTerm)
312
313
Expr(s"Type: $typeAnalysis, Structure: $termAnalysis")
314
315
val structure = showStructure(List(1, 2, 3).map(_ + 1))
316
// Detailed compile-time analysis of expression structure
317
```
318
319
### Macro with Multiple Expression Arguments
320
321
```scala
322
import scala.quoted.*
323
324
inline def benchmark[T](inline iterations: Int, inline code: T): (T, Long) =
325
${ benchmarkImpl('iterations, 'code) }
326
327
def benchmarkImpl[T: Type](iterations: Expr[Int], code: Expr[T])(using Quotes): Expr[(T, Long)] =
328
'{
329
val startTime = System.nanoTime()
330
var result: T = null.asInstanceOf[T]
331
var i = 0
332
while i < $iterations do
333
result = $code
334
i += 1
335
val endTime = System.nanoTime()
336
(result, endTime - startTime)
337
}
338
339
val (result, timeNanos) = benchmark(1000000, math.sqrt(42.0))
340
println(s"Result: $result, Time: ${timeNanos}ns")
341
```
342
343
### Advanced Type Manipulation
344
345
```scala
346
import scala.quoted.*
347
348
inline def createTuple[T, U](x: T, y: U): (T, U) =
349
${ createTupleImpl('x, 'y) }
350
351
def createTupleImpl[T: Type, U: Type](x: Expr[T], y: Expr[U])(using Quotes): Expr[(T, U)] =
352
import quotes.reflect.*
353
354
// Get type representations
355
val tType = TypeRepr.of[T]
356
val uType = TypeRepr.of[U]
357
358
// Create tuple type
359
val tupleType = AppliedType(TypeRepr.of[(_, _)].typeSymbol.typeRef, List(tType, uType))
360
361
'{ ($x, $y) }
362
363
// Advanced: Generate code based on type structure
364
inline def processType[T]: String =
365
${ processTypeImpl[T] }
366
367
def processTypeImpl[T: Type](using Quotes): Expr[String] =
368
import quotes.reflect.*
369
370
val tpe = TypeRepr.of[T]
371
val processing = tpe match
372
case AppliedType(base, List(arg)) if base <:< TypeRepr.of[List[_]] =>
373
s"List processing for element type: ${arg.show}"
374
case AppliedType(base, List(k, v)) if base <:< TypeRepr.of[Map[_, _]] =>
375
s"Map processing for key: ${k.show}, value: ${v.show}"
376
case tpe if tpe <:< TypeRepr.of[Product] =>
377
s"Product type processing: ${tpe.show}"
378
case _ =>
379
s"Generic processing: ${tpe.show}"
380
381
Expr(processing)
382
383
val process1 = processType[List[Int]] // "List processing for element type: Int"
384
val process2 = processType[Map[String, Boolean]] // "Map processing for key: String, value: Boolean"
385
```
386
387
The macro system in Scala 3 provides powerful compile-time metaprogramming capabilities, allowing developers to generate code, perform static analysis, and create domain-specific languages while maintaining type safety and performance.