Reputation: 3838
Here is a simple example:
trait Sup {
type A
def a: A
def b: A
}
trait Sub1 extends Sup {
override type A = Product
override def a = "s" -> "s"
}
trait Sub2 extends Sup {
override type A = Serializable
override def b = "s" -> "s"
}
object SS extends Sub1 with Sub2
Obviously it will cause a compilation error, as both override type A
are mutually exclusive. This is counter-intuitive as Product & Serializable are commonly used together. Alternatively, I can define:
trait Sup {
type A
def a: A
}
trait Sub1 extends Sup {
override type A <: Product
override def a = "s" -> "s"
}
trait Sub2 extends Sup {
override type A <: Serializable
override def b = "s" -> "s"
}
object SS extends Sub1 with Sub2 {
override type A = Product with Serializable
}
This makes definition of a and b invalid, as type A hasn't been reified, in addition, the line override type A = Product with Serializable
is clearly a boilerplate and can be inferred instead.
What is the correct way to define an abstract type that allows diamond mixin, while avoid the boilerplate to explicitly define it in every implementations?
Upvotes: 1
Views: 131
Reputation: 51658
I guess you lost lower bounds.
"s" -> "s"
has type (String, String)
, which is a subtype of Product
(and Serializable
) but not a subtype of A <: Product
(or A <: Serializable
).
Try
trait Sup {
type A
def a: A
def b: A
}
trait Sub1 extends Sup {
override type A >: (String, String) <: Product
override def a = "s" -> "s"
}
trait Sub2 extends Sup {
override type A >: (String, String) <: Serializable
override def b = "s" -> "s"
}
object SS extends Sub1 with Sub2 {
override type A = Product with Serializable
}
SS.a: (String, String)
SS.b: (String, String)
implicitly[SS.A =:= (Product with Serializable)]
If you specify return type of Sub1#a
, Sub2#b
to be A
(above they were inferred to be (String, String)
i.e. return type was narrowed upon method overriding) then
trait Sup {
type A
def a: A
def b: A
}
trait Sub1 extends Sup {
override type A >: (String, String) <: Product
override def a: A = "s" -> "s"
}
trait Sub2 extends Sup {
override type A >: (String, String) <: Serializable
override def b: A = "s" -> "s"
}
object SS extends Sub1 with Sub2 {
override type A = Product with Serializable
}
SS.a: Product with Serializable
SS.b: Product with Serializable
implicitly[SS.A =:= (Product with Serializable)]
You can do even
object SS extends Sub1 with Sub2 {
override type A >: (String, String) <: Product with Serializable
}
Upvotes: 5