You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _overviews/scala3-macros/tutorial/macros.md
+41-20Lines changed: 41 additions & 20 deletions
Original file line number
Diff line number
Diff line change
@@ -14,13 +14,16 @@ Macros enable us to do exactly this: treat **programs as data** and manipulate t
14
14
15
15
## Macros Treat Programs as Values
16
16
With a macro, we can treat programs as values, which allows us to analyze and generate them at compile time.
17
+
17
18
A Scala expression with type `T` is represented by an instance of the type `scala.quoted.Expr[T]`.
18
19
19
20
We will dig into the details of the type `Expr[T]`, as well as the different ways of analyzing and constructing instances, when talking about [Quoted Code][quotes] and [Reflection][tasty].
20
21
For now, it suffices to know that macros are metaprograms that manipulate expressions of type `Expr[T]`.
21
22
22
-
The following macro implementation simply prints the expression of the provided argument:
23
+
The following macro implementation prints the expression of the provided argument:
Here, the `pow` operation is a simple Scala function that computes the value of `xⁿ`.
@@ -131,22 +134,36 @@ Other types can also work if a `ToExpr` is implemented for it, we will [see this
131
134
132
135
### Extracting Values from Expressions
133
136
134
-
The second method we use in the implementation of `powerCode` is `Expr[T].valueOrError`, which has an effect opposite to `Expr.apply`.
137
+
The second method we use in the implementation of `powerCode` is `Expr[T].valueOrAbort`, which has an effect opposite to `Expr.apply`.
135
138
It attempts to extract a value of type `T` from an expression of type `Expr[T]`.
136
139
This can only succeed, if the expression directly contains the code of a value, otherwise, it will throw an exception that stops the macro expansion and reports that the expression did not correspond to a value.
137
140
138
-
Instead of `valueOrError`, we could also use the `value` operation, which will return an `Option`.
141
+
Instead of `valueOrAbort`, we could also use the `value` operation, which will return an `Option`.
139
142
This way we can report the error with a custom error message.
140
143
144
+
#### Reporting Custom Error Messages
145
+
146
+
The contextual `Quotes` parameter provides a `report` object that we can use to report a custom error message.
147
+
Within a macro implementation method, you can access the contextual `Quotes` parameter with the `quotes` method
148
+
(imported with `import scala.quoted.*`), then import the `report` object by `import quotes.reflect.report`.
149
+
150
+
#### Providing the Custom Error
151
+
152
+
We will provide the custom error message by calling `errorAndAbort` on the `report` object as follows:
141
153
```scala
142
-
...
154
+
defpowerCode(
155
+
x: Expr[Double],
156
+
n: Expr[Int]
157
+
)(usingQuotes):Expr[Double] =
158
+
importquotes.reflect.report
143
159
(x.value, n.value) match
144
160
case (Some(base), Some(exponent)) =>
145
-
pow(base, exponent)
161
+
valvalue:Double= pow(base, exponent)
162
+
Expr(value)
146
163
case (Some(_), _) =>
147
-
report.error("Expected a known value for the exponent, but was "+ n.show, n)
164
+
report.errorAndAbort("Expected a known value for the exponent, but was "+ n.show, n)
148
165
case _ =>
149
-
report.error("Expected a known value for the base, but was "+ x.show, x)
166
+
report.errorAndAbort("Expected a known value for the base, but was "+ x.show, x)
150
167
```
151
168
152
169
Alternatively, we can also use the `Expr.unapply` extractor
@@ -155,27 +172,30 @@ Alternatively, we can also use the `Expr.unapply` extractor
155
172
...
156
173
(x, n) match
157
174
case (Expr(base), Expr(exponent)) =>
158
-
pow(base, exponent)
175
+
valvalue:Double= pow(base, exponent)
176
+
Expr(value)
159
177
case (Expr(_), _) => ...
160
178
case _ => ...
161
179
```
162
-
The operations `value`, `valueOrError`, and `Expr.unapply` will work for all _primitive types_, _tuples_ of any arity, `Option`, `Seq`, `Set`, `Map`, `Either` and `StringContext`.
180
+
The operations `value`, `valueOrAbort`, and `Expr.unapply` will work for all _primitive types_, _tuples_ of any arity, `Option`, `Seq`, `Set`, `Map`, `Either` and `StringContext`.
163
181
Other types can also work if an `FromExpr` is implemented for it, we will [see this later][quotes].
164
182
165
183
166
184
### Showing Expressions
167
185
168
186
In the implementation of `inspectCode`, we have already seen how to convert expressions to the string representation of their _source code_ using the `.show` method.
169
187
This can be useful to perform debugging on macro implementations:
188
+
189
+
<!-- The below code example does not use multi-line string because it causes syntax highlighting to break -->
170
190
```scala
171
191
defdebugPowerCode(
172
192
x: Expr[Double],
173
193
n: Expr[Int]
174
194
)(usingQuotes):Expr[Double] =
175
195
println(
176
-
s"""powerCode
177
-
| x := ${x.show}
178
-
| n := ${n.show}""".stripMargin)
196
+
s"powerCode\n"+
197
+
s"x := ${x.show}\n"+
198
+
s"n := ${n.show}")
179
199
valcode= powerCode(x, n)
180
200
println(s" code := ${code.show}")
181
201
code
@@ -188,23 +208,24 @@ Varargs in Scala are represented with `Seq`, hence when we write a macro with a
188
208
It is possible to recover each individual argument (of type `Expr[T]`) using the `scala.quoted.Varargs` extractor.
189
209
190
210
```scala
191
-
importscala.quoted.Varargs
211
+
importscala.quoted.*// imports `Varargs`, `Quotes`, etc.
0 commit comments