I'm attempting to chain Iterators:
var it = Iterator(1)
it.next
it = Iterator(2) ++ it
it.next
it.hasNext
This infinitely loops on the hasNext as you can see here: https://scastie.scala-lang.org/qbHIVfsFSNO5OYmT4pkutA
If you run this and inspect the stack while it's infinitely looping, it's looping in the concetentation:
at scala.collection.Iterator$ConcatIterator.merge(Iterator.scala:213)
at scala.collection.Iterator$ConcatIterator.advance(Iterator.scala:197)
at scala.collection.Iterator$ConcatIterator.hasNext(Iterator.scala:227)
(This stack is from Scala 2.12.11, but the Scastie link shows same behavior in 2.13.2).
I know that one should never use an iterator after calling a method on it, but this appears like it would work to me. Using the var to point to the "current" Iterator and changing it to point to a new Iterator that appends the remainder of the previous one.
The following slight modification does work:
var it = Iterator(1)
it.next
val x = it
it = Iterator(2) ++ x
it.next
it.hasNext
Scastie link: https://scastie.scala-lang.org/1X0jslb8T3WIFLHamspYAg
This suggests to me that somehow the broken version is creating an Iterator that is appending itself. Any hints as to what is going on here?
The argument to the
++method ofIteratoris passed by name.++returns a newIteratorthat just stores a function which returnsit, but doesn't call it until you try to use the appended elements.So
++tries to evaluate the argument only when you callit.hasNext, but by that timeitis already redefined as the result of++, so it ends up trying to appenditto itself.In other words
vars and by-name parameters don't work together.So don't reassign
Iteratormethod results to the same variable and give them new names instead: