Skip to content

Commit 0a37253

Browse files
authored
Merge pull request #2539 from lmlynik/correct-macros.md
correct valueOrError to valueOrAbort
2 parents 69a362b + 85b5f31 commit 0a37253

File tree

3 files changed

+43
-22
lines changed

3 files changed

+43
-22
lines changed

_overviews/scala3-macros/faq.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ All quotes containing a value of a primitive type is optimised to an `Expr.apply
1313
Choose one in your project and stick with a single notation to avoid confusion.
1414

1515
## How do I get a value out of an `Expr`?
16-
If the expression represents a value, you can use `.value`, `.valueOrError` or `Expr.unapply`
16+
If the expression represents a value, you can use `.value`, `.valueOrAbort` or `Expr.unapply`
1717

1818
## How can I get the precise type of an `Expr`?
1919
We can get the precise type (`Type`) of an `Expr` using the following pattern match:

_overviews/scala3-macros/tutorial/macros.md

Lines changed: 41 additions & 20 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
@@ -113,7 +116,7 @@ def powerCode(
113116
x: Expr[Double],
114117
n: Expr[Int]
115118
)(using Quotes): Expr[Double] =
116-
val value: Double = pow(x.valueOrError, n.valueOrError)
119+
val value: Double = pow(x.valueOrAbort, n.valueOrAbort)
117120
Expr(value)
118121
```
119122
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
131134

132135
### Extracting Values from Expressions
133136

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`.
135138
It attempts to extract a value of type `T` from an expression of type `Expr[T]`.
136139
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.
137140

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`.
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-
...
154+
def powerCode(
155+
x: Expr[Double],
156+
n: Expr[Int]
157+
)(using Quotes): Expr[Double] =
158+
import quotes.reflect.report
143159
(x.value, n.value) match
144160
case (Some(base), Some(exponent)) =>
145-
pow(base, exponent)
161+
val value: Double = pow(base, exponent)
162+
Expr(value)
146163
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)
148165
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)
150167
```
151168

152169
Alternatively, we can also use the `Expr.unapply` extractor
@@ -155,27 +172,30 @@ Alternatively, we can also use the `Expr.unapply` extractor
155172
...
156173
(x, n) match
157174
case (Expr(base), Expr(exponent)) =>
158-
pow(base, exponent)
175+
val value: Double = pow(base, exponent)
176+
Expr(value)
159177
case (Expr(_), _) => ...
160178
case _ => ...
161179
```
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`.
163181
Other types can also work if an `FromExpr` is implemented for it, we will [see this later][quotes].
164182

165183

166184
### Showing Expressions
167185

168186
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.
169187
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 -->
170190
```scala
171191
def debugPowerCode(
172192
x: Expr[Double],
173193
n: Expr[Int]
174194
)(using Quotes): Expr[Double] =
175195
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}")
179199
val code = powerCode(x, n)
180200
println(s" code := ${code.show}")
181201
code
@@ -188,23 +208,24 @@ Varargs in Scala are represented with `Seq`, hence when we write a macro with a
188208
It is possible to recover each individual argument (of type `Expr[T]`) using the `scala.quoted.Varargs` extractor.
189209

190210
```scala
191-
import scala.quoted.Varargs
211+
import scala.quoted.* // imports `Varargs`, `Quotes`, etc.
192212

193213
inline def sumNow(inline nums: Int*): Int =
194214
${ sumCode('nums) }
195215

196216
def sumCode(nums: Expr[Seq[Int]])(using Quotes): Expr[Int] =
217+
import quotes.reflect.report
197218
nums match
198219
case Varargs(numberExprs) => // numberExprs: Seq[Expr[Int]]
199-
val numbers: Seq[Int] = numberExprs.map(_.valueOrError)
220+
val numbers: Seq[Int] = numberExprs.map(_.valueOrAbort)
200221
Expr(numbers.sum)
201-
case _ => report.error(
202-
"Expected explicit argument" +
203-
"Notation `args: _*` is not supported.", numbersExpr)
222+
case _ => report.errorAndAbort(
223+
"Expected explicit varargs sequence. " +
224+
"Notation `args*` is not supported.", nums)
204225
```
205226

206227
The extractor will match a call to `sumNow(1, 2, 3)` and extract a `Seq[Expr[Int]]` containing the code of each parameter.
207-
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.
208229

209230
`Varargs` can also be used as a constructor. `Varargs(Expr(1), Expr(2), Expr(3))` will return an `Expr[Seq[Int]]`.
210231
We will see how this can be useful later.
@@ -244,7 +265,7 @@ inline def test(inline ignore: Boolean, computation: => Unit): Boolean =
244265
${ testCode('ignore, 'computation) }
245266

246267
def testCode(ignore: Expr[Boolean], computation: Expr[Unit])(using Quotes) =
247-
if ignore.valueOrError then Expr(false)
268+
if ignore.valueOrAbort then Expr(false)
248269
else Expr.block(List(computation), Expr(true))
249270
```
250271

_overviews/scala3-macros/tutorial/quotes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ As with expression quote patterns, type variables are represented using lower ca
367367

368368
## FromExpr
369369

370-
The `Expr.value`, `Expr.valueOrError`, and `Expr.unapply` methods uses intances of `FromExpr` to extract the value if possible.
370+
The `Expr.value`, `Expr.valueOrAbort`, and `Expr.unapply` methods uses intances of `FromExpr` to extract the value if possible.
371371
```scala
372372
extension [T](expr: Expr[T]):
373373
def value(using Quotes)(using fromExpr: FromExpr[T]): Option[T] =

0 commit comments

Comments
 (0)