Scala self type and this.type in collections issue
Asked Answered
M

2

9

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!

Marylnmarylou answered 8/2, 2012 at 17:39 Comment(3)
Ah yes. The "Scala has no MyType" problem again.Farouche
as you can see I had a look at this in SO, and tried proposed variants. it works well for quite simple constructs (like c.incr().decr() example in Martin's paper), but with collections it doesn't.Marylnmarylou
yeah. got the point why after reading your discussion here scala-lang.org/node/6649, thanksMarylnmarylou
M
1

At the end I've settled with what was proposed in this discussion 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 })
    }
}  

I.e. variant (C) but with explicit self type. This gives a chance to use this in other methods (say, method find(path: String): Option[Node]).

Marylnmarylou answered 9/2, 2012 at 13:15 Comment(0)
A
5

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

Acrobatics answered 8/2, 2012 at 19:58 Comment(1)
Oh, thanks, missed this type selector notation. As for (C) - I shall add these type args to all child classes. This is not very convenient.Marylnmarylou
M
1

At the end I've settled with what was proposed in this discussion 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 })
    }
}  

I.e. variant (C) but with explicit self type. This gives a chance to use this in other methods (say, method find(path: String): Option[Node]).

Marylnmarylou answered 9/2, 2012 at 13:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.