I defined the following trait in Scala 3:
trait A[T <: Tuple]
Using Scala 3 macros, I then create objects of this trait performing further checks on the actual types of the tuple T
; in particular, I want to check that all the types (T_1
, …, T_n
) of the tuple T
are subtypes of another given type, B
:
trait B
private def allSubtypesOfB[T <: Tuple: Type](using quotes: Quotes): Boolean = {
import quotes.reflect.*
case '[Nothing] => false // I don't want nothing to be in T
case '[head *: tail] if TypeRepr.of[head] <:< TypeRepr.of[B] => allSubtypesOfB[tail]
case '[EmptyTuple] => true
case _ => false
}
inline def createA[T <: Tuple] = ${ createAImpl[T] }
private def createAImpl[T <: Tuple: Type](using quotes: Quotes): Expr[A[T]] = {
import quotes.reflect.*
if !allSubtypesOfB[T] then report.error("All types in T must be subtypes of B")
// ... create instance of A
}
The problem is that later I need to call, for each type in the tuple type T
, a method that has the following signature:
def doSomethingWithBSubtype[T <: B] = ??? // Do something with type T
So, the code would look something like this:
private def createAImpl[T <: Tuple: Type](using quotes: Quotes): Expr[A[T]] = {
import quotes.reflect.*
if !allSubtypesOfB[T] then report.error("All types in T must be subtypes of B")
Type.of[T] match {
case '[head *: tail] => doSomethingWithBSubtype[head]
case '[EmptyTuple] => ???
}
// ... create instance of A
}
This code won't compile as the compiler says that head
must be a subtype of B
to be used in the method doSomethingWithBSubtype
. However, with the if
preceding the match
case, I already ensured that all types inside T
are subtypes of B
. Is there a way to force the compiler to recognise head
as a subtype of B
?