Hi,
I was looking for a way to get a precise (literal-like) tuple in a type parameter of my generic type, like:
case class Foo[T](t: T)
val foo = Foo(("foo", 42))
I want above to be Foo[("foo", 42)]
, not Foo[(String, Int)]
without additional type ascription.
I managed to do this with the following macro:
private def preciseImpl[T <: Tuple: Type](expr: Expr[T])(using Quotes): Expr[Any] =
import quotes.reflect.*
val typeRepr: TypeRepr = expr.asTerm match
case Inlined(_, Nil, Apply(TypeApply(Select(Ident(name), _), _), innerTerms)) if name.startsWith("Tuple") =>
val arity = name.dropWhile(c => !c.isDigit).toInt
val innerTypes = innerTerms.collect { case Literal(a) => ConstantType(a) }
defn.TupleClass(arity).typeRef.appliedTo(innerTypes)
typeRepr.asType match
case '[t] => '{ ${expr}.asInstanceOf[t] }
It works to the point where, I can use it, but I’m curious about the last expression. If I change that to (seemingly more sensible):
typeRepr.asType match
case '[t] => expr.asExprOf[t]
It throws an exception:
[error] Exception occurred while executing macro expansion.
[error] java.lang.Exception: Expr cast exception: scala.Tuple2.apply[java.lang.String, scala.Int]("foo", 42)
[error] of type: scala.Tuple2[java.lang.String, scala.Int]
[error] did not conform to type: scala.Tuple2["foo", 42]
Why is that?