Why does Array.from accept non-iterable arraylikes?

# Jason Orendorff (12 years ago)

According to 1, Array.from will first try treating the argument as an iterable, then as an arraylike. This is much better than just arraylike. The committee considered making it iterable only, but decided against it. The rationale recorded in the notes is:

RW: No.

Rick, can you expand on that a bit? :-)

I propose changing it to iterable only. All builtin arraylike objects are iterable now, including arguments, per the same discussion.1 Certainly the idea is for all DOM arraylikes to be iterable. So what is this for?

P.S. Rick: Thanks again for taking notes at the meetings. They're indispensible. I've cloned the repo; grepping the notes has been super useful too.

# Erik Arvidsson (12 years ago)

I'm with Jason here.

The only argument I can vaguely remember is that people want to be able to use these on old browsers without @@iterator? But I don't see why a polyfill could not tag things with __iterator__ or something else?

# Allen Wirfs-Brock (12 years ago)

On Jun 24, 2013, at 9:07 AM, Erik Arvidsson wrote:

I'm with Jason here.

The only argument I can vaguely remember is that people want to be able to use these on old browsers without @@iterator? But I don't see why a polyfill could not tag things with __iterator__ or something else?

I think the other argument is that the Array iteration methods historically accept anything that vaguely looks "array-like".

If you can say:

Array.prototype.forEach({0:1,1:1,2:2,length:3}, n=> console.log(n));

why can't you say:

Array.from( {0:1,1:1,2:2,length:3}).forEach(n=>console.log(n));

or:

for (let n of Array.from( {0:1,1:1,2:2,length:3}))  console.log(n);
# Rick Waldron (12 years ago)

It's my pleasure and I'm glad you're finding them useful as a resource during implementation :)

The problem is that I can't really record my own input... while I'm talking, no one is taking notes. When I'm done talking, I'm taking notes. The example Brendan wrote on the board was to illustrate that a "helper" function could be used to turn arraylikes into iterables before being passed to Array.from. I said "no" because it contradicts the agreement made a few lines prior.

While all builtin arraylikes are also iterable now, arraylikes created by non-built-in (assuming we both use "built-in" to refer to objects defined by ES) may not implement iterator protocol. Array.from, like Function.prototype.apply, will allow those objects to be turned into an array, eg. NodeList, DOMTokenList or jQuery objects. A cowpath that Array.from was intended to clear and pave was [].slice.call(arrayLike)/Array.prototype.slice.call(arrayLike). Also, web devs will be able to use a polyfilled Array.from in code that must run in browsers that don't/won't support spread (ie. older non-updating browsers). Hopefully this helps to clarify

# Jason Orendorff (12 years ago)

On Mon, Jun 24, 2013 at 11:43 AM, Rick Waldron <waldron.rick at gmail.com> wrote:

While all builtin arraylikes are also iterable now, arraylikes created by non-built-in (assuming we both use "built-in" to refer to objects defined by ES) may not implement iterator protocol. Array.from, like Function.prototype.apply, will allow those objects to be turned into an array, eg. NodeList, DOMTokenList or jQuery objects.

NodeList and DOMTokenList are iterable in Firefox. NodeList is even provisionally iterable per spec at the moment. It's marked as [ArrayClass]:

dom.spec.whatwg.org/#interface-nodelist

which means instances inherit from Array.prototype, including its @@iterator method.

The infrastructure to make interfaces iterable, even without [ArrayClass], also already exists in WebIDL:

dev.w3.org/2006/webapi/WebIDL/#idl-iterators

In short, I wouldn't worry about other specs or software failing to adopt iterability. The awful truth is, every other party to the problem moves many times faster than new JS features propagate to the last 10% of Web users. I'd be astonished if jQuery objects weren't iterable long before ES6 features are considered generally available. Iterability support is just a few lines of code, and jQuery users will want to use Array.from!

A cowpath that Array.from was intended to clear and pave was [].slice.call(arrayLike)/Array.prototype.slice.call(arrayLike).

Array.from should still satisfy that use case. Note that as proposed currently, Array.from already isn't totally polyfillable; it is only approxi-fill-able; dropping support for arraylikes will not change that.

Also, web devs will be able to use a polyfilled Array.from in code that must run in browsers that don't/won't support spread (ie. older non-updating browsers).

Ooh, interesting. Let's think this through.

In browsers that have neither iteratorSymbol nor array-spread syntax, polyfills won't have access to either side of the iteration protocol. They can't provide or consume iterables. So they'll have to either "approxifill" Array.from or not try. I think they will not try.

I can imagine them using the arraylike protocol, with some extra cruft behind the scenes to pick up particular non-arraylike iterables, such as instances of their own Map and Set polyfills. That is, polyfills might try to approx-i-fill Array.from using various workarounds; but I'm not convinced we should carry those workarounds forward into ES6.

However I think the right answer is to make Array.from polyfillable.

If we change the name of the method from a symbol to plain .iterator(), the iteration protocol will be fully accessible in pure ES1-5 code. Functions that produce or consume iterables would become accurately polyfillable. Array.from() would be polyfillable. The Map and Set constructors. Polyfills would also be able to monkeypatch DOM classes to make them iterable in old browsers.

There's a benefit for pure ES6 code too; it would be easier to make a class iterable:

class Table {
    *iterator() {
        ...
    }
}

(Currently an extra scrap of code outside the class is needed, since we don't have syntactic sugar for symbol-named methods.)

And jQuery could just add iterator: [].iterator, to an object literal somewhere. Literally one line of code to make jQuery objects iterable in browsers that have iteration support. No try-catch, eval, or import needed.

I think TC39 made @@iterator a symbol on the theory that users would want to build iterable Proxy-based string-key maps that would support property-access syntax. I wonder if that rationale holds anymore, now that there's the invoke trap.

# Sam Tobin-Hochstadt (12 years ago)

On Tue, Jun 25, 2013 at 11:30 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

I think TC39 made @@iterator a symbol on the theory that users would want to build iterable Proxy-based string-key maps that would support property-access syntax.

My recollection is that we chose to make iterator a symbol because we worried about taking the name "iterator" on lots of existing objects.

Sam

# Jason Orendorff (12 years ago)

On Tue, Jun 25, 2013 at 10:42 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

I think TC39 made @@iterator a symbol on the theory that users would want to build iterable Proxy-based string-key maps that would support property-access syntax.

My recollection is that we chose to make iterator a symbol because we worried about taking the name "iterator" on lots of existing objects.

What kind of existing code would be a problem?

Firefox added Array.prototype.iterator a year ago. It has shipped in the release browser, and it's been fine.

# Sam Tobin-Hochstadt (12 years ago)

On Tue, Jun 25, 2013 at 11:58 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

On Tue, Jun 25, 2013 at 10:42 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

I think TC39 made @@iterator a symbol on the theory that users would want to build iterable Proxy-based string-key maps that would support property-access syntax.

My recollection is that we chose to make iterator a symbol because we worried about taking the name "iterator" on lots of existing objects.

What kind of existing code would be a problem?

Firefox added Array.prototype.iterator a year ago. It has shipped in the release browser, and it's been fine.

I think that's strong evidence against the worry, but I believe the worry was something like what we've seen with values recently.

# Anne van Kesteren (12 years ago)

On Tue, Jun 25, 2013 at 4:58 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

On Tue, Jun 25, 2013 at 10:42 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

My recollection is that we chose to make iterator a symbol because we worried about taking the name "iterator" on lots of existing objects.

What kind of existing code would be a problem?

Firefox added Array.prototype.iterator a year ago. It has shipped in the release browser, and it's been fine.

E.g. HTMLCollection has named getters. Making it iterable without breaking compatibility would be great.

-- annevankesteren.nl

# Allen Wirfs-Brock (12 years ago)

On Jun 25, 2013, at 10:19 AM, Anne van Kesteren wrote:

On Tue, Jun 25, 2013 at 4:58 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

On Tue, Jun 25, 2013 at 10:42 AM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

My recollection is that we chose to make iterator a symbol because we worried about taking the name "iterator" on lots of existing objects.

What kind of existing code would be a problem?

Firefox added Array.prototype.iterator a year ago. It has shipped in the release browser, and it's been fine.

E.g. HTMLCollection has named getters. Making it iterable without breaking compatibility would be great.

This design discussion really wasn't just about iterator. In ES6 we have various property keys that are hooks deeply into either the semantics of core language features: @@create, @@hasInstance, @@ToPrimitive, @@iterator, @@toStringTag, @@isRegExp.

Anyone plugging into these hooks really should be intentional about what they are doing. Accidentally doing so my poor name choice may mot be disastrous but it is likely to be dificult to debug. Using a symbol for these properties greatly reduces the likelihood of such an accident.

We could make an exception for iterator, but why? That just introduces an inconsistency in the design.

# Dean Landolt (12 years ago)

On Tue, Jun 25, 2013 at 1:33 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Jun 25, 2013, at 10:19 AM, Anne van Kesteren wrote:

On Tue, Jun 25, 2013 at 4:58 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

On Tue, Jun 25, 2013 at 10:42 AM, Sam Tobin-Hochstadt < samth at ccs.neu.edu> wrote:

My recollection is that we chose to make iterator a symbol because we worried about taking the name "iterator" on lots of existing objects.

What kind of existing code would be a problem?

Firefox added Array.prototype.iterator a year ago. It has shipped in the release browser, and it's been fine.

E.g. HTMLCollection has named getters. Making it iterable without breaking compatibility would be great.

This design discussion really wasn't just about iterator. In ES6 we have various property keys that are hooks deeply into either the semantics of core language features: @@create, @@hasInstance, @@ToPrimitive, @@iterator, @@toStringTag, @@isRegExp.

Anyone plugging into these hooks really should be intentional about what they are doing. Accidentally doing so my poor name choice may mot be disastrous but it is likely to be dificult to debug. Using a symbol for these properties greatly reduces the likelihood of such an accident.

We could make an exception for iterator, but why? That just introduces an inconsistency in the design.

For properties that are expected to be looked up on object or its prototype unique symbols are the only way to go. Otherwise library code will end up littered with the Class.prototype.method.call pattern and we'll quickly lose the ability to coherently overload these methods anyway.

But there's still a huge win to be had in also defining hooks for this kind of functionality as static class methods on the built-ins, at least for the functionality that could otherwise be polyfilled correctly. Any functionality defined in this way will be instantly usable on the open web as soon as it gets the TC39 stamp of approval in Rick's meeting notes. That's a pretty big win for a very small amount of bloat.

# Brandon Benvie (12 years ago)

On 6/25/2013 10:33 AM, Allen Wirfs-Brock wrote:

We could make an exception for iterator, but why? That just introduces an inconsistency in the design.

I think the motivation was to make it easier to polyfill, but I don't think that argument holds for @@iterator. If you're attempting to polyfill iteration, then you have to polyfill both ends of it; you have to supply both the iterators as well as functions to consume those iterators (since you can't polyfill for-of). That means you have control over the protocol, and can opt to use something like "iterator".

# Dean Landolt (12 years ago)

On Tue, Jun 25, 2013 at 2:16 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

On 6/25/2013 10:33 AM, Allen Wirfs-Brock wrote:

We could make an exception for iterator, but why? That just introduces an inconsistency in the design.

I think the motivation was to make it easier to polyfill, but I don't think that argument holds for @@iterator. If you're attempting to polyfill iteration, then you have to polyfill both ends of it; you have to supply both the iterators as well as functions to consume those iterators (since you can't polyfill for-of). That means you have control over the protocol, and can opt to use something like "iterator".

You could, but your polyfills probably won't play well with other polyfills unless TC39 specifies a hook (normative or otherwise). Again, if the platform were to also define a secondary, static way to get and set an object's iterator a la Object.getPrototypeOf the interop problem is solved. It's hideous, sure, but it's really only necessary in library code.

It would be a shame of the lack of symbols is the only thing that kept pre-es6-supporting libraries from using iterators or other code that uses them.

# Jason Orendorff (12 years ago)

On Tue, Jun 25, 2013 at 1:16 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

On 6/25/2013 10:33 AM, Allen Wirfs-Brock wrote:

We could make an exception for iterator, but why? That just introduces an inconsistency in the design.

I think the motivation was to make it easier to polyfill,

The motivation is to make it possible to polyfill correctly in existing browsers. The protocol and related library features are useful even without syntactic support.

but I don't think that argument holds for @@iterator. If you're attempting to polyfill iteration, then you have to polyfill both ends of it; you have to supply both the iterators as well as functions to consume those iterators (since you can't polyfill for-of). That means you have control over the protocol, and can opt to use something like "iterator".

That kind of polyfill would not support other code that produces or consumes the protocol.

# Jason Orendorff (12 years ago)

On Tue, Jun 25, 2013 at 12:33 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

This design discussion really wasn't just about iterator. In ES6 we have various property keys that are hooks deeply into either the semantics of core language features: @@create, @@hasInstance, @@ToPrimitive, @@iterator, @@toStringTag, @@isRegExp.

Anyone plugging into these hooks really should be intentional about what they are doing. Accidentally doing so my poor name choice may mot be disastrous but it is likely to be dificult to debug. Using a symbol for these properties greatly reduces the likelihood of such an accident.

This is indeed the counterargument. We should weigh this against the benefits of iterators being polyfillable.

Let's take a concrete example. CKEditor 4 has a constructor named CKEDITOR.dom.iterator: docs.ckeditor.com/#!/api/CKEDITOR.dom.iterator

The full extent of trouble we can expect from this naming conflict is that if somone accidentally did

arr = Array.from(CKEDITOR.dom);

the error message would say something like "iterator object has no .next() method" rather than "Object not iterable". The mistake seems unlikely in the first place, and the resulting error is actually no harder to debug.

We could make an exception for iterator, but why? That just introduces an inconsistency in the design.

I think I mentioned several reasons. But mainly, iteration is worth polyfilling, and making it polyfillable will put it in users' hands literally years before symbols become universally available.

I don't think the inconsistency will even be all that unaesthetic. ES5 and previous have protocols too, using plain old string property names: .length, .toString(), .valueOf(), and .prototype. One more is not so bad, if it's something important.

# Brandon Benvie (12 years ago)

On 6/25/2013 1:09 PM, Jason Orendorff wrote:

On Tue, Jun 25, 2013 at 12:33 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

This design discussion really wasn't just about iterator. In ES6 we have various property keys that are hooks deeply into either the semantics of core language features: @@create, @@hasInstance, @@ToPrimitive, @@iterator, @@toStringTag, @@isRegExp.

Anyone plugging into these hooks really should be intentional about what they are doing. Accidentally doing so my poor name choice may mot be disastrous but it is likely to be dificult to debug. Using a symbol for these properties greatly reduces the likelihood of such an accident. This is indeed the counterargument. We should weigh this against the benefits of iterators being polyfillable.

Let's take a concrete example. CKEditor 4 has a constructor named CKEDITOR.dom.iterator: docs.ckeditor.com/#!/api/CKEDITOR.dom.iterator

The full extent of trouble we can expect from this naming conflict is that if somone accidentally did

 arr = Array.from(CKEDITOR.dom);

the error message would say something like "iterator object has no .next() method" rather than "Object not iterable". The mistake seems unlikely in the first place, and the resulting error is actually no harder to debug.

We could make an exception for iterator, but why? That just introduces an inconsistency in the design. I think I mentioned several reasons. But mainly, iteration is worth polyfilling, and making it polyfillable will put it in users' hands literally years before symbols become universally available.

I don't think the inconsistency will even be all that unaesthetic. ES5 and previous have protocols too, using plain old string property names: .length, .toString(), .valueOf(), and .prototype. One more is not so bad, if it's something important.

-j


es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss

It's not just about backward compatibility, but also usability. Mandating the addition of a non-symbol property in order to work with a protocol (in this case the iterator protocol) is not good.

 var dict = Object.create(null);
 Object.defineProperty(dict, 'iterator', { value: function*(){ 

/.../ } })

It's __proto__ all over again (looking at proto as the "get/set [[Prototype]] protocol"). Just say no.

# Jason Orendorff (12 years ago)

On Tue, Jun 25, 2013 at 3:19 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

It's not just about backward compatibility, but also usability. Mandating the addition of a non-symbol property in order to work with a protocol (in this case the iterator protocol) is not good.

I don't buy this. Dynamic languages are full of protocols -- any time a function calls a method on an argument, there's a protocol involved. Duck typing.

Now that JS is getting symbols, things might change, but I expect methods will still have plain old string names, just like always. The class syntax was designed with this assumption.

var dict = Object.create(null);
Object.defineProperty(dict, 'iterator', { value: function*(){ /*...*/ }

})

You can certainly still use an object as a worse-is-better dictionary; you'd just leave the iterator code outside of the object:

function* props(obj) {
    for (var k in obj)
        yield [k, obj[k]];
}

for (let [k, v] of props(dict))
    ...

This is probably what you'd want to do anyway. It's less code.

Since you want something iterable, it's also worth considering Map. Iteration built in!

It's __proto__ all over again (looking at proto as the "get/set [[Prototype]] protocol"). Just say no.

...Oh, that seems like a bit much. proto was opt-out, for one thing. Iterators are opt-in.

# Brendan Eich (12 years ago)

I think Jason wins. But the game is not over. Brandon?

# Brandon Benvie (12 years ago)

On 6/25/2013 2:17 PM, Jason Orendorff wrote:

On Tue, Jun 25, 2013 at 3:19 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

It's not just about backward compatibility, but also usability. Mandating the addition of a non-symbol property in order to work with a protocol (in this case the iterator protocol) is not good. I don't buy this. Dynamic languages are full of protocols -- any time a function calls a method on an argument, there's a protocol involved. Duck typing.

Now that JS is getting symbols, things might change, but I expect methods will still have plain old string names, just like always. The class syntax was designed with this assumption.

 var dict = Object.create(null);
 Object.defineProperty(dict, 'iterator', { value: function*(){ /*...*/ }

}) You can certainly still use an object as a worse-is-better dictionary; you'd just leave the iterator code outside of the object:

 function* props(obj) {
     for (var k in obj)
         yield [k, obj[k]];
 }

 for (let [k, v] of props(dict))
     ...

This is probably what you'd want to do anyway. It's less code.

Since you want something iterable, it's also worth considering Map. Iteration built in!

It's __proto__ all over again (looking at proto as the "get/set [[Prototype]] protocol"). Just say no. ...Oh, that seems like a bit much. proto was opt-out, for one thing. Iterators are opt-in.

-j

I agree that Map is better than dict, proto is opt in, etc. Using "iterator" smelled wrong to me and I hadn't fully thought through exactly why. I should have made the point more directly.

Symbols provide a way of exposing meta-object protocol extension points via the normal object protocol without requiring a cumbersome layer of stratification, such as how Proxies separate the handler from the exposed object (the proxy). Things that tie into syntax or spec internals are prime targets for exposure through this way, such as @@create and @@hasInstance (as well as proposed ideas such as @@call, @@construct, @@geti, @@seti, etc.).

I think that the iteration protocol falls under the type of thing that is better exposed through this layer of faux-stratification rather than completely unstratified, due to its primary use in supporting syntax (for-of). The reason there's any argument against it is because, unlike most of the other extension points, there's some amount of "manually cranking the wheel" that can be useful with the iteration protocol.

(I also don't think that polyfillability is a good argument here, because as I said before, it's possible to create a polyfill that works in ES3-ES6 that provides a compatibility layer over iteration.)

# Allen Wirfs-Brock (12 years ago)

On Jun 25, 2013, at 7:36 PM, Brandon Benvie wrote:

On 6/25/2013 2:17 PM, Jason Orendorff wrote:

On Tue, Jun 25, 2013 at 3:19 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

It's not just about backward compatibility, but also usability. Mandating the addition of a non-symbol property in order to work with a protocol (in this case the iterator protocol) is not good. I don't buy this. Dynamic languages are full of protocols -- any time a function calls a method on an argument, there's a protocol involved. Duck typing.

Now that JS is getting symbols, things might change, but I expect methods will still have plain old string names, just like always. The class syntax was designed with this assumption.

var dict = Object.create(null);
Object.defineProperty(dict, 'iterator', { value: function*(){ /*...*/ }

}) You can certainly still use an object as a worse-is-better dictionary; you'd just leave the iterator code outside of the object:

function* props(obj) {
    for (var k in obj)
        yield [k, obj[k]];
}

for (let [k, v] of props(dict))
    ...

This is probably what you'd want to do anyway. It's less code.

Since you want something iterable, it's also worth considering Map. Iteration built in!

It's __proto__ all over again (looking at proto as the "get/set [[Prototype]] protocol"). Just say no. ...Oh, that seems like a bit much. proto was opt-out, for one thing. Iterators are opt-in.

-j

I agree that Map is better than dict, proto is opt in, etc. Using "iterator" smelled wrong to me and I hadn't fully thought through exactly why. I should have made the point more directly.

Symbols provide a way of exposing meta-object protocol extension points via the normal object protocol without requiring a cumbersome layer of stratification, such as how Proxies separate the handler from the exposed object (the proxy). Things that tie into syntax or spec internals are prime targets for exposure through this way, such as @@create and @@hasInstance (as well as proposed ideas such as @@call, @@construct, @@geti, @@seti, etc.).

I think that the iteration protocol falls under the type of thing that is better exposed through this layer of faux-stratification rather than completely unstratified, due to its primary use in supporting syntax (for-of). The reason there's any argument against it is because, unlike most of the other extension points, there's some amount of "manually cranking the wheel" that can be useful with the iteration protocol.

+1 and well said.

(I also don't think that polyfillability is a good argument here, because as I said before, it's possible to create a polyfill that works in ES3-ES6 that provides a compatibility layer over iteration.)

I.m also dubious of the polyfill argument in this situation where the primary use case (for-of) isn't polyfillable.

We are already highly constrained by backwards compatibility requirements. We shouldn't further constrain our options making requiring polyfillability.

The transition period is so much shorter then the future in front of us...

# Claude Pache (12 years ago)

Iterables are useful not only in for/of loops but also in otherwise polyfillable constructs; for example, in the Set constructor:

// Let `a` be a Set object
b = new Set(a) // copy the elements of `a` into a new Set 

Question: Will there be a standard way for obtaining the @@iterator symbol for use in JS code? If so, let say that it is called GetIteratorSymbol(); then in pre-ES6 environments, we could define:

function GetIteratorSymbol() { return '__iterator__' }

and implement/polyfill the iteration protocol in both old and new environments, using GetIteratorSymbol() where @@iterator is needed. If that GetIteratorSymbol() function is readily available in a standard place, it will allow different libraries to cooperate.

—Claude

Le 26 juin 2013 à 05:57, Allen Wirfs-Brock <allen at wirfs-brock.com> a écrit :

# Jason Orendorff (12 years ago)

On Tue, Jun 25, 2013 at 9:36 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

I think that the iteration protocol falls under the type of thing that is better exposed through this layer of faux-stratification rather than completely unstratified, due to its primary use in supporting syntax (for-of). The reason there's any argument against it is because, unlike most of the other extension points, there's some amount of "manually cranking the wheel" that can be useful with the iteration protocol.

The main use of the iterable protocol is as a contract between a function and its caller: a function may expect an iterable for an argument, and the caller has the flexibility of passing a great many different things.

The value of this protocol, and the language features supporting it, is determined by how much user code is written to take advantage of it. There's a network effect. If everyone uses the iterable protocol, it's great. The more objects and APIs don't support it, the worse it is.

This means that making the protocol accessible to programmers now makes it, and its supporting syntax, much more useful in the future, to the degree programmers choose to pre-populate the world with iterable objects and APIs that consume them. This is about the end state, not the short term.

Aside from all that... I get your argument, but I can't help feeling that, practically, iterator() is a lot less "meta" and more user-level than the other features. It's one thing if a feature hooks into property access. iterator() is just a method. for-of is just syntactic sugar. @@seti, @@geti, @@call, and @@construct are pretty clearly in another category, at least to me.

(I also don't think that polyfillability is a good argument here, because as I said before, it's possible to create a polyfill that works in ES3-ES6 that provides a compatibility layer over iteration.)

Well... I think a pretty strong argument could be made that we on TC39 don't discount the future enough, that we assume the future we can predict and affect is longer (and the interest rate lower) than it really is. But I'm not the person to make that argument. I don't have the temperament for it. Maybe Rick or Tab. :-)

Seriously, I don't make a habit of focusing on the short term and that's not what I'm doing here.

# Dean Landolt (12 years ago)

On Wed, Jun 26, 2013 at 6:23 AM, Claude Pache <claude.pache at gmail.com>wrote:

Iterables are useful not only in for/of loops but also in otherwise polyfillable constructs; for example, in the Set constructor:

// Let `a` be a Set object
b = new Set(a) // copy the elements of `a` into a new Set

Question: Will there be a standard way for obtaining the @@iterator symbol for use in JS code? If so, let say that it is called GetIteratorSymbol(); then in pre-ES6 environments, we could define:

function GetIteratorSymbol() { return '__iterator__' }

and implement/polyfill the iteration protocol in both old and new environments, using GetIteratorSymbol() where @@iterator is needed. If that GetIteratorSymbol() function is readily available in a standard place, it will allow different libraries to cooperate.

This is very similar to what I suggesting yesterday, though perhaps a little less noisy. I was beating around the Object.[get|set]IteratorOf bush but something like Object.getIteratorKey would solve the polyfill interop problem with just one addition to a system builtin. Perhaps Reflect.getIteratorKey would be a better place.

# Domenic Denicola (12 years ago)

From: Claude Pache

implement/polyfill the iteration protocol in both old and new environments, using GetIteratorSymbol() where @@iterator is needed. If that GetIteratorSymbol() function is readily available in a standard place, it will allow different libraries to cooperate.

I think this is not necessary for library cooperation or forward-compatible polyfilling. It may make it nicer, but it's not necessary, and I'd predict not even that helpful.

It's very easy for a community to standardize on a simple property name, whether it be iterator or __iterator__, without needing a blessed one handed down from TC39. The polyfill community can probably figure this out themselves. I can't see any polyfill maintainers rejecting a pull request to switch to a more widely-used name. And I don't think there will be that many incompatible polyfill libraries out there anyway; in ES5 at least, we've seen one ascendant.

As for forward compatibility, this is pretty simple: just use eval. Icky and annoying, but it works. If you detect native iterator support, don't polyfill.

# Dean Landolt (12 years ago)

On Wed, Jun 26, 2013 at 9:38 AM, Jason Orendorff <jason.orendorff at gmail.com>wrote:

On Tue, Jun 25, 2013 at 9:36 PM, Brandon Benvie <bbenvie at mozilla.com> wrote:

I think that the iteration protocol falls under the type of thing that is better exposed through this layer of faux-stratification rather than completely unstratified, due to its primary use in supporting syntax (for-of). The reason there's any argument against it is because, unlike most of the other extension points, there's some amount of "manually cranking the wheel" that can be useful with the iteration protocol.

The main use of the iterable protocol is as a contract between a function and its caller: a function may expect an iterable for an argument, and the caller has the flexibility of passing a great many different things.

The value of this protocol, and the language features supporting it, is determined by how much user code is written to take advantage of it. There's a network effect. If everyone uses the iterable protocol, it's great. The more objects and APIs don't support it, the worse it is.

This means that making the protocol accessible to programmers now makes it, and its supporting syntax, much more useful in the future, to the degree programmers choose to pre-populate the world with iterable objects and APIs that consume them. This is about the end state, not the short term.

I completely agree, and would just add that this argument generalizes to many new es6 features that would be polyfillable but for...

Aside from all that... I get your argument, but I can't help feeling that, practically, iterator() is a lot less "meta" and more user-level than the other features. It's one thing if a feature hooks into property access. iterator() is just a method.

I assume the primary reason to hang the iterator reference directly off an object is so that it can be prototypally overloaded -- would you agree? Given that library code tends toward being as generic as possible, I can just picture the schism in approaches for looking up the Right iterator. It won't take long before you start seeing branching for typeof iterator != 'function' (or even deeper feature tests on the iterator return value). There will be a schism between factions that walk up the prototype chain applying this same silly logic and those that go strait for their own iterator. This is inevitable and will create interop headaches and make it harder to count on reliable prototypal overloading of iteration.

This is the crux of my complaint about the plain iterator key, and I don't think it's been addressed yet.

[snipped the rest]

# Dean Landolt (12 years ago)

On Wed, Jun 26, 2013 at 11:16 AM, Domenic Denicola < domenic at domenicdenicola.com> wrote:

From: Claude Pache

implement/polyfill the iteration protocol in both old and new environments, using GetIteratorSymbol() where @@iterator is needed. If that GetIteratorSymbol() function is readily available in a standard place, it will allow different libraries to cooperate.

I think this is not necessary for library cooperation or forward-compatible polyfilling. It may make it nicer, but it's not necessary, and I'd predict not even that helpful.

It's very easy for a community to standardize on a simple property name, whether it be iterator or __iterator__, without needing a blessed one handed down from TC39. The polyfill community can probably figure this out themselves. I can't see any polyfill maintainers rejecting a pull request to switch to a more widely-used name. And I don't think there will be that many incompatible polyfill libraries out there anyway; in ES5 at least, we've seen one ascendant.

I hope you're right, but I doubt it. At least in this case the language clearly defines the semantics of the underlying protocol so it seems likepolyfills will only have one thing to agree on. But (especially if a particularly non-unique key like iterator or __iterator__ is chosen) they will also have to agree on what it to do when they see something that doesn't adhere to those semantics, and the space of possibilities is vast. There will be pain.

GetIteratorSymbol or something like it could solve all of this with the a few flicks of AWB's fingers -- what's not to like?

As for forward compatibility, this is pretty simple: just use eval. Icky and annoying, but it works. If you detect native iterator support, don't polyfill.

That's fine for sniffing for for/of support but tells you nothing about whether you've already been polyfilled, and what else you may need to do. Iteration polyfills will likely want to shim some or all of the built-in iterators. So they'll have to go somewhere too, but where?

Does the spec. already give us a way to get at the system modules cleanly or are we going to have to shim in enough of a dynamic loader to allow our polyfill-dependent code to "load" them, awkwardly? How will we keep these loaders from stepping on each other? This is a more important issue than whether or not the iterator key is a unique symbol, but if there were a coherent way to polyfill system modules it would solve this problem too. The iteration key live in @iter -- any polyfills wouldn't have to agree on what it is, they would just look it up from here. Typical to polyfills, the first shimmer wins WRT what the filled-in key looks like. Other built-ins could be tested and filled in a similar fashion. Problem solved.

I apologize if this has been covered already -- is there any plan to expose the built-in system modules in global space, one way or another? I try to follow this list closely but I haven't seen much discussion about it.

# Jason Orendorff (12 years ago)

On Wed, Jun 26, 2013 at 10:20 AM, Dean Landolt <dean at deanlandolt.com> wrote:

I assume the primary reason to hang the iterator reference directly off an object is so that it can be prototypally overloaded -- would you agree?

Sure. Polymorphism, therefore a method.

Given that library code tends toward being as generic as possible, I can just picture the schism in approaches for looking up the Right iterator.

If we make the name just plain "iterator", there's a conventional right answer for this: typeof obj.iterator === "function". See the tests for .toString and .valueOf methods in [[ToPrimitive]] (now OrdinaryToPrimitive in ES61), the test for a .toJSON method in 2, and the test for .then in DOM Promises.3

If it's a symbol, it appears obj[iteratorSymbol] !== undefined is what the spec is doing, at least in one place, the test for a .@@ToPrimitive method in 1.

Note that IsCallable(obj) is the same thing as typeof obj === "function". 4, 5

Of course existing code on the Web uses every possible flavor of type-test, and always will, whether the iterator method name is a string, a symbol, or a suffusion of yellow. "Schism" isn't the word. "Free-for-all" maybe.

# Dean Landolt (12 years ago)

On Wed, Jun 26, 2013 at 12:15 PM, Jason Orendorff <jason.orendorff at gmail.com

wrote:

On Wed, Jun 26, 2013 at 10:20 AM, Dean Landolt <dean at deanlandolt.com> wrote:

I assume the primary reason to hang the iterator reference directly off an object is so that it can be prototypally overloaded -- would you agree?

Sure. Polymorphism, therefore a method.

Given that library code tends toward being as generic as possible, I can just picture the schism in approaches for looking up the Right iterator.

If we make the name just plain "iterator", there's a conventional right answer for this: typeof obj.iterator === "function".

Yes -- my point about the schism is what libraries will decided to do when that test fails. What if iterator is present but not a function? Do you walk the prototype chain anyway? Blow up? Punt and lookup an iterator directly based on a mapping with some type testing? What kind of type testing? If this is easy to avoid, why not?

See the tests for .toString and .valueOf methods in [[ToPrimitive]] (now OrdinaryToPrimitive in ES61), the test for a .toJSON method in 2, and the test for .then in DOM Promises.3

I strongly suspect the language will evolve these into legacy warts, but as far as I know when these were introduced there weren't bodies of code that would obviously blow up (except 3 , but I'll get to that), and if they were, that blood was spilled long ago. I've come across quite a bit of code that treat objects as maps. Yes, I know we real maps now, and that's great, but authors of generic library code can't just cross their fingers and hope downstream users will do the Right Thing. They'll avoid iterators entirely, or if not, they'll certainly have to skip the iterator method lookup if they want to avoid weird bug reports. That's a fail.

And yes, 3 will be a completely unnecessary mistake if allowed to stand, and for similar reasons. Assuming the system module could act as a namespace for branding keys it's completely incomprehensible to my why the Promises/A+ folks would be unwilling to add an attempted lookup for this brand as a synonym for then to their spec. This is what I'd proposed as Promises/A++, but nobody seemed terribly interested. But the then ducktest has no place in the web platform -- it's nothing but a hazard. But that's a rant for another thread.

If it's a symbol, it appears obj[iteratorSymbol] !== undefined is what the spec is doing, at least in one place, the test for a .@@ToPrimitive method in 1.

Note that IsCallable(obj) is the same thing as typeof obj === "function". 4, 5

Of course existing code on the Web uses every possible flavor of type-test, and always will, whether the iterator method name is a string, a symbol, or a suffusion of yellow. "Schism" isn't the word. "Free-for-all" maybe.

I think you may have missed my point about where the schism is. I hope I've made it more clear above and in prior posts but to restate: the schism isn't the ducktest, it's that the ducktest isn't a quite a predicate -- sometimes it can fail in a way where you may want to proceed up the prototype chain (or do something else). This is where I believe there are interop landmines buried.

# Brendan Eich (12 years ago)

Dean Landolt wrote:

sometimes it can fail in a way where you may want to proceed up the prototype chain (or do something else). This is where I believe there are interop landmines buried.

How is this different from toString?

# Dean Landolt (12 years ago)

On Wed, Jun 26, 2013 at 1:31 PM, Brendan Eich <brendan at mozilla.com> wrote:

Dean Landolt wrote:

sometimes it can fail in a way where you may want to proceed up the prototype chain (or do something else). This is where I believe there are interop landmines buried.

How is this different from toString?

High-integrity generic code sidestes this problem by deferring to Object.prototype.toString.call, the consequence being that you can never really rely on a toString override. We make do, sure, but it sucks. There's no good reason we should have to pay this price for iterable -- especially when the language is finally delivering exactly what's needed (unique symbols) to avoid this issue going forward.

# Jason Orendorff (12 years ago)

On Wed, Jun 26, 2013 at 12:15 PM, Dean Landolt <dean at deanlandolt.com> wrote:

On Wed, Jun 26, 2013 at 12:15 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

If we make the name just plain "iterator", there's a conventional right answer for this: typeof obj.iterator === "function".

Yes -- my point about the schism is what libraries will decided to do when that test fails.

If the test returns false, then the object is not iterable.

What if iterator is present but not a function? Do you walk the prototype chain anyway? Blow up? Punt and lookup an iterator directly based on a mapping with some type testing? [...]

Well, no, it should be treated like any other non-iterable object.

If JS were being designed green-field today, maybe some of us would make array.length a symbol, not because it's "meta" (meaning, it is used by some key builtins, notably including Function.prototype.apply) and not because programmers might want to write a function that accepts an argument that's either an arraylike object or a dictionary. Those cases do exist, but it's just not a problem. Making .length a symbol would be a mistake.

# Jason Orendorff (12 years ago)

On Wed, Jun 26, 2013 at 5:10 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

If JS were being designed green-field today, maybe some of us would make array.length a symbol, not because it's "meta" (meaning, it is used by some key builtins, notably including Function.prototype.apply) and not because programmers might want to write a function that accepts an argument that's either an arraylike object or a dictionary. Those cases do exist, but it's just not a problem. Making .length a symbol would be a mistake.

Oops, s/not because/because/g.

# Dean Landolt (12 years ago)

On Wed, Jun 26, 2013 at 6:10 PM, Jason Orendorff <jason.orendorff at gmail.com>wrote:

On Wed, Jun 26, 2013 at 12:15 PM, Dean Landolt <dean at deanlandolt.com> wrote:

On Wed, Jun 26, 2013 at 12:15 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

If we make the name just plain "iterator", there's a conventional right answer for this: typeof obj.iterator === "function".

Yes -- my point about the schism is what libraries will decided to do when that test fails.

If the test returns false, then the object is not iterable.

What if iterator is present but not a function? Do you walk the prototype chain anyway? Blow up? Punt and lookup an iterator directly based on a mapping with some type testing? [...]

Well, no, it should be treated like any other non-iterable object.

Oh? I know I've personally created boolean database columns named "iterator" -- I'm sure I'm not alone. I pity the poor ORM user that tries to pass their objects to a library function which tries to iterate without fallback. Of course we know what's going to happen -- confusing bug reports will pressure library authors to hack in fallbacks, or just skip the unstratified iterator call entirely. Do you disagree? How else do you see libraries handling this specific case?

Spelling it iterator will work, but at what cost? The value of the protocol is seriously diminished.

If JS were being designed green-field today, maybe some of us would make array.length a symbol, not because it's "meta" (meaning, it is used by some key builtins, notably including Function.prototype.apply) and not because programmers might want to write a function that accepts an argument that's either an arraylike object or a dictionary. Those cases do exist, but it's just not a problem. Making .length a symbol would be a mistake.

I disagree that it would be an obvious mistake. Perhaps without syntax help from the language it would. But if there were a convenient syntax to reference system symbols like these it would be objectively better as a symbol. But we're not green-fielding javascript, so any Array.prototype.length comparison is nonsensical.

Just because you assert this isn't a problem doesn't make it so. What you're suggesting cripples the polymorphism win for no particularly good reason, and for what? To avoid defining some kind of mapping between system modules and the pre-es6 primordials? I contend that this mapping is inevitable...

If I have it right the crux of your argument is that using a string key makes this feature more polyfillable, but where do you propose polyfills put the iterators from the @iter module? I'm confident that one or more of of the polyfills will invent some kind of mapping. Can you already hear the calls for an ad hoc standard? Ugh. Let's end the madness before it starts and carve out a place for the system modules in the global namespace. I can imagine a bunch of ways to do this -- but I don't care much about how it happens.

# Jason Orendorff (12 years ago)

On Wed, Jun 26, 2013 at 7:54 PM, Dean Landolt <dean at deanlandolt.com> wrote:

On Wed, Jun 26, 2013 at 6:10 PM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

What if iterator is present but not a function? Do you walk the prototype chain anyway? Blow up? Punt and lookup an iterator directly based on a mapping with some type testing? [...]

Well, no, it should be treated like any other non-iterable object.

Oh? I know I've personally created boolean database columns named "iterator" -- I'm sure I'm not alone. I pity the poor ORM user that tries to pass their objects to a library function which tries to iterate without fallback. Of course we know what's going to happen -- confusing bug reports will pressure library authors to hack in fallbacks, or just skip the unstratified iterator call entirely. Do you disagree? How else do you see libraries handling this specific case?

I think I disagree, but I'm afraid I don't fully appreciate your point yet.

If you mean a library function that expects either an iterable or a dictionary, then it would correctly treat your non-iterable data object as a dictionary. So... I guess I don't see the problem. Everything seems fine.

Surely "length" is a more common database column name than "iterator". So if you're right, surely we already have these problems with existing library functions that take arraylike objects. Is that the case? Are there confusing bug reports and hacked-in fallbacks?

# Dean Landolt (12 years ago)

On Thu, Jun 27, 2013 at 11:56 AM, Jason Orendorff <jason.orendorff at gmail.com

wrote:

On Wed, Jun 26, 2013 at 7:54 PM, Dean Landolt <dean at deanlandolt.com> wrote:

On Wed, Jun 26, 2013 at 6:10 PM, Jason Orendorff < jason.orendorff at gmail.com> wrote:

What if iterator is present but not a function? Do you walk the prototype chain anyway? Blow up? Punt and lookup an iterator directly based on a mapping with some type testing? [...]

Well, no, it should be treated like any other non-iterable object.

Oh? I know I've personally created boolean database columns named "iterator" -- I'm sure I'm not alone. I pity the poor ORM user that tries to pass their objects to a library function which tries to iterate without fallback. Of course we know what's going to happen -- confusing bug reports will pressure library authors to hack in fallbacks, or just skip the unstratified iterator call entirely. Do you disagree? How else do you see libraries handling this specific case?

I think I disagree, but I'm afraid I don't fully appreciate your point yet.

If you mean a library function that expects either an iterable or a dictionary, then it would correctly treat your non-iterable data object as a dictionary. So... I guess I don't see the problem. Everything seems fine.

Surely "length" is a more common database column name than "iterator". So if you're right, surely we already have these problems with existing library functions that take arraylike objects. Is that the case? Are there confusing bug reports and hacked-in fallbacks?

Apples and oranges -- people don't use arrays as maps. I've seen countless cases where object keys . Perhaps a better example would be hasOwnProperty -- I know there have been bug reports. The MDN page goes out of its way to warn about this [1]:

JavaScript does not protect the property name hasOwnProperty; thus, if the possibility exists that an object might have a property with this name, it is necessary to use an external hasOwnProperty to get correct results ...

That's right -- MDN goes out of its way to recommend Object.prototype.hasOwnProperty.call. This is exactly what I fear. It's not a huge loss for hasOwnProperty (or toString) -- experienced JS devs just accept the fact that polymorphism can't be counted on for these properties. It would be tragic for this fate to befall iterator polymorphism. In part because it's unnecessary, but also because polymorphism is central to iterator's usefulness (which I know you agree about, otherwise you wouldn't be arguing so forcefully against symbol-based keys).

[1] developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

# Jason Orendorff (12 years ago)

On Thu, Jun 27, 2013 at 1:04 PM, Dean Landolt <dean at deanlandolt.com> wrote:

On Thu, Jun 27, 2013 at 11:56 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

If you mean a library function that expects either an iterable or a dictionary, then it would correctly treat your non-iterable data object as a dictionary. So... I guess I don't see the problem. Everything seems fine.

Dean, I would appreciate a response to this. An example of a specific case where there would be an actual problem would help the conversation tremendously.

Surely "length" is a more common database column name than "iterator". So if you're right, surely we already have these problems with existing library functions that take arraylike objects. Is that the case? Are there confusing bug reports and hacked-in fallbacks?

Apples and oranges -- people don't use arrays as maps.

People won't use iterables as maps either; the kind of maps we are talking about are ObjectLiterals, and they are not iterable. (You can iterate over their properties, though, using a 4-line generator or Object.keys().)

The reason I brought up that example is that people do use objects with .length properties as arraylikes, leading to potential ambiguity about whether an object with a .length property is a plain-Object map or an arraylike. I thought perhaps that was the sort of confusion you were concerned about.

Perhaps a better example would be hasOwnProperty -- I know there have been bug reports. The MDN page goes out of its way to warn about this [1]:

This is a problem because .hasOwnProperty is an operation that people naturally want to apply to plain-Object maps.

.iterator() is not, because plain Objects are not iterable.

# Dean Landolt (12 years ago)

On Fri, Jun 28, 2013 at 9:59 AM, Jason Orendorff <jason.orendorff at gmail.com>wrote:

On Thu, Jun 27, 2013 at 1:04 PM, Dean Landolt <dean at deanlandolt.com> wrote:

On Thu, Jun 27, 2013 at 11:56 AM, Jason Orendorff <jason.orendorff at gmail.com> wrote:

If you mean a library function that expects either an iterable or a dictionary, then it would correctly treat your non-iterable data object as a dictionary. So... I guess I don't see the problem. Everything seems fine.

Dean, I would appreciate a response to this. An example of a specific case where there would be an actual problem would help the conversation tremendously.

Surely "length" is a more common database column name than "iterator". So if you're right, surely we already have these problems with existing library functions that take arraylike objects. Is that the case? Are there confusing bug reports and hacked-in fallbacks?

Apples and oranges -- people don't use arrays as maps.

People won't use iterables as maps either; the kind of maps we are talking about are ObjectLiterals, and they are not iterable. (You can iterate over their properties, though, using a 4-line generator or Object.keys().)

The reason I brought up that example is that people do use objects with .length properties as arraylikes, leading to potential ambiguity about whether an object with a .length property is a plain-Object map or an arraylike. I thought perhaps that was the sort of confusion you were concerned about.

You've got bigger problems if you're trying this -- the language defines semantics for length on strings and arrays -- generic code would be foolish to try and go further. But this example does in fact kill my argument (see below).

An aside -- yes, in my ideal world there would be a unique symbol that stood in for the count of a thing (how many distinct items it has). More than one, in fact, since there are competing notions of counts on the same kind of thing. If designed carefully this kind of approach could go a long way toward cleaning up the notion of types in javascript...but I'm way off topic...

Perhaps a better example would be hasOwnProperty -- I know there have been bug reports. The MDN page goes out of its way to warn about this [1]:

This is a problem because .hasOwnProperty is an operation that people naturally want to apply to plain-Object maps.

.iterator() is not, because plain Objects are not iterable.

I was missing this detail -- I remember the discussion around this but for some reason was assuming a different design with a default iterator on Object.prototype, and the built-in iterator methods deferring to this). Admittedly this weakens my arguments. But I still wonder how the actual built-in iterators are expected to be shimmed (without shimming a whole module system)? From this thread I believe this is the core problem that has yet to be addressed. If solved it would also make system symbols much less inconvenient for polyfills.

From this perspective it seems unnecessary to, umm, pee in the namespace

pool. But yeah, my strongest arguments for iterator-as-symbol are pretty well mooted :)