Skip to content

Add quoted.{Lifted,Unlifted} #8513

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/reference/changed-features/numeric-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ implementation method `fromDigitsImpl`. Here is its definition:
case Const(ds) =>
try {
val BigFloat(m, e) = apply(ds)
'{BigFloat(${Expr(m)}, ${Expr(e)})}
'{BigFloat(${Lifted(m)}, ${Lifted(e)})}
}
catch {
case ex: FromDigits.FromDigitsException =>
Expand Down
12 changes: 6 additions & 6 deletions docs/docs/reference/contextual/derivation-macro.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ given derived[T: Type](using qctx: QuoteContext) as Expr[Eq[T]] = {
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elementTypes }} =>
val elemInstances = summonAll(elementTypes)
val eqProductBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => {
elemInstances.zipWithIndex.foldLeft(Expr(true: Boolean)) {
elemInstances.zipWithIndex.foldLeft(Lifted(true: Boolean)) {
case (acc, (elem, index)) =>
val e1 = '{$x.asInstanceOf[Product].productElement(${Expr(index)})}
val e2 = '{$y.asInstanceOf[Product].productElement(${Expr(index)})}
val e1 = '{$x.asInstanceOf[Product].productElement(${Lifted(index)})}
val e2 = '{$y.asInstanceOf[Product].productElement(${Lifted(index)})}

'{ $acc && $elem.asInstanceOf[Eq[Any]].eqv($e1, $e2) }
}
Expand Down Expand Up @@ -184,10 +184,10 @@ object Eq {
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elementTypes }} =>
val elemInstances = summonAll(elementTypes)
val eqProductBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => {
elemInstances.zipWithIndex.foldLeft(Expr(true: Boolean)) {
elemInstances.zipWithIndex.foldLeft(Lifted(true: Boolean)) {
case (acc, (elem, index)) =>
val e1 = '{$x.asInstanceOf[Product].productElement(${Expr(index)})}
val e2 = '{$y.asInstanceOf[Product].productElement(${Expr(index)})}
val e1 = '{$x.asInstanceOf[Product].productElement(${Lifted(index)})}
val e2 = '{$y.asInstanceOf[Product].productElement(${Lifted(index)})}

'{ $acc && $elem.asInstanceOf[Eq[Any]].eqv($e1, $e2) }
}
Expand Down
30 changes: 15 additions & 15 deletions docs/docs/reference/metaprogramming/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ import scala.quoted.{given _, _}

def compile(e: Exp, env: Map[String, Expr[Int]])(using QuoteContext): Expr[Int] = e match {
case Num(n) =>
Expr(n)
Lifted(n)
case Plus(e1, e2) =>
'{ ${ compile(e1, env) } + ${ compile(e2, env) } }
case Var(x) =>
Expand All @@ -238,19 +238,19 @@ Running `compile(letExp, Map())` would yield the following Scala code:
```scala
'{ val y = 3; (2 + y) + 4 }
```
The body of the first clause, `case Num(n) => Expr(n)`, looks suspicious. `n`
is declared as an `Int`, yet it is converted to an `Expr[Int]` with `Expr()`.
The body of the first clause, `case Num(n) => Lifted(n)`, looks suspicious. `n`
is declared as an `Int`, yet it is converted to an `Expr[Int]` with `Lifted()`.
Shouldn’t `n` be quoted? In fact this would not
work since replacing `n` by `'n` in the clause would not be phase
correct.

The `Expr.apply` method is defined in package `quoted`:
The `Lifted.apply` method is defined in package `quoted`:
```scala
package quoted

object Expr {
object Lifted {
...
def apply[T: Liftable](x: T)(using QuoteContext): Expr[T] = summon[Liftable[T]].toExpr(x)
def apply[T](x: T)(using qctx: QuoteContext, lift: Liftable[T]): Expr[T] = lift.toExpr(x)
...
}
```
Expand Down Expand Up @@ -291,7 +291,7 @@ a `List` is liftable if its element type is:
```scala
given [T: Liftable : Type] as Liftable[List[T]] {
def toExpr(xs: List[T]) = xs match {
case head :: tail => '{ ${ Expr(head) } :: ${ toExpr(tail) } }
case head :: tail => '{ ${ Lifted(head) } :: ${ toExpr(tail) } }
case Nil => '{ Nil: List[T] }
}
}
Expand All @@ -306,13 +306,13 @@ Using lifting, we can now give the missing definition of `showExpr` in the intro
```scala
def showExpr[T](expr: Expr[T])(using QuoteContext): Expr[String] = {
val code: String = expr.show
Expr(code)
Lifted(code)
}
```
That is, the `showExpr` method converts its `Expr` argument to a string (`code`), and lifts
the result back to an `Expr[String]` using `Expr.apply`.
the result back to an `Expr[String]` using `Lifted.apply`.

**Note**: Lifting `String` to `Expr[String]` using `Expr(code)` can be omitted by importing an implicit
**Note**: Lifting `String` to `Expr[String]` using `Lifted(code)` can be omitted by importing an implicit
conversion with `import scala.quoted.autolift.given`. The programmer is able to
declutter slightly the code at the cost of readable _phase distinction_ between
stages.
Expand Down Expand Up @@ -617,15 +617,15 @@ It is possible to deconstruct or extract values out of `Expr` using pattern matc
`scala.quoted` contains objects that can help extracting values from `Expr`.

* `scala.quoted.Const`/`scala.quoted.Consts`: matches an expression of a literal value (or list of values) and returns the value (or list of values).
* `scala.quoted.Value`/`scala.quoted.Values`: matches an expression of a value (or list of values) and returns the value (or list of values).
* `scala.quoted.Unlifted`: matches an expression of a value (or list of values) and returns the value (or list of values).
* `scala.quoted.Varargs`: matches an explicit sequence of expresions and returns them. These sequences are useful to get individual `Expr[T]` out of a varargs expression of type `Expr[Seq[T]]`.

These could be used in the following way to optimize any call to `sum` that has statically known values.
```scala
inline def sum(inline args: Int*): Int = ${ sumExpr('args) }
private def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = argsExpr match {
case Varargs(Consts(args)) => // args is of type Seq[Int]
Expr(args.sum) // precompute result of sum
Lifted(args.sum) // precompute result of sum
case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]]
val staticSum: Int = argExprs.map {
case Const(arg) => arg
Expand All @@ -635,7 +635,7 @@ private def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = a
case Const(_) => false
case arg => true
}
dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg })
dynamicSum.foldLeft(Lifted(staticSum))((acc, arg) => '{ $acc + $arg })
case _ =>
'{ $argsExpr.sum }
}
Expand All @@ -658,7 +658,7 @@ def sum(args: Int*): Int = args.sum
inline def optimize(arg: Int): Int = ${ optimizeExpr('arg) }
private def optimizeExpr(body: Expr[Int])(using QuoteContext): Expr[Int] = body match {
// Match a call to sum without any arguments
case '{ sum() } => Expr(0)
case '{ sum() } => Lifted(0)
// Match a call to sum with an argument $n of type Int. n will be the Expr[Int] representing the argument.
case '{ sum($n) } => n
// Match a call to sum and extracts all its args in an `Expr[Seq[Int]]`
Expand All @@ -679,7 +679,7 @@ private def sumExpr(args1: Seq[Expr[Int]])(using QuoteContext): Expr[Int] = {
case Const(_) => false
case arg => true
}
dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg })
dynamicSum.foldLeft(Lifted(staticSum))((acc, arg) => '{ $acc + $arg })
}
```

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/metaprogramming/tasty-reflect.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The method `qctx.tasty.Term.seal` provides a way to go back to a
`quoted.Expr[Any]`. Note that the type is `Expr[Any]`. Consequently, the type
must be set explicitly with a checked `cast` call. If the type does not conform
to it an exception will be thrown. In the code above, we could have replaced
`Expr(n)` by `xTree.seal.cast[Int]`.
`Lifted(n)` by `xTree.seal.cast[Int]`.

### Obtaining the underlying argument

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,6 @@ object StringContextMacro {
}

// macro expansion
'{(${Expr(parts.mkString)}).format(${argsExpr}: _*)}
'{(${Lifted(parts.mkString)}).format(${argsExpr}: _*)}
}
}
2 changes: 1 addition & 1 deletion library/src/scala/internal/quoted/showName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package scala.internal.quoted
* Usage:
* ```scala
* def let(name: String)(value: Expr[Int])(in: Expr[Int] => Expr[Int]): Expr[Int] = '{
* @showName(${Expr(name)})
* @showName(${Lifted(name)})
* val x = $value
* ${ in('x) }
* }
Expand Down
5 changes: 3 additions & 2 deletions library/src/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ object Expr {
Block(statements.map(_.unseal), expr.unseal).seal.asInstanceOf[Expr[T]]
}

/** Lift a value into an expression containing the construction of that value */
def apply[T](x: T)(using qctx: QuoteContext, lift: Liftable[T]): Expr[T] = lift.toExpr(x)
// /** Lift a value into an expression containing the construction of that value */
// @deprecated("Use scala.quoted.Lifted", "0.23")
// def apply[T](x: T)(using qctx: QuoteContext, lift: Liftable[T]): Expr[T] = Lifted(x)

/** Lifts this sequence of expressions into an expression of a sequence
*
Expand Down
Loading