Reputation: 8653
I have a trait in which (among other things) I want a method that will create a new instance of the class, and then there are other methods that use that instance of the class.
A very cut down version of my code is:
trait A {
def prev: A
def get(end: A): A
}
class B extends A {
def prev: B = new B()
def get(end: B): B = end.prev
}
What I am trying to show here is that next will return a new instance of the class (in reality with some new constructor parameters) and that the get method will make use of next internally (along with other logic)
The problem with the above is that the compiler says "class B must implement abstract member get(end: A): A", which is reasonable.
I tried to solve it using type bounds as:
trait A {
def prev: A
def get(end: A): A
}
case class B extends A {
def prev[TX <: A]: TX = new B()
def get[TX <: A](end: TX): TX = end.prev
}
but now the error is "Expression of type B doesn't conform to expected type TX" on new B()
and "Expression of type A doesn't conform to expected type TX" on end.prev
I don't understand why this is a problem as next is returning a B which is a subtype of A, which is what TX is.
Is there a way to implement what I wish to do here?
A bit of context in case the above all seems too abstract. I am implementing a circular doubly linked list as there's nothing like that that I could find. The trait includes:
trait Circular[T] {
// Nodes in the list from the current position up to but NOT INCLUDING the end
def toStream(end: Circular[T]): Stream[Circular[T]]
def prev: Circular[T]
...
And my class looks like:
case class Node[T](val data: T, var prev: Node[T], var next: Node[T])
case class CircularList[T](first: Node[T], last: Node[T], current: Node[T])
extends Circular[T] {
// Nodes in the list from the current position up to but not including the end
def toStream(end: CircularList[T]): Stream[CircularList[T]] = {
@tailrec
def toStreamRec(end: CircularList[T], acc: Stream[CircularList[T]]): Stream[CircularList[T]] = {
if (this == end) {
acc
} else {
toStreamRec(end.prev, Stream.cons(end.prev, acc))
}
}
toStreamRec(end, Stream.empty)
}
def prev: CircularList[T] = new CircularList[T](first, last, current.prev)
...
so toStream
maps to get
in my cutdown example.
Upvotes: 1
Views: 172
Reputation: 23788
What you want is something called F-bound generic. The code goes like this:
trait Base[T <: Base[T]] {
def next: T
def get(end: T): T
}
class Chlid extends Base[Child] {
def next: Chlid = new Chlid()
def get(end: Chlid): Chlid = end.next
}
Your code doesn't compile because
def get(end: B): B
is not an override of
def get(end: A): A
because the original method accepts objects of type A
while your method requires only more narrow type B
For your Circular
example you want something like
trait Circular[T, C <: Circular[T, C]] {
// Nodes in the list from the current position up to but NOT INCLUDING the end
def toStream(end: C): Stream[C]
def next: C
}
case class Node[T](val data: T, var prev: Node[T], var next: Node[T])
case class CircularList[T](first: Node[T], last: Node[T], current: Node[T]) extends Circular[T, CircularList[T]] {
// Nodes in the list from the current position up to but not including the end
def toStream(end: CircularList[T]): Stream[CircularList[T]] = {
@tailrec
def toStreamRec(end: CircularList[T], acc: Stream[CircularList[T]]): Stream[CircularList[T]] = {
if (this == end) {
acc
} else {
toStreamRec(end.prev, Stream.cons(end.prev, acc))
}
}
toStreamRec(end, Stream.empty)
}
def prev: CircularList[T] = new CircularList[T](first, last, current.prev)
override def next: CircularList[T] = ???
}
Upvotes: 3