I'm trying to wrap my head around abstract and explicit self types in scala. Lets consider this example: I want to create a base for extensible tree as simple as this:
trait Tree {
def children: Iterable[Tree]
def descendants: Iterable[Tree] = { val dv = children.view; dv ++ (dv.flatMap { _.children }) }
}
However, I want to be able to extend tree nodes with some methods and use these methods like: tree.children foreach { _.newMethod() }
For this I've tried:
A. this.type: FAIL
trait Tree {
def children: Iterable[this.type]
def descendants: Iterable[this.type] = {
val dv = children.view
// FAIL: type mismatch; found : scala.collection.IterableView[com.abovobo.data.Tree,Iterable[_]] required: Iterable[Tree.this.type]
// dv ++ (dv.flatMap { _.children })
// OK:
dv.++[this.type, Iterable[this.type]](dv.flatMap[this.type, Iterable[this.type]]{ _.children })
}
}
Working variant are pretty clumsy.
B. Abstract types: FAIL
trait Tree {
type Node <: Tree
def children: Iterable[Node]
def descendants: Iterable[Node] = {
val dv = children.view
// FAIL: type mismatch; found : scala.collection.IterableView[com.abovobo.data.Tree#Node,Iterable[_]] required: Iterable[Tree.this.Node]
dv ++ (dv.flatMap { _.children })
}
}
Doesn't work at all due to path specific type mismatch as I understood.
C. Type params (generics): OK
trait Tree[+Node <: Tree[Node]] {
def children: Iterable[Node]
def descendants: Iterable[Node] = {
val dv = children.view
dv ++ (dv.flatMap { _.children })
}
}
Works OK, but not so good to maintain in derived classes.
Any ideas how to make first two variants working without a tons of code?
Also, with this.type I've run into problems with implementation.
trait BiDTree extends Tree {
def parent: Option[this.type]
}
// how to accept this param? Option[TreeImpl] doesn't work.
class TreeImpl(val parent: Option[???]) extends BiDTree {
// ...
}
Thanks!
c.incr().decr()
example in Martin's paper), but with collections it doesn't. – Marylnmarylou