Let’s assume I have a declaration like
type TupleOf[Item, Size <: Int] <: Tuple = Size match
case 0 => EmptyTuple
case 1 => Item *: EmptyTuple
case 2 => Item *: Item *: EmptyTuple
(or even a more generic one
type TupleOf[Item, Size <: Int] <: Tuple = Size match
case 0 => EmptyTuple
case compiletime.ops.int.S[prevSize] => Item *: TupleOf[Item, prevSize]
though that doesn’t matter for this question).
This works:
def f(a: TupleOf[String, 2]) = ()
f(("hello", "world"))
This also works:
def g(a: TupleOf[?, 2]) = ()
g(("hello", "world"))
This DOES NOT:
def h(a: TupleOf[String, ?]) = ()
h(("hello", "world"))
The message is:
[E007] Type Mismatch Error:
h(("hello", "world"))
^^^^^^^^^^^^^^^^^^
Found: (String, String)
Required: TupleOf[String, ?]
Note: a match type could not be fully reduced:
trying to reduce TupleOf[String, ?]
failed since selector Any
does not match case (0 : Int) => EmptyTuple
and cannot be shown to be disjoint from it either.
Therefore, reduction cannot advance to the remaining cases
So, IIUC, it can’t prove that (String, String)
is disjoint from EmptyTuple
.
The first question: What is the status of this thing?
- Is it by design?
- Or is it considered as a bug (a temporary state, a to-do) of the compiler?
The second question is: Are there some suggested workarounds?
Specifically: is there a way to specify an arbitrary-sized tuple with items of a predefined base type without using implicits?
I understand that I can do
trait RequiredBase
def myMethod[T <: Tuple](a: T)(using
Tuple.Union[T] <:< RequiredBase
): T = ???
But is it possible to do that without using implicits?