Я пытаюсь осмыслить абстрактные и явные типы self в scala. Давайте рассмотрим этот пример: Я хочу создать основу для расширяемого дерева так просто:
trait Tree {
def children: Iterable[Tree]
def descendants: Iterable[Tree] = { val dv = children.view; dv ++ (dv.flatMap { _.children }) }
}
Однако я хочу иметь возможность расширять узлы дерева с помощью некоторых методов и использовать такие методы, как: tree.children foreach {_.newMethod () }
Для этого я пробовал:
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 })
}
}
Рабочий вариант довольно корявый.
Б. Абстрактные типы: 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 })
}
}
Не работает вообще из-за несоответствия типов для конкретного пути, как я понял.
С. Тип params (generics): OK
trait Tree[+Node <: Tree[Node]] {
def children: Iterable[Node]
def descendants: Iterable[Node] = {
val dv = children.view
dv ++ (dv.flatMap { _.children })
}
}
Работает нормально, но не очень удобно поддерживать в производных классах.
Есть идеи, как заставить работать первые два варианта без тонны кода?
Кроме того, с this.type у меня возникли проблемы с реализацией.
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 {
// ...
}
Спасибо!