trait A {
type T
def test(t: T): Unit
}
case class B[S <: A](a: S, t : S#T) {
def test() = a.test(t) // Error: type mismatch;
// found : B.this.t.typ
Instead of a type projection you can use the dependent type a.T
:
trait A {
type T
def test(t: T): Unit
}
case class B[S <: A](a: S)(t : a.T) {
def test() = a.test(t)
}
I could come up with encodings (removed the type parameters for simplification):
scala> :paste
// Entering paste mode (ctrl-D to finish)
def test0(a: A)(t : a.T) = a.test(t)
abstract class B{
val a: A
val t: a.T
def test = a.test(t)
}
// Exiting paste mode, now interpreting.
test0: (a: A)(t: a.T)Unit
defined class B
This on the other hand didn't work with case classes arguments (nor classes' for that matter).
One of the reasons your encoding wouldn't work:
scala> def test1(a: A)(t : A#T) = a.test(t)
<console>:12: error: type mismatch;
found : t.type (with underlying type A#T)
required: a.T
def test1(a: A)(t : A#T) = a.test(t)
The important part is required: a.T
(versus A#T
). The test method in A
doesn't take any T, it takes T this.T
, or in other words, the T belonging to one particular instance of A.
Compiler has not sufficient evidence that S#T
can be used as argument for test
in concrete instance.
Consider this hypotecical example for weakened scala compiler
trait A2 extends A{
type T <: AnyRef
}
class A3 extends A2{
override type T = Integer
def test(t: Integer): Unit = println(t * 2)
}
So B[A2]
should accept instance of A3
along with anything that is <: AnyRef
while A3
needs exactly Integer
for its own test
implementation
You can catch concrete type in the definition of B
, to make sure what type will be used
case class B[S <: A, ST](a: S {type T = ST}, t: ST) {
def test() = a.test(t)
}