Я пытаюсь обернуть голову вокруг абстрактных и явных типов себя в 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 })
}
}
Рабочий вариант довольно неуклюжий.
B.Абстрактные типы: 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 })
}
}
Не работает вообще из-за несоответствия типов конкретных путей, как я понял.
C.Введите 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 {
// ...
}
Спасибо!