Scala Self Type и this.type в выпуске коллекций - PullRequest
9 голосов
/ 08 февраля 2012

Я пытаюсь обернуть голову вокруг абстрактных и явных типов себя в 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 {
  // ...
}

Спасибо!

Ответы [ 2 ]

5 голосов
/ 08 февраля 2012

Не понимая, в чем проблема у вас с (C), вы можете попробовать вариант (B):

trait Tree {
    type Node <: Tree

    def children: Iterable[Tree#Node]  
    def descendants: Iterable[Tree#Node] = {
        val dv = children.view
        dv ++ (dv.flatMap { _.children })
    }
}

, который позволяет избежать проблем с типом вашего пути.Кстати, вы действительно должны взглянуть на http://www.assembla.com/spaces/scala-graph/wiki

1 голос
/ 09 февраля 2012

В конце я согласился с тем, что было предложено в этом обсуждении http://www.scala -lang.org / node / 6649 :

trait Tree[+Node <: Tree[Node]] {
    this: Node =>

    def children: Iterable[Node]

    def descendants: Iterable[Node] = {
       val dv = children.view
       dv ++ (dv.flatMap { _.children })
    }
}  

т.е. вариант (C), но с явным типом self. Это дает возможность использовать this в других методах (скажем, метод find(path: String): Option[Node]).

...