Final iterator spec

# joe (11 years ago)

A while back, the wiki Harmony draft spec for iterators changed from a Pythonic StopIteration approach to one where iterator objects return a {value : iter-value, done : bool} object. It since seems to have changed back. Is that the case, or am I misreading the situation?

It seems that Tracuer has yet to switch back, and before I bug the developers on that project I wanted to make sure that StopIteration is, in fact, back.

# Brandon Benvie (11 years ago)

What you're probably seeing is that the wiki no longer has up to date information. As things have been fully fleshed out in the es6 draft spec, the wiki is no longer up to date.

To answer your question, the iterator protocol hasn't changed back to using StopIteration. It's still { value, done }.

# joe (11 years ago)

Thanks. Btw, where is the final spec stored?

# Juan Ignacio Dopazo (11 years ago)

You can find it in the Drafts page: harmony:specification_drafts.

And there is an HTML version here: people.mozilla.org/~jorendorff/es6-draft.html

# Caitlin Potter (11 years ago)

I have a concern with these, which isn't 100% related to this thread, but is on the topic of iterators.

So, in Dart, we have this nice option of basically saying if (OBJ is Iterable), and we'll be able to know easily if an object conforms to that particular interface.

Admittedly, this construct makes a lot more sense in Dart than it does in Javascript, but it would be nice to have some kind of helper which will determine if an object adheres to the Iterable interface. For instance, Object.isIterable() => true if the interface is present in the object's prototype.

It would be nice to have a convenience function for this largely because it feels like the VM should have an easier time knowing if an object is iterable or not, and may be able to cache its knowledge of the Iterable status of an object, for performance reasons.

Maybe it would be good if a helper function like this would also determine that the return value of @@iterator() also conforms to the Iterator interface, or maybe that's not super important. But in any case, I could see utilities like this being very helpful.

Maybe that ship has sailed, but if something like that would hurt the harmony status of iterators, I would be interested in hearing the specific reasons against it.

# Domenic Denicola (11 years ago)

You can just do if (Symbol.iterator in potentialIterable).

# Peter van der Zee (11 years ago)

Does that work cross-frame?

# Allen Wirfs-Brock (11 years ago)

Yes

# Andy Wingo (11 years ago)

On Sun 02 Mar 2014 04:18, Domenic Denicola <domenic at domenicdenicola.com> writes:

You can just do if (Symbol.iterator in potentialIterable).

Of course, this can introduce time-of-check-to-time-of-use bugs. Actually calling @@iterator on the iterable is more reliable.

# David Bruant (11 years ago)

Le 03/03/2014 10:11, Andy Wingo a écrit :

Of course, this can introduce time-of-check-to-time-of-use bugs. Actually calling @@iterator on the iterable is more reliable.

This only shifts the problem one step without really solving it. Calling @@iterator may return a non-iterator or may return something that looks like a iterator ('next' method), but throws when calling 'next'. I wonder if time-of-check-to-time-of-use bugs can be fully avoided entirely in JS? It might be possible to guarantee some properties in TypeScript assuming all consummers of a piece of code are checked by the TypeScript compiler.

In practice, it looks like JS devs have lived well with solution like Domenic's one.

# Andy Wingo (11 years ago)

On Mon 03 Mar 2014 10:35, David Bruant <bruant.d at gmail.com> writes:

This only shifts the problem one step without really solving it.

An iterable is simply an object with a callable @@iterator property. Calling @@iterator on an object and getting back a result is the sum-total of the iterator structural type -- so yes, this problem is solved.

Calling @@iterator may return a non-iterator or may return something that looks like a iterator ('next' method), but throws when calling 'next'.

This is why the for-of and yield* desugarings eagerly get the "next" property on the iterator, and then use that property as a method all the way through. (And of course it can throw.)

I wonder if time-of-check-to-time-of-use bugs can be fully avoided entirely in JS?

To an extent, but it's hard; see Object.freeze et al. Anyway that's beside the point. The spec tries to eliminate all the bugs that it can, and the case of "get me an iterator from an iterable" or "prepare to call 'next' on this presumed iterable" are cases in which it can help.

# Claude Pache (11 years ago)

Le 3 mars 2014 à 10:46, Andy Wingo <wingo at igalia.com> a écrit :

An iterable is simply an object with a callable @@iterator property. Calling @@iterator on an object and getting back a result is the sum-total of the iterator structural type -- so yes, this problem is solved.

What does exactly the spec think what an iterable is? As far as I can tell, only Array.from and %TypedArray%.from test for iterability, and until bug 2486 is resolved, it is hard for us to guess, because there are really more than one possible answer, e.g.: Symbol.iterator in obj and typeof obj[Symbol.iterator] === "function".

Section The Iterable Interface define something more restrictive than typeof obj[Symbol.iterator] === "function" and less testable, given the requirements of the returned value of obj[Symbol.iterator]().

# Andy Wingo (11 years ago)

On Mon 03 Mar 2014 12:49, Claude Pache <claude.pache at gmail.com> writes:

What does exactly the spec think what an iterable is?

For what purpose? You rightly link to the denotation of Iterable; in context, it is used like this:

people.mozilla.org/~jorendorff/es6-draft.html#sec-getiterator

Then there are is the "next" operation on iterators:

people.mozilla.org/~jorendorff/es6-draft.html#sec-iteratornext

...and reading that I see that my memory was off, that we don't fetch the "next" method eagerly, and instead look up the "next" property each time. Apologies for the misinformation. Thus you can:

  function *g() {
    Object.defineProperty(g.prototype, 'next', {value:42});
    yield 4;
  }
  for (var x of new g()) print (x);

The iterator is indeed an iterator when it is returned by @@iterator, but not after the first iteration :)

Basically: you are looking for looking for certainty in structural typing of mutable values. You won't find it; or rather, if you do find it, it lasts only until the next piece of code that could mutate the world.

# Claude Pache (11 years ago)

Le 3 mars 2014 à 13:56, Andy Wingo <wingo at igalia.com> a écrit :

For what purpose?

For the purpose of giving the most reasonable answer to the if (OBJ is Iterable) test that Caitlin Potter was asking for earlier in this thread.

For me, the answer should be the same as what makes Array.from choose between the branch "iterable" and the branch "array-like". Which, as I have said, I cannot guess until bug 2486 is resolved. Deeper philosophical thoughts over what an iterable really is don't matter.

# Allen Wirfs-Brock (11 years ago)

7.4.2 IsIterable ( obj )

The abstract operation IsIterable with argument obj performs the following steps:

  1. If Type(obj) is not Object, then return undefined.
  2. Let iteratorGetter be Get(obj, @@Iterator).
  3. Return iteratorGetter.

the above is the test that Array.from uses

An Iterable is an object that has a Symbol.iterator keyed property whose value is not undefined.

A well-fromed Iterable is one whose @@iterator method is a function that returns an object that supports the Iterator interface. If an iterable is not well-formed then using it as such is likely to result in runtime exceptions or buggy behavior.