Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala self type and this.type in collections issue

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!

like image 743
tuxSlayer Avatar asked Feb 08 '12 17:02

tuxSlayer


1 Answers

Without really understanding what the problem is you have with (C) you could try a variant of (B):

trait Tree {
    type Node <: Tree

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

Which avoids your path specific type problem. By the way you should really have a look at http://www.assembla.com/spaces/scala-graph/wiki

like image 82
Jan van der Vorst Avatar answered Sep 18 '22 20:09

Jan van der Vorst