We currently have unidirectional iterators, which are very useful in many areas. However, these come with major caveats:
- If you need to have a navigable view of an iterable list, you have to buffer all entries of that list.
- When displaying large files, it's generally far more efficient to read only parts of it at a time.
- It's not uncommon to want to "reverse" an iterator, hence why so many languages provide it. Normally, in languages like Python that lack this feature, this involves loading the whole iterator into a buffer and then reading the buffer backwards, but this is highly inefficient in terms of memory for most collections.
- This would provide a natural "undo" functionality, and a future syntax addition could provide this for generators that support it.
- What prompted me to suggest this now is I'm currently designing an infinite scroller component, and it's way simpler and easier to accept a reversible iterator than to try to control the world by fetching and managing an internal list of everything.
So my proposal: add an optional iter.previous(value?)
to the iterator protocol, as a mirror of iter.next(value?)
with the same return value and everything.
- Where
next
advances the state forward,previous
advances the state back. - Where
next
on first call initializes the iterator at the start,previous
initializes the iterator at the end.
This would also be added to various built-in iterators where applicable, like array, map, and set iterators. And of course with the iterator helpers proposal, an iter.reversed()
utility could be created that works more or less like this:
function addReversed(Iterator) {
class ReverseIterator extends Iterator {
constructor(iter) { this._iter = iter }
next(value) { return this._iter.previous(value) }
previous(value) { return this._iter.next(value) }
}
Iterator.prototype.reversed = function () {
return new ReverseIterator(
this.prev == null ? [...this].values() : this
)
}
}
addReversed(Iterator)
addReversed(AsyncIterator)
As for language precedent:
- Rust:
std::iter::DoubleEndedIterator
- C++: the BidirectionalIterator interface + corresponding experimental concept
- Python: indirectly via
__len__()
+__getItem__()
- Java:
java.util.ListIterator
- Kotlin:
kotlin.collections.ListIterator
(inherited from Java, but exposed cross-platform) - Clojure internally uses reversible iterators to implement its
(clojure.core/rseq v)