6
votes

Say I have:

class Class[CC[A, B]]
class Thing[A, B <: Int]
class Test extends Class[Thing] // compile error here

I get the compiler error:

kinds of the type arguments (cspsolver.Thing) do not conform to the expected kinds of the type parameters (type CC) in class Class. cspsolver.
Thing's type parameters do not match type CC's expected parameters: type C's bounds <: Int are stricter than type B's declared bounds >: Nothing <: Any

However when I modify the code such that it looks like this:

class Class[CC[A, B]]
class Thing[A, B] {
  type B <: Int
}
class Test extends Class[Thing]

it compiles fine. Aren't they both functionally equivalent?

2
In the last example you have type parameter B and type member B. They have the same name (so only one is visible), bot they are not the same.senia
@senia, is there a case where using the same name is useful?huynhjl
@huynhjl: I guess no. But in some cases shadowing is useful: you can reuse name. There are also some partial useful abuses of shadowing in case of implicits: see this answer.senia
@senia You should turn your comment into an answer so that it can be accepted. You have the right answer and it's simple.Jean-Philippe Pellet
@Jean-PhilippePellet: thank you, but it's not an answer. I've explained why last code sample compiles, but not why the first one doesn't compiles.senia

2 Answers

1
votes

The reason is given in the compiler message. In Class you expect an unrestricted CC, while Thing has the restriction that the second type argument must be <: Int. One possibility is to add the same constraint to Class as in

class Class[CC[A,B <: Int]]
class Thing[A, B <: Int]
class Test extends Class[Thing]
0
votes

Elaborating on Petr Pudlák's explanation, here is what I assume happens: The compiler tries to unify CC[A, B] with Thing[A, B <: Int]. According to the declaration of B in CC, B's upper type-bound is Any, which is picked to instantiate B. The B in Thing, however, is expected to have Int a its upper type-bound, and the compiler thus fails with the error message you got.

This is necessary in order to preserve soundness of the type system, as illustrated in the following sketch. Assume that Thing defines an operation that relies on the fact that its B <: Int, e.g.,

class Thing[A, B <: Int] {
  def f(b: B) = 2 * b
}

If you declared Class as

class Class[CC[A,B]] {
  val c: CC
}

and Test as

class Test extends Class[Thing] {
  val t: Thing
}

without the compiler complaining, then you could make the following call

new Test().t.f(true)

which is obviously not safe.