1

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 = ??? } 

4 Answers 4

2

@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 = ??? } 
Sign up to request clarification or add additional context in comments.

2 Comments

Indeed, your code is more sound :) I wonder why you write self: T with Ordered[T] instead of trait ATrait[T <: ATrait[T]] { self: T such that you have to write with Ordered[T] for each subclass?
@Sheng, Good point. I was just working from my notes on how F-bounding works. Updated.
0

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.

Comments

0

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.

Comments

0

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.

3 Comments

But I would need to implement Ordering anyway, so I dont think that using an implicit Ordering saves me much work in my use case.
@PhilBa It makes it a lot cleaner and separates concern properly, your model currently doesn't do that and quite frankly smells.
I'm not sure how implementing logic specific to an object in that object is a code smell, but I thank you for the time you took to answer. Also please understand that this was just a specific example to a general question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.