Skip to content

Commit 0423507

Browse files
committed
clear up how report is imported
1 parent c436bee commit 0423507

File tree

1 file changed

+34
-15
lines changed

1 file changed

+34
-15
lines changed

_overviews/scala3-macros/tutorial/macros.md

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ Macros enable us to do exactly this: treat **programs as data** and manipulate t
1414

1515
## Macros Treat Programs as Values
1616
With a macro, we can treat programs as values, which allows us to analyze and generate them at compile time.
17+
1718
A Scala expression with type `T` is represented by an instance of the type `scala.quoted.Expr[T]`.
1819

1920
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].
2021
For now, it suffices to know that macros are metaprograms that manipulate expressions of type `Expr[T]`.
2122

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:
2324
```scala
25+
import scala.quoted.* // imports Quotes, Expr
26+
2427
def inspectCode(x: Expr[Any])(using Quotes): Expr[Any] =
2528
println(x.show)
2629
x
@@ -138,12 +141,25 @@ This can only succeed, if the expression directly contains the code of a value,
138141
Instead of `valueOrAbort`, we could also use the `value` operation, which will return an `Option`.
139142
This way we can report the error with a custom error message.
140143

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:
141153
```scala
142-
...
143-
import quotes.reflect.*
154+
def powerCode(
155+
x: Expr[Double],
156+
n: Expr[Int]
157+
)(using Quotes): Expr[Double] =
158+
import quotes.reflect.report
144159
(x.value, n.value) match
145160
case (Some(base), Some(exponent)) =>
146-
pow(base, exponent)
161+
val value: Double = pow(base, exponent)
162+
Expr(value)
147163
case (Some(_), _) =>
148164
report.errorAndAbort("Expected a known value for the exponent, but was " + n.show, n)
149165
case _ =>
@@ -154,30 +170,32 @@ Alternatively, we can also use the `Expr.unapply` extractor
154170

155171
```scala
156172
...
157-
import quotes.reflect.*
158173
(x, n) match
159174
case (Expr(base), Expr(exponent)) =>
160-
pow(base, exponent)
175+
val value: Double = pow(base, exponent)
176+
Expr(value)
161177
case (Expr(_), _) => ...
162178
case _ => ...
163179
```
164-
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`.
165181
Other types can also work if an `FromExpr` is implemented for it, we will [see this later][quotes].
166182

167183

168184
### Showing Expressions
169185

170186
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.
171187
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 -->
172190
```scala
173191
def debugPowerCode(
174192
x: Expr[Double],
175193
n: Expr[Int]
176194
)(using Quotes): Expr[Double] =
177195
println(
178-
s"""powerCode
179-
| x := ${x.show}
180-
| n := ${n.show}""".stripMargin)
196+
s"powerCode \n" +
197+
s" x := ${x.show}\n" +
198+
s" n := ${n.show}")
181199
val code = powerCode(x, n)
182200
println(s" code := ${code.show}")
183201
code
@@ -190,23 +208,24 @@ Varargs in Scala are represented with `Seq`, hence when we write a macro with a
190208
It is possible to recover each individual argument (of type `Expr[T]`) using the `scala.quoted.Varargs` extractor.
191209

192210
```scala
193-
import scala.quoted.Varargs
211+
import scala.quoted.{Varargs, *}
194212

195213
inline def sumNow(inline nums: Int*): Int =
196214
${ sumCode('nums) }
197215

198216
def sumCode(nums: Expr[Seq[Int]])(using Quotes): Expr[Int] =
217+
import quotes.reflect.report
199218
nums match
200219
case Varargs(numberExprs) => // numberExprs: Seq[Expr[Int]]
201220
val numbers: Seq[Int] = numberExprs.map(_.valueOrAbort)
202221
Expr(numbers.sum)
203-
case _ => report.error(
204-
"Expected explicit argument" +
205-
"Notation `args: _*` is not supported.", numbersExpr)
222+
case _ => report.errorAndAbort(
223+
"Expected explicit varargs sequence. " +
224+
"Notation `args*` is not supported.", nums)
206225
```
207226

208227
The extractor will match a call to `sumNow(1, 2, 3)` and extract a `Seq[Expr[Int]]` containing the code of each parameter.
209-
But, if we try to match the argument of the call `sumNow(nums: _*)`, the extractor will not match.
228+
But, if we try to match the argument of the call `sumNow(nums*)`, the extractor will not match.
210229

211230
`Varargs` can also be used as a constructor. `Varargs(Expr(1), Expr(2), Expr(3))` will return an `Expr[Seq[Int]]`.
212231
We will see how this can be useful later.

0 commit comments

Comments
 (0)