Reputation: 1124
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
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