scand1sk
scand1sk

Reputation: 1124

Inheriting traits that keep types

I am quite new to Scala and still a bit confused with its complicated type system.

I try to solve the following problem. I want to design classes that are members of mutable doubly linked lists (and more complicated data structures such as heaps). My objective is, of course, to be able to remove the objects from the data structures in constant time.

I designed the following trait :

trait DLLMember {
  var next: DLLMember = this
  var prev: DLLMember = this

  ...
}

with a few additional methods for adding and removing the object from the list.

The problem is, that when I am in my actual class, say :

class IntInDL(val v: Int) extends DLLMember

When parsing my list, IntInDL.next would return me a DLLMember type instead of IntInDL, which I have to cast to retrieve the value: this is not nice…

Is there a way to exploit Scala's type system to keep my work typesafe?

Upvotes: 4

Views: 141

Answers (1)

nonVirtualThunk
nonVirtualThunk

Reputation: 2852

It's a little roundabout, but the following should work:

trait DLLMember[T >: Null <: DLLMember[T]] { self : T =>
  var next: T = this
  var prev: T = this
  ...
}

class IntInDL(val v: Int) extends DLLMember[IntInDL]

var i = new IntInDL(3)
println(i.next.v) //prints 3
i.next = null //is valid
i.next = new IntInDL(1) //is valid

Essentially, what's going on here is that you are saying with self : T => that the type parameter T must be a superclass of the class this trait is applied to. When you use the trait in IntInDL it is now known that the next and prev variables must be of some subtype of IntInDL because you supplied that as the type parameter. As such, you can use their members directly, without having to cast.

If you had supplied some other, arbitrary type that was not part of the hierarchy, class IntInDL(val v: Int) extends DLLMember[String] for example, it would simply have failed to compile.

Upvotes: 6

Related Questions