Reputation: 732
Is it possible in Scala to enforce the implementation of a typed trait for subclasses with the type of the subclass? Ie. I'd like to define a trait which forces all subtypes to implement Ordered for only the concrete subtype. So I want to ensure that every implementation TraitImpl of the trait ATrait implements Ordered[TraitImpl]. Below is a non working example to illustrate what I am trying to do.
trait ATrait extends Ordered[_ <: ATrait] {
}
class TraitImpl extends ATrait {
override def compare(that: TraitImpl): Int = ???
}
Upvotes: 1
Views: 127
Reputation: 51271
@Sheng is right, F-bounded polymorphism is probably the way to go, but the code offered is incomplete in that it will allow the following.
trait ATrait[T <: ATrait[T]] extends Ordered[T]
class B extends ATrait[B] {
override def compare(that: B): Int = ???
}
class C extends ATrait[B] {
override def compare(that: B): Int = ???
}
This bleeding of B
into C
is not what you want. A more complete implementation would be as such.
trait ATrait[T <: ATrait[T]] {
self: T with Ordered[T] =>
}
class B extends ATrait[B] with Ordered[B] {
override def compare(that: B): Int = ???
}
class C extends ATrait[C] with Ordered[C] {
override def compare(that: C): Int = ???
}
With this you can't use Ordered[B]
in the definition of C
.
update
Or, a slightly more concise rendition as @Sheng has kindly pointed out.
trait ATrait[T <: ATrait[T]] extends Ordered[T] {self: T =>}
class B extends ATrait[B] {
def compare(that: B): Int = ???
}
class C extends ATrait[C] {
def compare(that: C): Int = ???
}
Upvotes: 2
Reputation: 28511
I think if you "step outside the box" you could fair better with a context bound. Fortunately, Scala offers you two flavours of implementing order, the second of which being through implicits.
trait A extends Ordered[T <: A[T]] {
def compare(that: T)(implicit ordering: Ordering[T]): Int = {
ordering.compare(this, that)
}
}
class TraitImpl extends A[TraitImpl]
Now you can insert the Ordering
at call site and save yourself the trouble of constantly overriding things in any form, wherever you end up calling compare
that's where that implicit will be required.
You can control your default ordering imports with some kind of package object
, so you would offer a much better importing experience to your users or colleagues.
Upvotes: 0
Reputation: 1006
You may create a F-bounded type:
trait ATrait[T <: ATrait[T]] extends Ordered[T] {
}
class TraitImpl extends ATrait[TraitImpl] {
override def compare(that: TraitImpl): Int = ???
}
Update: as pointed out by @jwvh, this is not unbreakable as class TraitImpl extends ATrait[B]
also compiles, given B is another subclass of ATrait. Please refer to his anwser for a more complete solution.
Upvotes: 0
Reputation: 755
I think that the most elegant solution would be usage of self-annotation :
trait ATtrait[T] { self: Ordered[T] =>
}
Every class implementing this trait has to also implement ordered, and you can use ordered methods inside ATrait.
Upvotes: 0