Closed
Description
Consider the following:
import scala.quoted._
trait IsExpr[T] {
type Underlying
def toExpr(x: T): Expr[Underlying]
}
// object IsExpr { type Aux[T, U] = IsExpr[T] { type Underlying = U } }
// given [U] as IsExpr.Aux[Expr[U], U] = new IsExpr[Expr[U]] {
given [U] as IsExpr[Expr[U]] = new IsExpr[Expr[U]] {
type Underlying = U
def toExpr(x: Expr[U]): Expr[U] = x
}
def f(x: Any): String = x.toString
def g[T](x: T) given (e: IsExpr[T], tu: Type[e.Underlying]): given QuoteContext => Expr[String] = {
val underlying: Expr[e.Underlying] = e.toExpr(x)
'{f($underlying)}
}
// def g[T, U](x: T) given (e: IsExpr.Aux[T, U], tu: Type[U]): given QuoteContext => Expr[String] = {
// val underlying: Expr[U] = e.toExpr(x)
// '{f($underlying)}
// }
inline def mcr: Any = ${mcrImpl}
def mcrImpl given QuoteContext: Expr[Any] = {
val x = '{1}
g(x)
}
Will output:
-- Error: ../pg/Lib.scala:21:6 -------------------------------------------------
21 | '{f($underlying)}
| ^^^^^^^^^^^
| access to value e from wrong staging level:
| - the definition is at level 0,
| - but the access is at level 1.
Sort of makes sense, since e
is indeed defined at level 0. However, two facts make me think something can be done here. First, only the path-dependent type is accessed there (presumably, since the compiler needs to know the type of underlying
), and we have it with the Type
argument. Second, the following trick works:
import scala.quoted._
trait IsExpr[T] {
type Underlying
def toExpr(x: T): Expr[Underlying]
}
object IsExpr { type Aux[T, U] = IsExpr[T] { type Underlying = U } }
given [U] as IsExpr.Aux[Expr[U], U] = new IsExpr[Expr[U]] {
// given [U] as IsExpr[Expr[U]] = new IsExpr[Expr[U]] {
type Underlying = U
def toExpr(x: Expr[U]): Expr[U] = x
}
def f(x: Any): String = x.toString
// def g[T](x: T) given (e: IsExpr[T], tu: Type[e.Underlying]): given QuoteContext => Expr[String] = {
// val underlying: Expr[e.Underlying] = e.toExpr(x)
// '{f($underlying)}
// }
def g[T, U](x: T) given (e: IsExpr.Aux[T, U], tu: Type[U]): given QuoteContext => Expr[String] = {
val underlying: Expr[U] = e.toExpr(x)
'{f($underlying)}
}
inline def mcr: Any = ${mcrImpl}
def mcrImpl given QuoteContext: Expr[Any] = {
val x = '{1}
g(x)
}
Aux
trick comes from Shapeless 2 and was used to bypass the limitations of Scala 2, where it was impossible to refer to a path-dependent type of a sibling argument from the same argument list.