Scala集合中的self type和this.type问题。

9
我在尝试理解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: 失败
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 })
    }
}

工作变体相当笨拙。抽象类型:失败。
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. 类型参数(泛型):OK
trait Tree[+Node <: Tree[Node]] {

    def children: Iterable[Node]

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

“Works OK,但在派生类中维护不太好。”
“有没有什么想法可以使前两个变量在不编写大量代码的情况下正常工作?”
“同时,使用 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 {
  // ...
}

谢谢!


3
啊,是的。又是“Scala没有MyType”的问题。 - Rex Kerr
正如您所看到的,我在 Stack Overflow 上查看了这个问题,并尝试了提出的变量。它可以很好地处理相当简单的结构(例如 Martin 论文中的 c.incr().decr() 示例),但对于集合来说则不行。 - tuxSlayer
1
是的。在阅读了你在这里的讨论之后http://www.scala-lang.org/node/6649,我明白了。谢谢。 - tuxSlayer
2个回答

5

如果你对 (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
哦,谢谢,我错过了这个类型选择器符号。至于(C)-我将把这些类型参数添加到所有子类中。这不是很方便。 - tuxSlayer

1
最终,我决定采用这个讨论中提出的方案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 })
    }
}  

即使用显式self类型的变体(C)。这样可以在其他方法中使用this(例如,方法find(path:String):Option [Node] )。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接