12

This compiles:

import scala.collection._

trait Foo[A, +This <: SortedSet[A] with SortedSetLike[A,This]]
extends SortedSetLike[A, This] { this: This =>

  def bar: This = (this: SortedSetLike[A,This]).empty

}

But if the upcast is removed it fails to compile:

import scala.collection._

trait Foo[A, +This <: SortedSet[A] with SortedSetLike[A,This]]
extends SortedSetLike[A, This] { this: This =>

  def bar: This = this.empty

}

Why? From the extends clause we know that Foo is a SortedSetLike[A, This], so the upcast is valid of course - but doesn't this show that the compiler has allowed conflicting inheritance to occur?

Robin Green
  • 32,079
  • 16
  • 104
  • 187
  • 4
    I don't know the exact details of this case, but it's another example of the horrible stuff that can happen because methods in subclasses can have more specific return types than the method signatures they implement. – Travis Brown Jun 27 '15 at 03:26

1 Answers1

6

The SortedSetLike trait inherits the empty method from SetLike.

/** The empty set of the same type as this set
* @return  an empty set of type `This`.
*/
def empty: This

But SortedSet overrides the empty method and has an explicit return type:

/** Needs to be overridden in subclasses. */
override def empty: SortedSet[A] = SortedSet.empty[A]

Since you specify that This is a subclass of SortedSet the compiler will find SortedSet's implementation of empty first, which returns a SortedSet. The compiler does not know how to convert the resulting SortedSet to your This subclass.

But if you upcast to the SortedSetLike trait the compiler will find its empty method which returns a This.

Chris P.
  • 116
  • 1
  • 6