Pattern Matching on Tuple

Is it OK to expect that the following compiles? If so, is it a know issue?

trait T
case class A() extends T

val list: List[T] = new A :: Nil
list.zipWithIndex.collect[(A, Int)] { case t @ (_: A, _) => t }

type mismatch;
found : (T, Int)
required: (A, Int)

1 Like

t is a (T, Int). You’ll need to re-package its members to form a new tuple of the proper type:

case (a: A, i) => a -> i
1 Like

@sangamon Thank you for your quick reply. Of course I’m aware of this. Maybe you misunderstood my question.

Long version: I wouldn’t expect this to compile because the partial function you’re passing to #collect() must produce results of type (A, Int), whereas with t you attempt to return a value of type (T, Int).

If this doesn’t match, perhaps you can rephrase your question?

1 Like

OK, the difference is that I understand t must be of type (A, Int) due to the type annotation _: A.
When I searched for related issues I did find some pattern matching issues but I was not sure so I decided to ask here first.

Through the pattern match you have established that the T member of the tuple t is of subtype A, but that doesn’t change the type of t, and it can’t.

Just for the sake of argument, assume that instead of a tuple we have this:

case class MyTuple[T](v1: T, i: Int)(val v2: T)


val list: List[MyTuple[T]] = List(MyTuple[T](A(), 0)(B()))
list.collect[MyTuple[A]] { case t @ MyTuple(_: A, _) => t }

Now we have the same successful match, but clearly that doesn’t make t a MyTuple[A] - the v2 member still has type B. This would be legal on the other hand:

list.collect[MyTuple[A]] { case MyTuple(a: A, i) => MyTuple(a, i)(a) }

Good counter example.
So what I expected from the compiler would only work if it also took into account that Tuple's unapply exposes all values directly. But the compiler has no knowledge of classes in the standard library.

One more take away is that, assumed you are concerned about repackaging resp. unnecessary memory allocation, asInstanceOf is your friend here. Correct if I’m wrong.

I’d consider this bad practice. The cast only works due to type erasure, i.e. because of a limitation of the underlying platform. It “works” as well for my example, making it blow up at runtime sometime later.

I’d first look for more benign ways to reduce object creation elsewhere - e.g. using a view to avoid creating the intermediate list from #zipWithIndex. If these measures don’t suffice, I might try to come up with some #collectWithIndex() implementation that doesn’t create the intermediate (T, Int) tuples at all. Which in turn may make me contemplate using a different data type than List. Etc.

To sum up, I’d establish whether I actually do have a performance issue at all and whether it can be attributed to object creation or some other cause, then attempt to resolve this from a more holistic perspective via means that don’t work around the type system.

1 Like