Iteration in ES4

Brendan Eich brendan at mozilla.org
Fri Apr 25 14:08:23 PDT 2008


On Apr 25, 2008, at 12:09 PM, Jason Orendorff wrote:
> Here are some more comments on iteration in ES4; still more to come.

Great to have feedback on a spec derived from a pretty old proposal  
-- better late than not in time. :-)


> === for-each on Array objects ===
>
> The planned behavior, as far as I can discern it, is like this:
>
>   - Properties will be visited in the order they were added.
>   - Enumerable properties of Array.prototype will be visited.  (This
>     will hurt libraries that add extra Array methods there, like
>     Prototype <http://www.prototypejs.org/api/array>.  There are also
>     more obscure cases.)


You are concerned with for-each-in, I know, but the same concern  
arises with for-in. This has come up many times, and there is a  
converging (I hope) proposal to add Object.defineProperty or  
something named similarly, which allows "DontEnum" properties to be  
added to objects, including to standard constructor prototypes.


>   - Non-numeric ("expando") properties will be visited.

Similarity to for-in, which is bound by backward compatibility. One  
improvement: the iteration protocol allows swapping in better  
behavior. From the proposal:

Array.prototype.iterator::get      = iterator::DEFAULT_GET_VALUES;
Array.prototype.iterator::contains = function (v) this.indexOf(v) != -1;

Then one does not have to use for-each-in at all, and Arrays become  
much more pleasant to use in a small-world or modular program that  
customizes Array.prototype like that.

> I think users will find all these details astonishing and undesirable.
> The first seems especially perverse.  No prior standard requires it.

You probably have read 4.2 in http://www.ecmascript.org/es4/spec/ 
incompatibilities.pdf but I thought I'd point to it for the list. The  
de-facto standard set by competing browser implementations starting  
in 1995 trumps the de-jure standard regarding for-in enumeration  
following property creation order, even for arrays.

But for-each-in could do otherwise. That's a fair point, since in E4X  
(ECMA-357), for-each-in follows XMLList child index order.

So let's say we make for-each-in special for Array, as it is for  
XMLList (and XML, but vacuously) in E4X. Now for-each-in and for-in  
differ more substantially than in the former enumerating values and  
the latter keys. This could be a good thing, but it might be annoying  
if one is rewriting code that does

for (prop in obj) {
     ... obj[prop] ...
}

to look like

for each (value in obj) {
     ... value ...
}

where obj might be an Array. The symmetry between for-each-in and for- 
in that E4X half-supports (viz, prototype property enumeration with  
shadowing, and deleted-after-loop-starts coherence) is broken.


> The latter two are kind of implicitly specified in E4X.

The conversations I've had with E4X principals suggest they did not  
intend for-each-in to consider prototype properties at all. But the  
spec flatly contradicts that intention in the Semantics section:

The order of enumeration is defined by the object (steps 6 and 6a in  
the first algorithm and steps 7 and 7a in the second algorithm). When  
e evaluates to a value of type XML or XMLList, properties are  
enumerated in ascending sequential order according to their numeric  
property names (i.e., document order for XML objects).

The mechanics of enumerating the properties (steps 7 and 7a in the  
first algorithm, steps 8 and 8a in the second) is implementation  
dependent. Properties of the object being enumerated may be deleted  
during enumeration. If a property that has not yet been visited  
during enumeration is deleted, then it may not be visited. If new  
properties are added to the object being enumerated during  
enumeration, the newly added properties are not guaranteed to be  
visited in the active enumeration. Enumerating the properties of an  
object includes enumerating properties of its prototype and the  
prototype of the prototype, and so on, recursively; but a property of  
a prototype is not enumerated if it is "shadowed" because some  
previous object in the prototype chain has a property with the same  
name.

(end of E4X spec citation)

So intent and spec may be out of whack, and we should consider doing  
something more aligned with intent in ES4.


> A cost-benefit analysis applies here.  The cost of following E4X is  
> real, e.g. Web
> pages that use Prototype can't use for-each on Arrays.

This is a bigger problem for for-in, and most Ajax libraries steer  
clear of adding properties to any standard constructor prototypes. So  
it's a good reminder of a deeper problem, but not as compelling with  
for-each-in as with for-in -- and the ship sailed 13 years ago.

Again I'm not sure if rescuing for-each-in is going to pay off, if  
the price is loss of symmetry with for-in -- and assuming programmers  
can save themselves by customizing via the iteration protocol.


>   I don't see any
> offsetting benefit.  Note that several ES4 classes will have custom
> for-each behavior, so ES4's for-each generally won't behave the way  
> it's
> specified in E4X anyway.

And E4X left order of enumeration up to "the object" anyway. And,  
this may bear repeating, E4X is not in ES4. We don't want to break  
E4X with a conflicting change from ES3, but where it's incomplete or  
buggy, we should not be bound by it.

I like your suggestion to make for-each-in iterate indexed values, in  
index order, for Array. The further thought I would appreciate more  
feedback on is that for-in is in the same boat. While we can extend  
E4X's for-each-in to do what you want for Array, we can't change for- 
in even under opt-in versioning without imposing a hidden, and  
potentially high, migration tax.

Lars and I have talked about supplying a few standard iterators for  
arrays that do follow index order (and are otherwise sane). Then the  
programmer would have to hook these up, or call them explicitly on  
the right of 'in'. We could even sugar the opt-in, potentially a lot  
(a pragma? 'use sane_array_iteration' or something like that). Comments?


> === "Type" suffix on structural type names ===
>
> The proposal defines structural types named IteratorType,  
> IterableType,
> and ItemizableType.  I think they should be named Iterator, Iterable,
> and Itemizable.

I agree. The -Type suffix has bothered me, although the reflect::  
interface names use it too (for better reasons). I'm guilty, I will  
remove it (Dave Herman was going to weigh in on it, and I bet he  
agrees).


> === Generator.throw type parameter ===
>
> The Generator class has a third type parameter, the type of exceptions
> that can be thrown to it.  I can't think of a use case where this
> doesn't feel like the Java "throws" clause, which ES4 otherwise  
> rejects.
> I think the throw method should accept any value, and the third type
> parameter should be dropped.


We don't have declared exceptions in ES4 (and won't in ES-anything),  
but you made me throw up a little in my mouth by reminding me of them  
in Java :-/. I'm ok with this change too, but I'd like Lars and Dave  
to sign off too.


> === Generator return-type annotations ===
>
> The proposal doesn't specify how return-type annotations work on
> generator-functions.  I think generator-functions should only accept a
> return-type annotation that boils down to one of:
>
>   *                       // (the default)
>   Iterator.<X>            // I'm just an iterator
>   Generator.<X, Y>        // I'm a coroutine
>
> The run-time type of the generator-iterators produced by these  
> functions
> would be, respectively:
>
>   Generator.<*, *>
>   Generator.<X, void>   // I'm just an iterator, don't send() me data
>   Generator.<X, Y>      // I'm a coroutine, send() me Ys


I like this too.

Thanks for the great comments!

/be




More information about the Es4-discuss mailing list