Reputation: 575
An advantage of generic type parameters over abstract type members seems to be that the former can be equalled, for example:
trait A[X]
trait B[Y]
trait C[Z] extends A[Z] with B[Z]
Similarly:
trait C[Z] {
self: A[Z] with B[Z] =>
}
The assignment of the type parameters says in fact three things: X = Z, Y = Z, and hereby X = Y.
The first case can be represented analogously:
trait A { type X }
trait B { type Y }
class C extends A with B { type X = Z; type Y = Z; type Z }
However, is something like the second case possible with abstract type members? The following solution won't work since type "Z" cannot be referred to from the self-type definition, which itself must come first:
trait C {
self: A with B { type X = Z; type Y = Z } =>
type Z
}
Strangely, the following seems to compile even if the type requirement of "b" is obviously violated:
trait C2 {
val a: A { type X = Z }
val b: B { type Y = Z }
type Z
}
class A2 extends A { type X = Int }
class B2 extends B { type Y = String }
class D extends C2 {
override val a = new A2
override val b = new B2
type Z = Int
}
Upvotes: 2
Views: 123
Reputation: 17431
You can use a witness that the type parameters are equal, either =:=
or scalaz.Leibniz.===
(which is more general, but means depending on scalaz).
class C extends A with B { type Z; val w1: X =:= Z; val w2: Y =:= Z }
Then there is no way to instantiate this if the types are not equal, and you can use w1
and w2
to "convert" values of type X
or Y
to type Z
.
Upvotes: 2