Redattack34
Redattack34

Reputation: 125

Why doesn't this code compile? Is this really unsafe?

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

Answers (1)

xiefei
xiefei

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

Related Questions