Unexpected behavior with compound types

117 views Asked by At

I have 3 traits

  trait A
  trait B
  trait AB extends A with B

and a method

def collect[E: Manifest](list: List[Any]) =
    list flatMap {
      case record: E => Some(record)
      case _         => None
    }

For the given list

val list = new A {} :: new A {} :: new A with B {} :: new AB {} :: Nil

I invoked collect with different types

collect[A with B](list) // collect all elements from the list
collect[AB](list) // collect only the last element

Could anyone explain the difference in behavior for A with B and AB types?

1

There are 1 answers

0
Eduardo Pareja Tobes On BEST ANSWER

It is highly unintuitive but it conforms to the specification. First, quoting from the Manifest docs (emphasis mine):

A Manifest[T] is an opaque descriptor for type T. Its supported use is to give access to the erasure of the type

So in collect[A with B] we are matching the erasure of A with B. And what is that? if we look at the Type erasure section of the spec, we read:

The erasure of a compound type T1 with ... with Tn is the erasure of the intersection dominator of T1, ..., Tn

and the intersection dominator is defined as (again, emphasis mine)

The intersection dominator of a list of types T1, ..., Tn is computed as follows. Let Ti1, ..., Tim be the subsequence of types Ti which are not supertypes of some other type Tj. If this subsequence contains a type designator Tc that refers to a class which is not a trait, the intersection dominator is Tc. Otherwise, the intersection dominator is the first element of the subsequence, Ti1

In our case the subsequence is A,B as they don't have any subtyping relationship, and thus the erasure of A with B is A, so that in collect[A with B] we are actually matching A.

You can easily see this behavior by looking at the output of collect[B with A], and/or adding a new B {} to your list.