Semantics of Iterator.take

I always lived under the impression that after calling it.take(n) the original iterator it must be discarded, as it is left in an undefined state. Some implementations, for example NewVectorIterator, implement an optimization by essentially changing the semantics to takeInPlace.

However, now I see no documentation mentioning that, like in the case of copyToArray. SliceIterator does not, for example, use this optimization.
Additionally, it may even be unintuitive behaviour. In this situation I am not sure where I stand. If the original iterator is left in an undefined state by essentially all methods other than hasNext, next(), and take, then we can make such optimizations. If not, then splitting iterator contents (doing something with the first n elements, and something else with the rest) becomes that much easier, as it doesn’t require calling splitAt. As it is, we got the worst of both worlds.
Is it only an ommission in the docummentation and the intent was always clear(), or is it a bit of a whole in the language semantics?

Any method call on an Iterator (other than next & hasHext of course) may leave it on an undefined state, period.
Menaing you MUST not use it anymore after that, trying to assume otherwise is always a bad idea.

span is lightweight if you consume the first iterator before the second.

(You could write a version that discards, that is, exhausts the first when the second iterator is used.)

It’s interesting to see what implementation class you wind up with, which I did instead of insomniac wordle.

scala> val xs = 1.to(10).toArray.toIndexedSeq
val xs: IndexedSeq[Int] = ArraySeq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> .getClass
val res0: Class[_ <: IndexedSeq[Int]] = class scala.collection.immutable.ArraySeq$ofInt

scala> xs.iterator
val res1: Iterator[Int] = <iterator>

scala> .getClass
val res2: Class[_ <: Iterator[Int]] = class scala.collection.ArrayOps$ArrayIterator$mcI$sp

scala> val xs = 1.to(10).to(Vector)
val xs: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> xs.iterator
val res3: Iterator[Int] = <iterator>

scala> .getClass
val res4: Class[_ <: Iterator[Int]] = class scala.collection.immutable.NewVectorIterator

scala> val xs = collection.mutable.Stack.from(1 to 10)
val xs: scala.collection.mutable.Stack[Int] = Stack(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> xs.iterator
val res5: Iterator[Int] = <iterator>

scala> .getClass
val res6: Class[_ <: Iterator[Int]] = class scala.collection.IndexedSeqView$IndexedSeqViewIterator

I think the lesson is, “trust your iterator”.

The last example shows that if you start with an IndexedSeq, then your iterator will just track an index.

The API you desire may not be, “I want to call take a bunch of times,” but, “I’d like to consume a prefix and then a suffix of my iterator.” That is what splitAt is for. But maybe you want something more like grouped, but lazy, an Iterator[Iterator[A]].

val (prefix, suffix) = (stack.iterator.take(5), stack.iterator.drop(5))

IndexedSeqViewIterator could override splitAt with the naive implementation, which is on IterableOps.

I did not understand from the post what is “the worst of both worlds”, or why there should be only two worlds.