Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala iterator: "one should never use an iterator after calling a method on it" - why?

Tags:

iterator

scala

Scala documentation on Iterator[T] here says the following:

It is of particular importance to note that, unless stated otherwise, one should never use an iterator after calling a method on it. The two most important exceptions are also the sole abstract methods: next and hasNext.

They also give a specific example of safe and unsafe use:

def f[A](it: Iterator[A]) = {
  if (it.hasNext) {            // Safe to reuse "it" after "hasNext"
    it.next                    // Safe to reuse "it" after "next"
    val remainder = it.drop(2) // it is *not* safe to use "it" again after this line!
    remainder.take(2)          // it is *not* safe to use "remainder" after this line!
  } else it
}

Unfortunately I don't follow the idea of unsafety here. Could someone shed some light for me here?

like image 824
Alexey Alexandrov Avatar asked Jan 13 '23 13:01

Alexey Alexandrov


1 Answers

Here's a concrete example:

def eleventh[A](xs: Iterator[A]) = {
  xs.take(10).toList
  xs.next
}

We can try it out:

scala> eleventh((1 to 100).toList.toIterator)
res0: Int = 11

scala> eleventh((1 to 100).toStream.toIterator)
res1: Int = 11

scala> eleventh(Stream.from(1).toIterator)
res2: Int = 11

Looks fine. But then:

scala> eleventh((1 to 100).toIterator)
res3: Int = 1

Now (1 to 100).toIterator has the same type as (1 to 100).toList.toIterator, but the two behave very differently here—we're seeing implementation details leak out of the API. That's a very bad thing, and is the direct result of mixing purely functional combinators like take with an inherently imperative and mutable concept like the iterator.

like image 189
Travis Brown Avatar answered Jan 18 '23 02:01

Travis Brown