Skip to content

Commit 0e1c29d

Browse files
authored
Merge pull request #7299 from dotty-staging/remove-asFunction-implicit-class
Replace AsFunction implicit class with Expr.reduce
2 parents 5bcb1ce + 1979749 commit 0e1c29d

File tree

26 files changed

+119
-111
lines changed

26 files changed

+119
-111
lines changed

docs/docs/reference/metaprogramming/macros.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,24 +135,20 @@ expressiveness.
135135

136136
### From `Expr`s to Functions and Back
137137

138-
The `Expr` companion object contains an implicit `AsFunction` conversion that turns a tree
138+
The `Expr` companion object contains a `betaReduce` conversion that turns a tree
139139
describing a function into a function mapping trees to trees.
140140
```scala
141141
object Expr {
142142
...
143-
implicit class AsFunction[...](...) { ... }
143+
def betaReduce[...](...)(...): ... = ...
144144
}
145145
```
146-
This decorator gives `Expr` the `apply` operation of an applicative functor, where `Expr`s
147-
over function types can be applied to `Expr` arguments. The definition
148-
of `AsFunction(f).apply(x)` is assumed to be functionally the same as
146+
The definition of `Expr.betaReduce(f)(x)` is assumed to be functionally the same as
149147
`'{($f)($x)}`, however it should optimize this call by returning the
150148
result of beta-reducing `f(x)` if `f` is a known lambda expression.
151-
152-
The `AsFunction` decorator distributes applications of `Expr` over function
153-
arrows:
149+
`Expr.betaReduce` distributes applications of `Expr` over function arrows:
154150
```scala
155-
AsFunction(_).apply: Expr[S => T] => (Expr[S] => Expr[T])
151+
Expr.betaReduce(_): Expr[(T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R])
156152
```
157153
Its dual, let’s call it `reflect`, can be defined as follows:
158154
```scala

library/src/scala/quoted/Expr.scala

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,31 @@ package quoted {
2828
/** Converts a tuple `(T1, ..., Tn)` to `(Expr[T1], ..., Expr[Tn])` */
2929
type TupleOfExpr[Tup <: Tuple] = Tuple.Map[Tup, [X] =>> (given QuoteContext) => Expr[X]]
3030

31-
implicit class AsFunction[F, Args <: Tuple, R](f: Expr[F])(given tf: TupledFunction[F, Args => R], qctx: QuoteContext) {
32-
/** Beta-reduces the function appication. Generates the an expression only containing the body of the function */
33-
def apply[G](given tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = {
34-
import qctx.tasty._
35-
tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]])
36-
}
31+
/** `Expr.betaReduce(f)(x1, ..., xn)` is functionally the same as `'{($f)($x1, ..., $xn)}`, however it optimizes this call
32+
* by returning the result of beta-reducing `f(x1, ..., xn)` if `f` is a known lambda expression.
33+
*
34+
* `Expr.betaReduce` distributes applications of `Expr` over function arrows
35+
* ```scala
36+
* Expr.betaReduce(_): Expr[(T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R])
37+
* ```
38+
*/
39+
def betaReduce[F, Args <: Tuple, R, G](f: Expr[F])(given tf: TupledFunction[F, Args => R], tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]], qctx: QuoteContext): G = {
40+
import qctx.tasty._
41+
tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]])
3742
}
3843

39-
implicit class AsContextualFunction[F, Args <: Tuple, R](f: Expr[F])(given tf: TupledFunction[F, (given Args) => R], qctx: QuoteContext) {
40-
/** Beta-reduces the function appication. Generates the an expression only containing the body of the function */
41-
def apply[G](given tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = {
42-
import qctx.tasty._
43-
tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]])
44-
}
44+
/** `Expr.betaReduceGiven(f)(x1, ..., xn)` is functionally the same as `'{($f)(given $x1, ..., $xn)}`, however it optimizes this call
45+
* by returning the result of beta-reducing `f(given x1, ..., xn)` if `f` is a known lambda expression.
46+
*
47+
* `Expr.betaReduceGiven` distributes applications of `Expr` over function arrows
48+
* ```scala
49+
* Expr.betaReduceGiven(_): Expr[(given T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R])
50+
* ```
51+
* Note: The
52+
*/
53+
def betaReduceGiven[F, Args <: Tuple, R, G](f: Expr[F])(given tf: TupledFunction[F, (given Args) => R], tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]], qctx: QuoteContext): G = {
54+
import qctx.tasty._
55+
tg.untupled(args => qctx.tasty.internal.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]])
4556
}
4657

4758
/** Returns a null expresssion equivalent to `'{null}` */

tests/pos/i6783.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import scala.quoted._
22

3-
def testImpl(f: Expr[(Int, Int) => Int])(given QuoteContext): Expr[Int] = f('{1}, '{2})
3+
def testImpl(f: Expr[(Int, Int) => Int])(given QuoteContext): Expr[Int] = Expr.betaReduce(f)('{1}, '{2})
44

55
inline def test(f: (Int, Int) => Int) = ${
66
testImpl('f)

tests/run-macros/gestalt-optional-staging/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ object Optional {
2424
// FIXME fix issue #5097 and enable private
2525
/*private*/ def mapImpl[A >: Null : Type, B >: Null : Type](opt: Expr[Optional[A]], f: Expr[A => B])(given QuoteContext): Expr[Optional[B]] = '{
2626
if ($opt.isEmpty) new Optional(null)
27-
else new Optional(${f('{$opt.value})})
27+
else new Optional(${Expr.betaReduce(f)('{$opt.value})})
2828
}
2929

3030
}

tests/run-macros/i4734/Macro_1.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ object Macros {
1313
while (i < size) {
1414
${
1515
for (j <- new UnrolledRange(0, unrollSize)) '{
16-
val index = i + ${j}
16+
val index = i + $j
1717
val element = ($seq)(index)
18-
${ f('element) } // or `($f)(element)` if `f` should not be inlined
18+
${ Expr.betaReduce(f)('element) } // or `($f)(element)` if `f` should not be inlined
1919
}
2020
}
2121
i += ${unrollSize}

tests/run-macros/i4735/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object Macro {
1717
${
1818
for (j <- new UnrolledRange(0, unrollSize)) '{
1919
val element = ($seq)(i + ${j})
20-
${f('element)} // or `($f)(element)` if `f` should not be inlined
20+
${Expr.betaReduce(f)('element)} // or `($f)(element)` if `f` should not be inlined
2121
}
2222
}
2323
i += ${unrollSize}

tests/run-macros/i7008/macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ def mcrProxy(expr: Expr[Boolean])(given QuoteContext): Expr[Unit] = {
1515
def mcrImpl[T](func: Expr[Seq[Box[T]] => Unit], expr: Expr[T])(given ctx: QuoteContext, tt: Type[T]): Expr[Unit] = {
1616
import ctx.tasty._
1717
val arg = Expr.ofSeq(Seq('{(Box($expr))}))
18-
func(arg)
18+
Expr.betaReduce(func)(arg)
1919
}

tests/run-macros/quote-inline-function/quoted_1.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ object Macros {
1212
var i = $start
1313
val j = $end
1414
while (i < j) {
15-
${f.apply('i)}
15+
${Expr.betaReduce(f)('i)}
1616
i += 1
1717
}
1818
while {
19-
${f.apply('i)}
19+
${Expr.betaReduce(f)('i)}
2020
i += 1
2121
i < j
2222
} do ()

tests/run-macros/quote-matcher-symantics-2/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ object StringNum extends Symantics[String] {
7272
def value(x: Int)(given QuoteContext): Expr[String] = Expr(x.toString)
7373
def plus(x: Expr[String], y: Expr[String])(given QuoteContext): Expr[String] = '{ s"${$x} + ${$y}" } // '{ x + " + " + y }
7474
def times(x: Expr[String], y: Expr[String])(given QuoteContext): Expr[String] = '{ s"${$x} * ${$y}" }
75-
def app(f: Expr[String => String], x: Expr[String])(given QuoteContext): Expr[String] = f(x) // functions are beta reduced
75+
def app(f: Expr[String => String], x: Expr[String])(given QuoteContext): Expr[String] = Expr.betaReduce(f)(x)
7676
def lam(body: Expr[String] => Expr[String])(given QuoteContext): Expr[String => String] = '{ (x: String) => ${body('x)} }
7777
}
7878

tests/run-macros/quote-matching-optimize-1/Macro_1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ object Macro {
99

1010
def optimize(x: Expr[Any]): Expr[Any] = x match {
1111
case '{ type $t; ($ls: List[`$t`]).filter($f).filter($g) } =>
12-
optimize('{ $ls.filter(x => ${f('x)} && ${g('x)}) })
12+
optimize('{ $ls.filter(x => ${Expr.betaReduce(f)('x)} && ${Expr.betaReduce(g)('x)}) })
1313

1414
case '{ type $t; type $u; type $v; ($ls: List[`$t`]).map[`$u`]($f).map[`$v`]($g) } =>
15-
optimize('{ $ls.map(x => ${g(f('x))}) })
15+
optimize('{ $ls.map(x => ${Expr.betaReduce(g)(Expr.betaReduce(f)('x))}) })
1616

1717
case '{ type $t; ($ls: List[`$t`]).filter($f).foreach[Unit]($g) } =>
18-
optimize('{ $ls.foreach(x => if (${f('x)}) ${g('x)} else ()) })
18+
optimize('{ $ls.foreach(x => if (${Expr.betaReduce(f)('x)}) ${Expr.betaReduce(g)('x)} else ()) })
1919

2020
case _ => x
2121
}

tests/run-macros/quote-matching-optimize-2/Macro_1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ object Macro {
1111

1212
def optimize(x: Expr[Any]): Expr[Any] = x match {
1313
case '{ ($ls: List[$t]).filter($f).filter($g) } =>
14-
optimize('{ $ls.filter(x => ${f('x)} && ${g('x)}) })
14+
optimize('{ $ls.filter(x => ${Expr.betaReduce(f)('x)} && ${Expr.betaReduce(g)('x)}) })
1515

1616
case '{ type $u; type $v; ($ls: List[$t]).map[`$u`]($f).map[`$v`]($g) } =>
17-
optimize('{ $ls.map(x => ${g(f('x))}) })
17+
optimize('{ $ls.map(x => ${Expr.betaReduce(g)(Expr.betaReduce(f)('x))}) })
1818

1919
case '{ ($ls: List[$t]).filter($f).foreach[$u]($g) } =>
20-
optimize('{ $ls.foreach[Any](x => if (${f('x)}) ${g('x)} else ()) })
20+
optimize('{ $ls.foreach[Any](x => if (${Expr.betaReduce(f)('x)}) ${Expr.betaReduce(g)('x)} else ()) })
2121

2222
case _ => x
2323
}

tests/run-macros/quote-unrolled-foreach/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ object Macro {
1515
println("<log> start loop")
1616
${
1717
@tailrec def loop(j: Int, acc: Expr[Unit]): Expr[Unit] =
18-
if (j >= 0) loop(j - 1, '{ ${f('{$seq(i + ${j})})}; $acc })
18+
if (j >= 0) loop(j - 1, '{ ${Expr.betaReduce(f)('{$seq(i + ${j})})}; $acc })
1919
else acc
2020
loop(unrollSize - 1, '{})
2121
}

tests/run-macros/tasty-seal-method/quoted_1.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ object Asserts {
1414
fn.tpe.widen match {
1515
case Type.IsMethodType(_) =>
1616
args.size match {
17-
case 0 => fn.seal.cast[() => Int].apply()
18-
case 1 => fn.seal.cast[Int => Int].apply('{0})
19-
case 2 => fn.seal.cast[(Int, Int) => Int].apply('{0}, '{0})
20-
case 3 => fn.seal.cast[(Int, Int, Int) => Int].apply('{0}, '{0}, '{0})
17+
case 0 => Expr.betaReduce(fn.seal.cast[() => Int])()
18+
case 1 => Expr.betaReduce(fn.seal.cast[Int => Int])('{0})
19+
case 2 => Expr.betaReduce(fn.seal.cast[(Int, Int) => Int])('{0}, '{0})
20+
case 3 => Expr.betaReduce(fn.seal.cast[(Int, Int, Int) => Int])('{0}, '{0}, '{0})
2121
}
2222
}
2323
case _ => x
@@ -35,10 +35,10 @@ object Asserts {
3535
case Apply(fn, args) =>
3636
val pre = rec(fn)
3737
args.size match {
38-
case 0 => pre.seal.cast[() => Any].apply().unseal
39-
case 1 => pre.seal.cast[Int => Any].apply('{0}).unseal
40-
case 2 => pre.seal.cast[(Int, Int) => Any].apply('{0}, '{0}).unseal
41-
case 3 => pre.seal.cast[(Int, Int, Int) => Any].apply('{0}, '{0}, '{0}).unseal
38+
case 0 => Expr.betaReduce(pre.seal.cast[() => Any])().unseal
39+
case 1 => Expr.betaReduce(pre.seal.cast[Int => Any])('{0}).unseal
40+
case 2 => Expr.betaReduce(pre.seal.cast[(Int, Int) => Any])('{0}, '{0}).unseal
41+
case 3 => Expr.betaReduce(pre.seal.cast[(Int, Int, Int) => Any])('{0}, '{0}, '{0}).unseal
4242
}
4343
case _ => term
4444
}

tests/run-macros/tasty-unsafe-let/quoted_1.scala

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

1313
import qctx.tasty.{let => letTerm}
1414
letTerm(rhsTerm) { rhsId =>
15-
body(rhsId.seal.asInstanceOf[Expr[T]]).unseal // Dangerous uncheked cast!
15+
Expr.betaReduce(body)(rhsId.seal.asInstanceOf[Expr[T]]).unseal // Dangerous uncheked cast!
1616
}.seal.cast[Unit]
1717
}
1818

tests/run-staging/i3876-b.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ object Test {
1111
f
1212
}
1313

14-
println(run(f2(x)))
15-
println(withQuoteContext(f2(x).show))
14+
println(run(Expr.betaReduce(f2)(x)))
15+
println(withQuoteContext(Expr.betaReduce(f2)(x).show))
1616
}
1717
}

tests/run-staging/i3876-c.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ object Test {
1111
f
1212
}
1313

14-
println(run(f3(x)))
15-
println(withQuoteContext(f3(x).show)) // TODO improve printer
14+
println(run(Expr.betaReduce(f3)(x)))
15+
println(withQuoteContext(Expr.betaReduce(f3)(x).show)) // TODO improve printer
1616
}
1717
}

tests/run-staging/i3876-d.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ object Test {
99
def f4(given QuoteContext): Expr[Int => Int] = '{
1010
inlineLambda
1111
}
12-
println(run(f4(x)))
13-
println(withQuoteContext(f4(x).show))
12+
println(run(Expr.betaReduce(f4)(x)))
13+
println(withQuoteContext(Expr.betaReduce(f4)(x).show))
1414
}
1515

1616
inline def inlineLambda <: Int => Int = x => x + x

tests/run-staging/i3876-e.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ object Test {
99
def f4(given QuoteContext): Expr[Int => Int] = '{
1010
inlineLambda
1111
}
12-
println(run(f4(x)))
13-
println(withQuoteContext(f4(x).show))
12+
println(run(Expr.betaReduce(f4)(x)))
13+
println(withQuoteContext(Expr.betaReduce(f4)(x).show))
1414
}
1515

1616
inline def inlineLambda <: Int => Int = x => x + x

tests/run-staging/i3876.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object Test {
88

99
def f(given QuoteContext): Expr[Int => Int] = '{ (x: Int) => x + x }
1010

11-
println(run(f(x)))
12-
println(withQuoteContext(f(x).show))
11+
println(run(Expr.betaReduce(f)(x)))
12+
println(withQuoteContext(Expr.betaReduce(f)(x).show))
1313
}
1414
}

tests/run-staging/i5144b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import scala.quoted.staging._
33

44
object Test {
55
given Toolbox = Toolbox.make(getClass.getClassLoader)
6-
def eval1(ff: Expr[Int => Int])(given QuoteContext): Expr[Int] = ff('{42})
6+
def eval1(ff: Expr[Int => Int])(given QuoteContext): Expr[Int] = Expr.betaReduce(ff)('{42})
77

88
def peval1()(given QuoteContext): Expr[Unit] = '{
99
def f(x: Int): Int = ${eval1('f)}

tests/run-staging/i6281.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ object Test extends App {
2727
}
2828
// for reify, we need type tags for E and also strangely for L.
2929
implicit def cons [E, L <: HList](given Effects[L])(given Type[E])(given Type[L])(given QuoteContext): Effects[E :: L] = new Effects[E :: L] {
30-
def reify[A](given Type[A]) = m => '{ k => ${ Effects[L].reify[E] { m( a => Effects[L].reflect[E]('k(a))) } }}
31-
def reflect[A](given Type[A]) = m => k => Effects[L].reflect[E] { m('{ a => ${ Effects[L].reify[E]( k('a)) } })}
30+
def reify[A](given Type[A]) = m => '{ k => ${ Effects[L].reify[E] { m( a => Effects[L].reflect[E](Expr.betaReduce('k)(a))) } }}
31+
def reflect[A](given Type[A]) = m => k => Effects[L].reflect[E] { Expr.betaReduce(m)('{ a => ${ Effects[L].reify[E]( k('a)) } })}
3232
}
3333
def Effects[L <: HList](given Effects[L]): Effects[L] = summon[Effects[L]]
3434

@@ -45,7 +45,7 @@ object Test extends App {
4545
val effects = cons[Boolean, RS2](given cons[Int, String :: HNil](given cons[String, HNil](given empty)))
4646
println(effects.reify[Int] { m }.show)
4747

48-
val res : Expr[Stm[Int, RS]] = '{ k => ${ Effects[RS2].reify[Boolean] { m(a => Effects[RS2].reflect[Boolean]('k(a))) }}}
48+
val res : Expr[Stm[Int, RS]] = '{ k => ${ Effects[RS2].reify[Boolean] { m(a => Effects[RS2].reflect[Boolean](Expr.betaReduce('k)(a))) }}}
4949
println(res.show)
5050
}
5151

tests/run-staging/quote-ackermann-1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ object Test {
1515
def ackermann(m: Int)(given QuoteContext): Expr[Int => Int] = {
1616
if (m == 0) '{ n => n + 1 }
1717
else '{ n =>
18-
def `ackermann(m-1)`(n: Int): Int = ${ackermann(m - 1)('n)} // Expr[Int => Int] applied to Expr[Int]
18+
def `ackermann(m-1)`(n: Int): Int = ${Expr.betaReduce(ackermann(m - 1))('n)} // Expr[Int => Int] applied to Expr[Int]
1919
def `ackermann(m)`(n: Int): Int =
2020
if (n == 0) `ackermann(m-1)`(1) else `ackermann(m-1)`(`ackermann(m)`(n - 1))
2121
`ackermann(m)`(n)

tests/run-staging/quote-fun-app-1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ object Test {
1212
println(f(43))
1313
}
1414

15-
def f1(given QuoteContext): Expr[Int => Int] = '{ n => ${f2('n)} }
16-
def f2(given QuoteContext): Expr[Int => Int] = '{ n => ${f3('n)} }
17-
def f3(given QuoteContext): Expr[Int => Int] = '{ n => ${f4('n)} }
15+
def f1(given QuoteContext): Expr[Int => Int] = '{ n => ${Expr.betaReduce(f2)('n)} }
16+
def f2(given QuoteContext): Expr[Int => Int] = '{ n => ${Expr.betaReduce(f3)('n)} }
17+
def f3(given QuoteContext): Expr[Int => Int] = '{ n => ${Expr.betaReduce(f4)('n)} }
1818
def f4(given QuoteContext): Expr[Int => Int] = '{ n => n }
1919
}

0 commit comments

Comments
 (0)