Skip to content

Commit 154069d

Browse files
committed
Add Const.unapply(Seq) and Value.unapply(Seq)
Instead of having a special concept to extract values out of sequences, with this change, we can take advantage of composition with `Exprs`. The old `ConstSeq` and `ValueSeq` are deprecated to reduce the sumber of concepts in the API.
1 parent 761405d commit 154069d

File tree

10 files changed

+81
-52
lines changed

10 files changed

+81
-52
lines changed

docs/docs/reference/metaprogramming/macros.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -616,17 +616,15 @@ It is possible to deconstruct or extract values out of `Expr` using pattern matc
616616

617617
`scala.quoted` contains objects that can help extracting values from `Expr`.
618618

619-
* `scala.quoted.Const`: matches an expression of a literal value and returns the value.
620-
* `scala.quoted.Value`: matches an expression of a value and returns the value.
619+
* `scala.quoted.Const`: matches an expression of a literal value (or list of values) and returns the value (or list of values).
620+
* `scala.quoted.Value`: matches an expression of a value (or list of values) and returns the value (or list of values).
621621
* `scala.quoted.Exprs`: 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]]`.
622-
* `scala.quoted.ConstSeq`: matches an explicit sequence of literal values and returns them.
623-
* `scala.quoted.ValueSeq`: matches an explicit sequence of values and returns them.
624622

625623
These could be used in the following way to optimize any call to `sum` that has statically known values.
626624
```scala
627625
inline def sum(inline args: Int*): Int = ${ sumExpr('args) }
628626
private def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = argsExpr match {
629-
case ConstSeq(args) => // args is of type Seq[Int]
627+
case Exprs(Const(args)) => // args is of type Seq[Int]
630628
Expr(args.sum) // precompute result of sum
631629
case Exprs(argExprs) => // argExprs is of type Seq[Expr[Int]]
632630
val staticSum: Int = argExprs.map {

library/src-bootstrapped/dotty/internal/StringContextMacro.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,12 @@ object StringContextMacro {
6363

6464
def notStatic =
6565
qctx.throwError("Expected statically known String Context", strCtxExpr)
66-
def splitParts(seq: Expr[Seq[String]]) = (seq, seq) match {
67-
case (Exprs(p1), ConstSeq(p2)) => (p1.toList, p2.toList)
68-
case (_, _) => notStatic
66+
def splitParts(seq: Expr[Seq[String]]) = seq match {
67+
case Exprs(p1) =>
68+
p1 match
69+
case Const(p2) => (p1.toList, p2.toList)
70+
case _ => notStatic
71+
case _ => notStatic
6972
}
7073
val (partsExpr, parts) = strCtxExpr match {
7174
case '{ StringContext($parts: _*) } => splitParts(parts)

library/src/scala/quoted/Const.scala

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
package scala.quoted
22

3-
/** Matches expressions containing literal constant values and extracts the value.
4-
* It may match expressions of type Boolean, Byte, Short, Int, Long,
5-
* Float, Double, Char, String, ClassTag, scala.Symbol, Null and Unit.
6-
*
7-
* Usage:
8-
* ```
9-
* (x: Expr[B]) match {
10-
* case Const(value: B) => ...
11-
* }
12-
* ```
13-
*/
3+
/** MLiteral constant values */
144
object Const {
155

6+
/** Matches expressions containing literal constant values and extracts the value.
7+
* It may match expressions of type Boolean, Byte, Short, Int, Long,
8+
* Float, Double, Char, String, ClassTag, scala.Symbol, Null and Unit.
9+
*
10+
* Usage:
11+
* ```
12+
* (x: Expr[B]) match {
13+
* case Const(value: B) => ...
14+
* }
15+
* ```
16+
*/
1617
def unapply[T](expr: Expr[T])(using qctx: QuoteContext): Option[T] = {
1718
import qctx.tasty.{_, given _}
1819
def rec(tree: Term): Option[T] = tree match {
@@ -25,4 +26,24 @@ object Const {
2526
rec(expr.unseal)
2627
}
2728

29+
/** Matches literal sequence of literal constant value expressions and return a sequence of values.
30+
*
31+
* Usage:
32+
* ```scala
33+
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
34+
* def sumExpr(argsExpr: Expr[Seq[Int]])(usingusing QuoteContext): Expr[Int] = argsExpr match
35+
* case Exprs(Const(args)) =>
36+
* // args: Seq[Int]
37+
* ...
38+
* }
39+
* ```
40+
*/
41+
def unapply[T](exprs: Seq[Expr[T]])(using qctx: QuoteContext): Option[Seq[T]] =
42+
exprs.foldRight(Option(List.empty[T])) { (elem, acc) =>
43+
(elem, acc) match {
44+
case (Const(value), Some(lst)) => Some(value :: lst)
45+
case (_, _) => None
46+
}
47+
}
48+
2849
}

library/src/scala/quoted/Value.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,23 @@ object Value {
1515
def unapply[T](expr: Expr[T])(using valueOf: ValueOfExpr[T], qxtc: QuoteContext): Option[T] =
1616
valueOf(expr)
1717

18+
/** Matches literal sequence of literal constant value expressions and return a sequence of values.
19+
*
20+
* Usage:
21+
* ```scala
22+
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
23+
* def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = argsExpr match
24+
* case Exprs(Value(args)) =>
25+
* // args: Seq[Int]
26+
* ...
27+
* }
28+
* ```
29+
*/
30+
def unapply[T](exprs: Seq[Expr[T]])(using valueOf: ValueOfExpr[T], qctx: QuoteContext): Option[Seq[T]] =
31+
exprs.foldRight(Option(List.empty[T])) { (elem, acc) =>
32+
(elem, acc) match {
33+
case (Value(value), Some(lst)) => Some(value :: lst)
34+
case (_, _) => None
35+
}
36+
}
1837
}

library/src/scala/quoted/ValueOfExpr.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ object ValueOfExpr {
4242

4343
given StringContext_delegate as ValueOfExpr[StringContext] = new {
4444
def apply(x: Expr[StringContext])(using qctx: QuoteContext): Option[StringContext] = x match {
45-
case '{ new StringContext(${ConstSeq(args)}: _*) } => Some(StringContext(args: _*))
46-
case '{ StringContext(${ConstSeq(args)}: _*) } => Some(StringContext(args: _*))
45+
case '{ new StringContext(${Exprs(Const(args))}: _*) } => Some(StringContext(args: _*))
46+
case '{ StringContext(${Exprs(Const(args))}: _*) } => Some(StringContext(args: _*))
4747
case _ => None
4848
}
4949
override def toString(): String = "scala.quoted.ValueOfExpr.Tuple1_delegate"
@@ -270,9 +270,9 @@ object ValueOfExpr {
270270

271271
given Seq_delegate[T](using Type[T], ValueOfExpr[T]) as ValueOfExpr[Seq[T]] = new {
272272
def apply(x: Expr[Seq[T]])(using qctx: QuoteContext): Option[Seq[T]] = x match {
273-
case ValueSeq(elems) => Some(elems)
274-
case '{ scala.collection.Seq[T](${ValueSeq(elems)}: _*) } => Some(elems)
275-
case '{ scala.collection.immutable.Seq[T](${ValueSeq(elems)}: _*) } => Some(elems)
273+
case Exprs(Value(elems)) => Some(elems)
274+
case '{ scala.collection.Seq[T](${Exprs(Value(elems))}: _*) } => Some(elems)
275+
case '{ scala.collection.immutable.Seq[T](${Exprs(Value(elems))}: _*) } => Some(elems)
276276
case _ => None
277277
}
278278
override def toString(): String = "scala.quoted.ValueOfExpr.Seq_delegate"
Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
package scala.quoted
2+
package matching
23

34
/** Literal sequence of literal constant value expressions */
45
object ConstSeq {
@@ -15,15 +16,11 @@ object ConstSeq {
1516
* }
1617
* ```
1718
*/
18-
def unapply[T](expr: Expr[Seq[T]])(using qctx: QuoteContext): Option[Seq[T]] = expr match {
19-
case Exprs(elems) =>
20-
elems.foldRight(Option(List.empty[T])) { (elem, acc) =>
21-
(elem, acc) match {
22-
case (Const(value), Some(lst)) => Some(value :: lst)
23-
case (_, _) => None
24-
}
25-
}
26-
case _ => None
27-
}
19+
@deprecated("use scala.quoted.Exprs(scala.quoted.Const(_)) instead", "0.23.0")
20+
def unapply[T](expr: Expr[Seq[T]])(using qctx: QuoteContext): Option[Seq[T]] =
21+
import scala.quoted.Const
22+
expr match
23+
case Exprs(Const(elems)) => Some(elems)
24+
case _ => None
2825

2926
}
Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
package scala.quoted
2+
package matching
23

34
/** Value sequence of value expressions */
45
object ValueSeq {
@@ -15,15 +16,11 @@ object ValueSeq {
1516
* }
1617
* ```
1718
*/
18-
def unapply[T](expr: Expr[Seq[T]])(using valueOf: ValueOfExpr[T], qctx: QuoteContext): Option[Seq[T]] = expr match {
19-
case Exprs(elems) =>
20-
elems.foldRight(Option(List.empty[T])) { (elem, acc) =>
21-
(elem, acc) match {
22-
case (Value(value), Some(lst)) => Some(value :: lst)
23-
case (_, _) => None
24-
}
25-
}
26-
case _ => None
27-
}
19+
@deprecated("use scala.quoted.Exprs(scala.quoted.Value(_)) instead", "0.23.0")
20+
def unapply[T](expr: Expr[Seq[T]])(using valueOf: ValueOfExpr[T], qctx: QuoteContext): Option[Seq[T]] =
21+
import scala.quoted.Const
22+
expr match
23+
case Exprs(Value(elems)) => Some(elems)
24+
case _ => None
2825

2926
}

library/src/scala/quoted/matching/package.scala

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ package object matching {
1717
@deprecated("use scala.quoted.Const instead", "0.23.0")
1818
val Const: quoted.Const.type = quoted.Const
1919

20-
@deprecated("use scala.quoted.ConstSeq instead", "0.23.0")
21-
val ConstSeq: quoted.ConstSeq.type = quoted.ConstSeq
22-
2320
@deprecated("use scala.quoted.Exprs instead", "0.23.0")
2421
val ExprSeq: quoted.Exprs.type = quoted.Exprs
2522

@@ -32,7 +29,4 @@ package object matching {
3229
@deprecated("use scala.quoted.ValueOfExpr instead", "0.23.0")
3330
val ValueOfExpr: quoted.ValueOfExpr.type = quoted.ValueOfExpr
3431

35-
@deprecated("use scala.quoted.ValueSeq instead", "0.23.0")
36-
val ValueSeq: quoted.ValueSeq.type = quoted.ValueSeq
37-
3832
}

tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object Macros {
88

99
private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = {
1010
self match {
11-
case '{ StringContext(${ConstSeq(parts)}: _*) } =>
11+
case '{ StringContext(${Exprs(Const(parts))}: _*) } =>
1212
val upprerParts: List[String] = parts.toList.map(_.toUpperCase)
1313
val upprerPartsExpr: Expr[List[String]] = Expr.ofList(upprerParts.map(Expr(_)))
1414
'{ StringContext($upprerPartsExpr: _*).s($args: _*) }

tests/run-macros/quoted-matching-docs/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ private def sumExprShow(argsExpr: Expr[Seq[Int]]) (using QuoteContext): Expr[Str
1212
private def sumExpr(argsExpr: Expr[Seq[Int]])(using qctx: QuoteContext) : Expr[Int] = {
1313
import qctx.tasty.{given _, _}
1414
UnsafeExpr.underlyingArgument(argsExpr) match {
15-
case ConstSeq(args) => // args is of type Seq[Int]
15+
case Exprs(Const(args)) => // args is of type Seq[Int]
1616
Expr(args.sum) // precompute result of sum
1717
case Exprs(argExprs) => // argExprs is of type Seq[Expr[Int]]
1818
val staticSum: Int = argExprs.map {

0 commit comments

Comments
 (0)