The migration guide to Scala 2.13 explains that Traversable
has been removed and that Iterable
should be used instead. This change is particularly annoying for one project, which is using a visitor to implement the foreach
method in the Node
class of a tree:
case class Node(val subnodes: Seq[Node]) extends Traversable[Node] {
override def foreach[A](f: Node => A) = Visitor.visit(this, f)
}
object Visitor {
def visit[A](n: Node, f: Node => A): Unit = {
f(n)
for (sub <- n.subnodes) {
visit(sub, f)
}
}
}
object Main extends App {
val a = Node(Seq())
val b = Node(Seq())
val c = Node(Seq(a, b))
for (Node(subnodes) <- c) {
Console.println("Visiting a node with " + subnodes.length + " subnodes")
}
}
Output:
Visiting a node with 2 subnodes
Visiting a node with 0 subnodes
Visiting a node with 0 subnodes
An easy fix to migrate to Scala 2.13 is to first store the visited elements in a remaining
buffer, which is then used to return an iterator:
import scala.collection.mutable
import scala.language.reflectiveCalls
case class Node(val subnodes: Seq[Node]) extends Iterable[Node] {
override def iterator: Iterator[Node] = {
val remaining = mutable.Queue.empty[Node]
Visitor.visit(this, item => iterator.remaining.enqueue(item))
remaining.iterator
}
}
// Same Visitor object
// Same Main object
This solution has the disadvantages that it introduces new allocations that put pressure on the GC, because the number of visited elements is usually quite large.
Do you have suggestions on how to migrate from Traversable
into Iterable
, using the existing visitor but without introducing new allocations?
As you noticed, you need to extend
Iterable
instead ofTraversable
. You can do it like this:outputs:
Then you can do more operations such as:
Code run at Scastie.