5

In ECMAScript6 an iterator is an object with a next() method that returns an object with two properties: {value, done}. E.g.:

function createIterator(ra) {
    let i = 0;
    return {
        next() {
            if (i < ra.length)
                return {value: ra[i++],
                        done: false};
            else
                return {value: undefined,
                        done: true};
        }
    };
}

Having created an iterator one finds it is not very useful in its own right as there's no built-in syntax to take advantage of an iterator. Instead, one has to do things like:

    const it = createIterator(['a', 'b', 'c']);
    while (true) {
        const o = it.next();
        if (o.done)
            break;
        else
            console.log(o.value);
    }

"Iterable" on the other hand is an object with a Symbol.iterator property that has to be a no-argument function that returns an iterator:

function createIterable(ra) {
    let it = createIterator(ra);
    return {
        [Symbol.iterator]() {return it;}
    };
}

... and when we create an iterable we are finally able to use the for of syntax:

for (let v of createIterable(['a', 'b', 'c']))
    console.log(v);

Looking at the above code the added value of the "iterable" object over the "iterator" is not easily perceived. So why does Javascript need these two separate concepts of iterable and iterator? BTW this is what Java does as well so there must be some language-independent reason why it is a good idea to have these two distinct concepts.

Community
  • 1
  • 1
Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • An iterator should use `yield`, not `return`. Iterators implement lazy evaluation, like Python generators and, yes, Java. Iterables are simply a way to standardise how you iterate over objects - this comes from C++, where iterators are meant to standardise iteration over objects where it's not clear what to iterate over. There's a world of difference. – Akshat Mahajan Apr 11 '16 at 08:40
  • 1
    Conceptually, the "iterator" wraps the current state of an iteration operation, and the "iterable" symbol marks the collection as capable of providing an iterator. – Alnitak Apr 11 '16 at 08:41
  • @AkshatMahajan iterators and iterables stand on their own right. Generators are (in this regard) just a convenience. – Marcus Junius Brutus Apr 11 '16 at 08:41
  • @MarcusJuniusBrutus "An object is an iterator when it knows how to access items from a collection one at a time, while keeping track of its current position within that sequence. " - from Mozilla docs. Javascript iterables = generators in Python; JS generators are factory functions for returning iterators and are misnomers - they don't correspond to Python generators. The benefit of an iterator is lazy evaluation. That's why it exists. – Akshat Mahajan Apr 11 '16 at 08:45
  • @Alnitak Your comment makes sense. Iterator objects is where the state is kept and the [Symbol.iterator] method returns a new Iterator (with initialized state) every time it's invoked. – Marcus Junius Brutus Apr 11 '16 at 08:46
  • @AkshatMahajan even so, AFAIK there's no reason one has to use a generator and `yield` in JS - you can write an iterator without `yield`. – Alnitak Apr 11 '16 at 08:47
  • @Alnitak That is true, and that is because iterators are also used as the backbone for iterables in JS. It's a double-edged sword - you can use it to implement either lazy evaluation with `yield` or standardise iteration within objects or both. There are real needs for both use cases, and it's fairly elegant to have them be solved by the same mechanism. – Akshat Mahajan Apr 11 '16 at 08:49
  • @AkshatMahajan _"An iterator should use yield"_ What would be the advantage of using `yield` when you iterate over an array, e.g.? _"Javascript iterables = generators in Python"_ No. _"The benefit of an iterator is lazy evaluation. That's why it exists"_ It exists to encapsulate iteration. That's why it's called iterator and not evaluator. _"Iterators implement lazy evaluation"_ `iterator` is a protocol and there is no such requirement for implementations. _"JS generators are ... misnomers - they don't correspond to Python generators"_ Why do you think that and why does it even matter? – a better oliver Apr 11 '16 at 10:36

1 Answers1

3

Conceptually, the object that implements the "iterator" interface just encapsulates the current state of an iteration operation. Once that iterator has been consumed (it.done === true) it cannot be re-used.

The [Symbol.iterator]() function of an "iterable" object returns a new iterator each time it's invoked.

In that respect, be wary of the OP's function which is potentially flawed since the object created by his createIterable function only allows for a one-off call to its Symbol.iterator function. It would fail if the object returned was passed to more than one for .. of operation. Ideally it should read:

function createIterable(ra) {
    return {
        [Symbol.iterator]() {
             return createIterator(ra);
        }
    }
}
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • @MarcusJuniusBrutus I've reinstated my caveat - your `createIterable` function ought to work such that the returned "iterable" object can be re-used. – Alnitak Apr 11 '16 at 09:06
  • 1
    Yes it is fine now. I am leaving the buggy implementation in my question as it stands for the benefit of future readers as your caveat is a crucial one and serves to highlight the division of labor between the iterable and the iterator. – Marcus Junius Brutus Apr 11 '16 at 09:09
  • @MarcusJuniusBrutus I'm jetlagged - didn't realise you were the OP! :p – Alnitak Apr 11 '16 at 09:10