Skip to content

Commit dceb41b

Browse files
committed
Update macros
1 parent a077f63 commit dceb41b

File tree

8 files changed

+58
-60
lines changed

8 files changed

+58
-60
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 `.unlift`, `.unliftOrError` or `Unlifted.unapply`
16+
If the expression represents a value, you can use `.value`, `.valueOrError` 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/other-resources.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ num: 9
2222
* [Shapeless 3](https://github.com/dotty-staging/shapeless/tree/shapeless-3)
2323
* *More Coming soon*
2424

25-
2625
[contributing]: contributing
2726
[best-practices]: best-practices
2827
[compiletime]: tutorial/compiletime
@@ -33,4 +32,4 @@ num: 9
3332
[migration-status]: https://scalacenter.github.io/scala-3-migration-guide/docs/macros/migration-status.html
3433
[quotes]: tutorial/quotes
3534
[references]: references
36-
[tasty]: tutorial/tasty-reflection
35+
[tasty]: tutorial/reflection

_overviews/scala3-macros/tutorial/compiletime.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,5 @@ Summon all provides a way to summon multiple values at the same time from a tupl
8181
[migration-status]: https://scalacenter.github.io/scala-3-migration-guide/docs/macros/migration-status.html
8282
[quotes]: quotes
8383
[references]: ../other-resources
84-
[tasty]: tasty-reflection
84+
[tasty]: reflection
8585
[compiletime-api]: https://dotty.epfl.ch/api/scala/compiletime/index.html

_overviews/scala3-macros/tutorial/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ abstractions and offers more fine-grained control.
2626

2727
- Macros can also be defined in terms of a more _low-level_ API of [TASTy Reflection](tasty-reflection), that allows detailed inspection of programs.
2828

29-
> The tutorial uses the API of Scala 3.0.0-M2. The API had many small changes in this revision.
29+
> The tutorial uses the API of Scala 3.0.0-M3. The API had many small changes in this revision.
3030
3131
> 🚧 We are still in the process of writing the tutorial. You can [help us improve it][contributing] 🚧
3232

_overviews/scala3-macros/tutorial/macros.md

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def powerCode(
113113
x: Expr[Double],
114114
n: Expr[Int]
115115
)(using Quotes): Expr[Double] =
116-
val value: Double = pow(x.unliftOrError, n.unliftOrError)
116+
val value: Double = pow(x.valueOrError, n.valueOrError)
117117
Expr(value)
118118
```
119119
Here, the `pow` operation is a simple Scala function that computes the value of `xⁿ`.
@@ -126,21 +126,21 @@ Let's first look at `Expr.apply(value)`. Given a value of type `T`, this call wi
126126
The argument value to `Expr` is computed at compile-time, at runtime we only need to instantiate this value.
127127

128128
Creating expressions from values works for all _primitive types_, _tuples_ of any arity, `Class`, `Array`, `Seq`, `Set`, `List`, `Map`, `Option`, `Either`, `BigInt`, `BigDecimal`, `StringContext`.
129-
Other types can also work if a `Liftable` is implemented for it, we will [see this later][quotes].
129+
Other types can also work if a `ToExpr` is implemented for it, we will [see this later][quotes].
130130

131131

132132
### Extracting Values from Expressions
133133

134-
The second method we use in the implementation of `powerCode` is `Expr[T].unliftOrError`, which has an effect opposite to `Expr.apply`.
134+
The second method we use in the implementation of `powerCode` is `Expr[T].valueOrError`, which has an effect opposite to `Expr.apply`.
135135
It attempts to extract a value of type `T` from an expression of type `Expr[T]`.
136136
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.
137137

138-
Instead of `unliftOrError`, we could also use the `unlift` operation, which will return an `Option`.
138+
Instead of `valueOrError`, we could also use the `value` operation, which will return an `Option`.
139139
This way we can report the error with a custom error message.
140140

141141
```scala
142142
...
143-
(x.unlift, n.unlift) match
143+
(x.value, n.value) match
144144
case (Some(base), Some(exponent)) =>
145145
pow(base, exponent)
146146
case (Some(_), _) =>
@@ -149,18 +149,18 @@ This way we can report the error with a custom error message.
149149
report.error("Expected a known value for the base, but was " + x.show, x)
150150
```
151151

152-
Alternatively, we can also use the `Unlifted` extractor
152+
Alternatively, we can also use the `Expr.unapply` extractor
153153

154154
```scala
155155
...
156156
(x, n) match
157-
case (Unlifted(base), Unlifted(exponent)) =>
157+
case (Expr(base), Expr(exponent)) =>
158158
pow(base, exponent)
159-
case (Unlifted(_), _) => ...
159+
case (Expr(_), _) => ...
160160
case _ => ...
161161
```
162-
The operations `unlift`, `unliftOrError`, and `Unlifted` will work for all _primitive types_, _tuples_ of any arity, `Option`, `Seq`, `Set`, `Map`, `Either` and `StringContext`.
163-
Other types can also work if an `Unliftable` is implemented for it, we will [see this later][quotes].
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`.
163+
Other types can also work if an `FromExpr` is implemented for it, we will [see this later][quotes].
164164

165165

166166
### Showing Expressions
@@ -194,7 +194,7 @@ inline def sumNow(inline nums: Int*): Int =
194194
def sumCode(nums: Expr[Seq[Int]])(using Quotes): Expr[Int] =
195195
nums match
196196
case Varargs(numberExprs) => // numberExprs: Seq[Expr[Int]]
197-
val numbers: Seq[Int] = numberExprs.map(_.unliftOrError)
197+
val numbers: Seq[Int] = numberExprs.map(_.valueOrError)
198198
Expr(numbers.sum)
199199
case _ => report.error(
200200
"Expected explicit argument" +
@@ -242,7 +242,7 @@ inline def test(inline ignore: Boolean, computation: => Unit): Boolean =
242242
${ testCode('ignore, 'computation) }
243243

244244
def testCode(ignore: Expr[Boolean], computation: Expr[Unit])(using Quotes) =
245-
if ignore.unliftOrError then Expr(false)
245+
if ignore.valueOrError then Expr(false)
246246
else Expr.block(List(computation), Expr(true))
247247
```
248248

@@ -252,10 +252,10 @@ The macro call `test(false, EXPRESSION)` will generate `{ EXPRESSION; true}`, wh
252252
### Simple Matching
253253

254254
The method `Expr.matches` can be used to check if one expression is equal to another.
255-
With this method we could implement an `unlift` operation for `Expr[Boolean]` as follows.
255+
With this method we could implement an `value` operation for `Expr[Boolean]` as follows.
256256

257257
```scala
258-
def unlift(boolExpr: Expr[Boolean]): Option[Boolean] =
258+
def value(boolExpr: Expr[Boolean]): Option[Boolean] =
259259
if boolExpr.matches(Expr(true)) then Some(true)
260260
else if boolExpr.matches(Expr(false)) then Some(false)
261261
else None
@@ -271,7 +271,6 @@ For example `'{ ${expr}; true }` will generate an `Expr[Int]` equivalent to `Exp
271271
The subsequent section on [Quoted Code][quotes] presents quotes in more detail.
272272

273273

274-
275274
[best-practices]: ../best-practices
276275
[compiletime]: compiletime
277276
[faq]: ../faq
@@ -280,4 +279,4 @@ The subsequent section on [Quoted Code][quotes] presents quotes in more detail.
280279
[migration-status]: https://scalacenter.github.io/scala-3-migration-guide/docs/macros/migration-status.html
281280
[quotes]: quotes
282281
[references]: ../other-resources
283-
[tasty]: tasty-reflection
282+
[tasty]: reflection

_overviews/scala3-macros/tutorial/quotes.md

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,17 @@ In some cases, we will not know statically the type within the `Type` and will n
105105
When do we need this extra `Type` parameter?
106106
* When a type is abstract and it is used in a level that is larger than the current level.
107107

108-
When you add a `Type` contextual parameter to a method you will either get it from another context parameter or implicitly with a call to `Type.apply`.
108+
When you add a `Type` contextual parameter to a method you will either get it from another context parameter or implicitly with a call to `Type.of`.
109109
```scala
110110
evalAndUse(Expr(3))
111111
// is equivalent to
112-
evalAndUse[Int](Expr(3))(using Type[Int])
112+
evalAndUse[Int](Expr(3))(using Type.of[Int])
113113
```
114-
As you may have guessed, not every type is can be used in this `Type[..]` out of the box.
114+
As you may have guessed, not every type is can be used in this `Type.of[..]` out of the box.
115115
We cannot recover abstract types that have already been erased.
116116
```scala
117117
def evalAndUse[T](x: Expr[T])(using Quotes) =
118-
given Type[T] = Type[T] // error
118+
given Type[T] = Type.of[T] // error
119119
'{
120120
val x2: T = $x
121121
... // use x2
@@ -129,32 +129,32 @@ Good code should only add `Type` to the context parameters and never use them ex
129129
Explicit use is useful while debugging at the cost of conciseness and clarity.
130130

131131

132-
## Liftables
133-
The `Expr.apply` method uses intances of `Liftable` to perform the lifting.
132+
## ToExpr
133+
The `Expr.apply` method uses intances of `ToExpr` to generate an expression that will create a copy of the value.
134134
```scala
135135
object Expr:
136-
def apply[T](x: T)(using Quotes, Liftable[T]): Expr[T] =
137-
summon[Liftable[T]].toExpr(x)
136+
def apply[T](x: T)(using Quotes, ToExpr[T]): Expr[T] =
137+
summon[ToExpr[T]].apply(x)
138138
```
139139

140-
`Liftable` is defined as follows:
140+
`ToExpr` is defined as follows:
141141
```scala
142-
trait Liftable[T]:
143-
def toExpr(x: T): Quotes ?=> Expr[T]
142+
trait ToExpr[T]:
143+
def apply(x: T)(using Quotes): Expr[T]
144144
```
145145

146-
The `toExpr` method will take a value `T` and generate code that will construct a copy of this value at runtime.
146+
The `ToExpr.apply` method will take a value `T` and generate code that will construct a copy of this value at runtime.
147147

148-
We can define our own `Liftable`s like:
148+
We can define our own `ToExpr`s like:
149149
```scala
150-
given Liftable[Boolean] = new Liftable[Boolean] {
151-
def toExpr(x: Boolean) =
150+
given ToExpr[Boolean] with {
151+
def apply(x: Boolean)(using Quotes) =
152152
if x then '{true}
153153
else '{false}
154154
}
155155

156-
given Liftable[StringContext] = new Liftable[StringContext] {
157-
def toExpr(x: StringContext) =
156+
given ToExpr[StringContext] with {
157+
def apply(x: StringContext)(using Quotes) =
158158
val parts = Varargs(stringContext.parts.map(Expr(_)))
159159
'{ StringContext($parts: _*) }
160160
}
@@ -251,53 +251,53 @@ case ...
251251

252252
*Coming soon*
253253

254-
## Unliftables
254+
## FromExpr
255255

256-
The `Expr.unlift`, `Expr.unlift.orError` `Unlifted.unapply` method uses intances of `Unliftable` to perform the unlifting.
256+
The `Expr.value`, `Expr.valueOrError` `Expr.unapply` method uses intances of `FromExpr` to to extract the value if possible.
257257
```scala
258258
extension [T](expr: Expr[T]):
259-
def unlift(using Quotes)(using unlift: Unliftable[T]): Option[T] =
260-
unlift(expr)
259+
def value(using Quotes)(using fromExpr: FromExpr[T]): Option[T] =
260+
fromExpr.unapply(expr)
261261

262-
def unliftOrError(using Quotes)(using unlift: Unliftable[T]): T =
263-
unlift(expr).getOrElse(eport.throwError("...", expr))
262+
def valueOrError(using Quotes)(using fromExpr: FromExpr[T]): T =
263+
fromExpr.unapply(expr).getOrElse(eport.throwError("...", expr))
264264
end extension
265265

266-
object Unlifted:
267-
def unapply[T](expr: Expr[T])(using Quotes)(using unlift: Unliftable[T]): Option[T] =
268-
unlift(expr)
266+
object Expr:
267+
def unapply[T](expr: Expr[T])(using Quotes)(using fromExpr: FromExpr[T]): Option[T] =
268+
fromExpr.unapply(expr)
269269
```
270270

271-
`Unliftable` is defined as follows:
271+
`FromExpr` is defined as follows:
272272
```scala
273-
trait Unliftable[T]:
274-
def fromExpr(x: Expr[T])(using Quotes): Option[T]
273+
trait FromExpr[T]:
274+
def unapply(x: Expr[T])(using Quotes): Option[T]
275275
```
276276

277-
The `toExpr` method will take a value `T` and generate code that will construct a copy of this value at runtime.
277+
The `FromExpr.unapply` method will take a value `T` and generate code that will construct a copy of this value at runtime.
278278

279-
We can define our own `Uniftable`s like:
279+
We can define our own `FromExpr`s like:
280280
```scala
281-
given Unliftable[Boolean] = new Unliftable[Boolean] {
282-
def fromExpr(x: Expr[Boolean])(using Quotes): Option[Boolean] =
281+
given FromExpr[Boolean] with {
282+
def unapply(x: Expr[Boolean])(using Quotes): Option[Boolean] =
283283
x match
284284
case '{ true } => Some(true)
285285
case '{ false } => Some(false)
286286
case _ => None
287287
}
288288

289-
given Unliftable[StringContext] = new Unliftable[StringContext] {
290-
def fromExpr(x: Expr[StringContext])(using Quotes): Option[StringContext] = x match {
291-
case '{ new StringContext(${Varargs(Unlifted(args))}: _*) } => Some(StringContext(args: _*))
292-
case '{ StringContext(${Varargs(Unlifted(args))}: _*) } => Some(StringContext(args: _*))
289+
given FromExpr[StringContext] with {
290+
def unapply(x: Expr[StringContext])(using Quotes): Option[StringContext] = x match {
291+
case '{ new StringContext(${Varargs(Exprs(args))}: _*) } => Some(StringContext(args: _*))
292+
case '{ StringContext(${Varargs(Exprs(args))}: _*) } => Some(StringContext(args: _*))
293293
case _ => None
294294
}
295295
}
296296
```
297297
Note that we handled two cases for the `StringContext`.
298298
As it is a `case class` it can be created with the `new StringContext` or with the `StringContext.apply` in the companion object.
299299
We also used the `Varargs` extractor to match the arguments of type `Expr[Seq[String]]` into a `Seq[Expr[String]]`.
300-
Then we used the `Unlifted` to match known constants in the `Seq[Expr[String]]` to get a `Seq[String]`.
300+
Then we used the `Exprs` to match known constants in the `Seq[Expr[String]]` to get a `Seq[String]`.
301301

302302

303303
## The Quotes

scala3/new-in-scala3.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ If you want to learn more about meta programming in Scala 3, we invite you to ta
126126
[meta-inline]: {% link _overviews/scala3-macros/tutorial/inline.md %}
127127
[meta-compiletime]: {% link _overviews/scala3-macros/tutorial/compiletime.md %}
128128
[meta-quotes]: {% link _overviews/scala3-macros/tutorial/quotes.md %}
129-
[meta-reflection]: {% link _overviews/scala3-macros/tutorial/tasty-reflection.md %}
129+
[meta-reflection]: {% link _overviews/scala3-macros/tutorial/reflection.md %}
130130

131131
[oo-explicit-null]: {{ site.scala3ref }}/other-new-features/explicit-nulls.html
132132
[oo-safe-init]: {{ site.scala3ref }}/other-new-features/safe-initialization.html

0 commit comments

Comments
 (0)