Reputation: 125
Consider this simple binary tree type:
abstract class BinaryTree[T] {
type Repr <: BinaryTree[T]
def left: Repr
def value: T
def right: Repr
def height: Int
def add(elem: T): Repr =
if ( left.height <= right.height ) copy( left.add( elem ), right)
else copy( left, right.add(elem))
def copy( left: Repr, right: Repr ) : Repr
}
abstract class BinarySearchTree[T] extends BinaryTree[T] {
type Repr <: BinarySearchTree[T]
def ordering: Ordering[T]
override def add(elem: T) =
if ( ordering.equiv( elem, value ) ) this.asInstanceOf[Repr]
else if ( ordering.lt(elem, value) ) copy( left.add( elem ), right )
else copy( left, right.add( elem ) )
}
class ScapegoatTree[T]( val value: T, val left: ScapegoatTree[T], val right: ScapegoatTree[T] )(implicit val ordering: Ordering[T])
extends BinarySearchTree[T] {
type Repr = ScapegoatTree[T]
def add = this /* snip */
def copy( left: ScapegoatTree[T], right: ScapegoatTree[T] ) = new ScapegoatTree( value, left, right )
}
class BalancedBinaryTree[T]( val value: T, val left: BalancedBinaryTree[T], val right: BalancedBinaryTree[T] )(implicit val ordering: Ordering[T])
extends BinarySearchTree[T] {
type Repr = BalancedBinaryTree[T]
def add = this /* snip */
def copy( left: BalancedBinaryTree[T], right: BalancedBinaryTree[T] ) = new BalancedBinaryTree( value, left, right )
}
Using Scala 2.9 in Scala-IDE, the add method fails to compile because copy takes a Repr
while left.add(elem)
and right.add(elem)
return a Repr#Repr
. As far as I can tell, Repr#Repr
must always be a subtype of Repr
, and therefore it should be safe. Have I failed to consider some case, or is this one of those fortunately rare instances where the type system prevents you from doing something that ought to work?
Also, is there any way I could change my type definition so this would be legal, or assert to the compiler that Repr#Repr
must be a subtype of Repr
(aside from a cast, I mean). I'm still getting used to Scala and I'm not sure I've fully comprehended the type system yet. I did get it to compile by writing an implicit method that does a cast, but that feels like more of a workaround than a solution.
Thanks in advance for your help.
Edit: I should add that in my actual implementation of this I have subclasses that further restrict the type of Repr, if that makes a difference.
Edit 2: Added more code, as requested. This issue appears in the BinarySearchTree
type as well, but not in concrete implementations such as ScapegoatTree
where Repr
is specified. There is also a concrete implementation of the basic BinaryTree
, but I'm trying to keep it minimal so you don't have to wade through too much unnecessary code.
Upvotes: 1
Views: 182
Reputation: 6599
Just tried a rewritten using type parameters instead of abstract types, hope it suite your need.
trait BinaryTree[T, Repr <: BinaryTree[T, Repr]] {
def left: Repr
def value: T
def right: Repr
def height: Int
def add(elem: T): Repr =
if ( left.height <= right.height ) copy( left.add( elem ), right)
else copy( left, right.add(elem))
def copy( l: Repr, r: Repr ) : Repr
}
trait BinarySearchTree[T, U<:BinarySearchTree[T,U]] extends BinaryTree[T, U] { self:U =>
def ordering: Ordering[T]
override def add(elem: T) =
if ( ordering.equiv( elem, value ) ) self
else if ( ordering.lt(elem, value) ) copy( left.add( elem ), right )
else copy( left, right.add( elem ) )
}
class ScapegoatTree[T]( val value: T, val left: ScapegoatTree[T], val right: ScapegoatTree[T] )(implicit val ordering: Ordering[T]) extends BinarySearchTree[T, ScapegoatTree[T]] {
override def height = 1
override def copy( left: ScapegoatTree[T], right: ScapegoatTree[T] ) = new ScapegoatTree( value, left, right )
}
class BalancedBinaryTree[T]( val value: T, val left: BalancedBinaryTree[T], val right: BalancedBinaryTree[T] )(implicit val ordering: Ordering[T]) extends BinarySearchTree[T, BalancedBinaryTree[T]]{
override def height = 1
override def copy( left: BalancedBinaryTree[T], right: BalancedBinaryTree[T] ) = new BalancedBinaryTree( value, left, right )
}
According to this question, you are encounting a 'MyType' problem.
Upvotes: 1