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 ofIterator
is passed by name.++
returns a newIterator
that 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 timeit
is already redefined as the result of++
, so it ends up trying to appendit
to itself.In other words
var
s and by-name parameters don't work together.So don't reassign
Iterator
method results to the same variable and give them new names instead: