5

I have two subtypes that I need to be F-bounded polymorphic by a type A, and a subtype of one of those subtypes, i.e.

trait A[T <: A[T]] {
  def x: T
}
trait Ter extends A[Ter]
trait For extends A[For]
trait C extends Ter

Next I try to implement a concrete type

case class F2(l: List[A[_]]) extends For {
  def x: For = F2(l.map(_.x))
}

But this fails to compile with:

<console>:11: error: type mismatch;
 found   : List[Any]
 required: List[A[_]]
         def x: For = F2(l.map(_.x))
                              ^

So, google says I need to use existential types, which makes sense, so I try:

import scala.language.existentials

type SomeA = T forSome { type T <: A[T] }

case class F1(l: List[SomeA]) extends For {
  def x: For = F1(l.map(_.x))
}

But, now I face a new problem when I try to instantiate

trait Example {
  val b: Ter
  val c: C
  val d: For

  // Works fine
  val l1: List[A[_]] = List(b, c, d)
  // But this doesn't work, fails to compile (see below)
  val l2: List[SomeA] = List(b, c, d)

  val f1 = F1(l2)
}

The compile error:

<console>:22: error: type mismatch;
 found   : C
 required: SomeA
    (which expands to)  T forSome { type T <: A[T] }
         val l2: List[SomeA] = List(b, c, d)
                                       ^

Why do I get this error? Surely C is a subtype of Ter, which in turn is a subtype of A[Ter], therefore C is a subtype of A[Ter], therefore there exists a T namely Ter such that C is a subtype of A[T], therefore C is a subtype of SomeA.

It's as if the transitivity of subtyping isn't working. When I hack it with c.asInstanceOf[SomeA] my code compiles and my unit tests pass. Could it be a compiler bug?

I also thought that List[A[_]] was stronger typing than List[SomeA], i.e. the former was saying the list consists of A[T] some fixed type T, where the latter is saying the list consists of A[T] where T is not fixed.

BOUNS If you can explain why the currently accepted answer works, i.e. why the compiler cannot work out that the type is valid without the ascription.

samthebest
  • 28,224
  • 21
  • 93
  • 129

1 Answers1

1

I guess the compiler needs some help. The following should work:

val l2 = List[SomeA](b, c: Ter, d)
  • Nope :(. Try it in a shell, I get `:24: error: type mismatch; found : List[A[_ >: For with Ter <: a="">: For with Ter <: a="" c:="" d="" expands="" forsome="" l2:="" list="" object="" required:="" t="" ter="" to="" type="" val=""> – samthebest Feb 23 '15 at 18:09
  • Also, I would prefer a way where I can avoid up casting. – samthebest Feb 23 '15 at 18:10
  • Did you try `val l2 = List[SomeA](b, c: Ter, d)` instead of `val l2: List[SomeA] = List(b, c: Ter, d)`? I don't know how to avoid the type cast. At least it is checked at compile time unlike `c.asInstanceOf[Ter]`. – Andreas Flueckiger Feb 23 '15 at 18:35
  • 1
    Yup, my bad, thanks. Accepting this answer since it does indeed get around `asInstanceOf` and is indeed type checked. Nevertheless I'd like to better understand *why* the compiler cannot work this out itself. Why do I have to ascribe the type? Is it just that they have not yet implemented this level of inference yet?? – samthebest Feb 23 '15 at 20:55
  • Thank you! I also would like to know why the compiler cannot work this out itself. – Andreas Flueckiger Feb 23 '15 at 20:59