In your code, there is no obvious reason to make Node
and Leaf
nested path-dependent classes. Just make them covariant stand-alone classes, then all the problems with path-dependence go away:
trait Node[+A] {
val id: Byte
def put[A1 >: A](ids: Seq[Byte], item: A1): Node[A1]
def remove[A1 >: A](ids: Seq[Byte], item: A1): Node[A1]
}
case class Branch[+A](id: Byte, subs: Vector[Node[A]]) extends Node[A] {
def put[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
def remove[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
}
case class Leaf[+A](id: Byte, subs: Vector[A]) extends Node[A] {
def put[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
def remove[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
}
class SomeFancyCollection[+A](val root: Node[A] = Branch(0x0, Vector[Node[A]]())) {
def add[A1 >: A](item: A1): SomeFancyCollection[A1] = {
val ids: Seq[Byte] = ???// getIds() // doesn't matter
val newRoot = root.put(ids, item)
new SomeFancyCollection[A1](newRoot)
}
}
If you don't want to pollute the namespace, just declare the Node
classes package-private, or even hide all those auxiliary implementation-detail classes inside the companion object of SomeFancyCollection
:
class SomeFancyCollection[+A] private[SomeFancyCollection](
val root: SomeFancyCollection.AnnoyingDetails.Node[A]
) {
def add[A1 >: A](item: A1): SomeFancyCollection[A1] = {
val ids: Seq[Byte] = ???// getIds() // doesn't matter
val newRoot = root.put(ids, item)
new SomeFancyCollection[A1](newRoot)
}
}
object SomeFancyCollection {
def empty[A]: SomeFancyCollection[A] = new SomeFancyCollection[A](
AnnoyingDetails.Branch(0x0, Vector[AnnoyingDetails.Node[A]]())
)
private[SomeFancyCollection] object AnnoyingDetails {
trait Node[+A] {
val id: Byte
def put[A1 >: A](ids: Seq[Byte], item: A1): Node[A1]
def remove[A1 >: A](ids: Seq[Byte], item: A1): Node[A1]
}
case class Branch[+A](id: Byte, subs: Vector[Node[A]]) extends Node[A] {
def put[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
def remove[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
}
case class Leaf[+A](id: Byte, subs: Vector[A]) extends Node[A] {
def put[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
def remove[A1 >: A](ids: Seq[Byte], item: A1): Node[A1] = ???
}
}
}
Node
belongs to a specificSomeFancyCollection
instance, and herenewRoot
belongs to thethis
instance and can't be used withnewInstance
. – Alexey Romanov