Skip to content

Add scala.quoted.Expr.unapply as dual of Expr.apply #10580

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
wants to merge 1 commit into from
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
8 changes: 4 additions & 4 deletions docs/docs/reference/metaprogramming/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,16 +632,16 @@ 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.Unlifted`: matches an expression of a value (or list of values) and returns the value (or list of values).
* `scala.quoted.Const`/`scala.quoted.Consts`: Same as `Unlifted` but only works on primitive values.
* `scala.quoted.Varargs`: matches an explicit sequence of expressions and returns them. These sequences are useful to get individual `Expr[T]` out of a varargs expression of type `Expr[Seq[T]]`.
* `scala.quoted.Expr`/`scala.quoted.Exprs`: matches an expression of a value (or list of values) and returns the value (or list of values).
* `scala.quoted.Const`/`scala.quoted.Consts`: Same as `Expr`/`Exprs` but only works on primitive 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 Quotes): Expr[Int] = argsExpr match {
case Varargs(Unlifted(args)) => // args is of type Seq[Int]
case Varargs(Exprs(args)) => // args is of type Seq[Int]
Expr(args.sum) // precompute result of sum
case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]]
val staticSum: Int = argExprs.map(_.unlift.getOrElse(0))
Expand Down
18 changes: 16 additions & 2 deletions library/src-bootstrapped/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,22 @@ object Expr {
}

/** Lift a value into an expression containing the construction of that value */
def apply[T](x: T)(using lift: Liftable[T])(using Quotes): Expr[T] =
lift.toExpr(x)
def apply[T](x: T)(using Liftable[T])(using Quotes): Expr[T] =
scala.Predef.summon[Liftable[T]].toExpr(x)

/** Matches expressions containing values and extracts the value.
*
* Usage:
* ```
* case '{ ... ${expr @ Expr(value)}: T ...} =>
* // expr: Expr[T]
* // value: T
* ```
*
* To directly unlift an expression `expr: Expr[T]` consider using `expr.unlift`/`expr.unliftOrError` insead.
*/
def unapply[T](x: Expr[T])(using Unliftable[T])(using Quotes): Option[T] =
scala.Predef.summon[Unliftable[T]].fromExpr(x)

/** Lifts this sequence of expressions into an expression of a sequence
*
Expand Down
122 changes: 61 additions & 61 deletions library/src-bootstrapped/scala/quoted/Unliftable.scala

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions library/src-non-bootstrapped/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
package scala.quoted

abstract class Expr[+T] private[scala]

object Expr:
def unapply[T](x: Expr[T])(using Unliftable[T])(using Quotes): Option[T] = ???
Original file line number Diff line number Diff line change
@@ -1,29 +1,15 @@
package scala.quoted

/** Value expressions */
object Unlifted {

/** Matches expressions containing values and extracts the value.
*
* Usage:
* ```
* case '{ ... ${expr @ Unlifted(value)}: T ...} =>
* // expr: Expr[T]
* // value: T
* ```
*
* To directly unlift an expression `expr: Expr[T]` consider using `expr.unlift`/`expr.unliftOrError` insead.
*/
def unapply[T](expr: Expr[T])(using Unliftable[T])(using Quotes): Option[T] =
summon[Unliftable[T]].fromExpr(expr)
object Exprs {

/** Matches literal sequence of literal constant value expressions and return a sequence of values.
*
* Usage:
* ```scala
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
* def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes): Expr[Int] = argsExpr match
* case Varargs(Unlifted(args)) =>
* case Varargs(Exprs(args)) =>
* // args: Seq[Int]
* ...
* }
Expand All @@ -34,8 +20,8 @@ object Unlifted {
val builder = Seq.newBuilder[T]
val iter = exprs.iterator
while iter.hasNext do
iter.next() match
case Unlifted(value) => builder += value
iter.next().unlift match
case Some(value) => builder += value
case _ => return None
Some(builder.result())

Expand Down
2 changes: 1 addition & 1 deletion tests/run-macros/expr-map-3/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ private def stringRewriter(e: Expr[Any])(using Quotes): Expr[Any] =
private object StringRewriter extends ExprMap {

def transform[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] = e match
case '{ ${Unlifted(s)}: String } =>
case '{ ${Expr(s)}: String } =>
// checkIfValid(s)
val s2: String & T = s
Expr(s2)
Expand Down
2 changes: 1 addition & 1 deletion tests/run-macros/xml-interpolation-5/Macros_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object XmlQuote {

def impl(receiver: Expr[SCOps.StringContext], args: Expr[Seq[Any]])(using Quotes): Expr[Xml] = {
val string = receiver match {
case '{ SCOps(${Unlifted(sc)}) } => Expr(sc.parts.mkString("??"))
case '{ SCOps(${Expr(sc)}) } => Expr(sc.parts.mkString("??"))
}
'{new Xml(${string}, $args.toList)}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/run-macros/xml-interpolation-6/Macros_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object XmlQuote {

def impl(receiver: Expr[SCOps.StringContext], args: Expr[Seq[Any]])(using Quotes): Expr[Xml] = {
val string = receiver match {
case '{ SCOps(${Unlifted(sc)}): SCOps.StringContext } => sc.parts.mkString("??")
case '{ SCOps(${Expr(sc)}): SCOps.StringContext } => sc.parts.mkString("??")
}
'{new Xml(${Expr(string)}, $args.toList)}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/run-macros/xml-interpolation-7/Macros_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object XmlQuote {

def impl(receiver: Expr[XMLOps.StringContext], args: Expr[Seq[Any]])(using Quotes): Expr[Xml] = {
val string = receiver match {
case '{ XMLOps.xml(${Unlifted(sc)}) } => sc.parts.mkString("??")
case '{ XMLOps.xml(${Expr(sc)}) } => sc.parts.mkString("??")
}
'{new Xml(${Expr(string)}, $args.toList)}
}
Expand Down