Reducing frictions when use quoted expressions with tasty reflect
The problem can be demonstrated below, which is encountered in https://github.com/scalatest/scalatest/pull/1584:
def default: Term = {
type T
implicit val tp: quoted.Type[T] = expr.tpe.seal.asInstanceOf[quoted.Type[T]]
'{ DiagrammedExpr.simpleExpr(${expr.seal.cast[T]}, ${ getAnchor(expr) } ) }.unseal
}
// another use case
val left = l.seal.cast[DiagrammedExpr[T]]
val right = r.seal.cast[DiagrammedExpr[_]]
val res = apply('{$left.value}.unseal, op, Nil, '{$right.value}.unseal :: Nil).seal.cast[R]
'{ DiagrammedExpr.applyExpr[R]($left, $right :: Nil, $res, $anchor) }.unseal
There are several usability issues here:
-
P1: boilerplate to declare
Tand the implicittp -
P2: the call
.seal.castcould be simplified to be just one method call -
P3: the call
.unsealis annoying, given the expected type isTerm -
[x] P4: writing
${x.toExpr}is annoying whenxis primitive typeimport scala.quoted.autolift -
[x] P5:
tree.show(the[Context].withoutColors)is annoying, should default no color
The first use case could be improved if inside Term there was a type T abstract member along with an implicit def T: Type[T], to be used as:
def default: Term = {
import expr.T
'{ DiagrammedExpr.simpleExpr(${expr.seal}, ${ getAnchor(expr) } ) }.unseal
}
This way, you also retain more type safety (no unsafe casts in this case). One can also rename the import if several such types are imported, as in import expr.{T => exprT}.
That's the kind of tricks I used in Squid.
@LPTK Sounds like a very clever idea to me. WDYT @nicolasstucki?
That is exactly where I planned to place the abstract type T but I was missing the implicit def T trick to import them. I will try it out. Thanks @LPTK.
Right, Term is defined as type Term <: Statement. I can add the type member but not the implicit def
I'm not sure if we can do something as follows:
// library
trait TypeEvidence {
type T
val tpe: Type[T]
}
implicit def getType(implicit ev: TypeEvidence): ev.Type[ev.T] = ev.tpe
// user code
def default: Term = {
implicit val ev = expr.typeEvidence
'{ DiagrammedExpr.simpleExpr(${expr.seal.cast[ev.T]}, ${ getAnchor(expr) } ) }.unseal
}
Another idea to reduce .seal.cast:
// library
trait TypeEvidence[O] {
type T
val tpe: Type[T]
}
implicit def getType(implicit ev: TypeEvidence): ev.Type[ev.T] = ev.tpe
implicit def toExpr(expr: Term)(implicit ev: TypeEvidence[expr.type]): Expr[ev.T]
= expr.seal.cast[ev.T]
// user code
def default: Term = {
implicit val ev: TypeEvidence[expr] = expr.typeEvidence
'{ DiagrammedExpr.simpleExpr($expr, ${ getAnchor(expr) } ) }
}