lexical for-in/for-of loose end

Brendan Eich brendan at mozilla.org
Wed Feb 1 19:13:13 PST 2012


Allen Wirfs-Brock wrote:
> On Feb 1, 2012, at 3:31 PM, Brendan Eich wrote:
>> Brendan Eich wrote:
>>> It's never inconsistent to allow one thing and disallow another. The 
>>> particulars matter. This isn't "anything goes". Destructuring has a 
>>> bit of utility and a lot of regularity in for-in head position. The 
>>> initialiser from VariableDeclarationNoIn has neither specific 
>>> utility nor regularity with respect to for-in in AWK, Python, or any 
>>> other language with such a construct.
>>
>> More to say, I sent too soon. Don't take this as more than it is: an 
>> attempt to explore an alternative meaning of for-in combined with 
>> certain destructuring patterns.
>>
>> We have a request that seemed to receive popular support, to support
>>
>>  for own (k in o) ...;
>>
>> This could compose with the following nicely, and it tries to pave 
>> the CoffeeScript cowpath a bit (without taking CoffeeScript as 
>> normative or overriding or anything like that).
>
> Towards the end of 
> https://mail.mozilla.org/pipermail/es-discuss/2011-November/018332.html I 
> made an argument  that own-ness of properties  is an implementation 
> choice that normally should not be of concern to application clients 
> of an object-based abstraction.   Own-ness  is primarily relevant to 
> abstraction implementors or somebody who is using reflection to 
> examine the implementation structure of an object.

That's an ideal but the reality is for-in and easy transpilation from 
better derived forms to it are with us, and will be for a while.

> Hopefully reflection is tucked away in a obscure but pleasant corner 
> of the language and not widely used for routine application logic. 
> Generally, reflection doesn't deserve (or require) statement level 
> support.

  still want easy array-value iteration. That's what for-of does out of 
the box. This is important, it shouldn't take any more chars than

   for (v of a)...

eliding declaration of v and decl/init of array a.

> That raises the question of the role of the for-in statement in the 
> language (besides just being an obsolete and less useful alternative 
> to for-of).  Being a full fledged syntactic statement of the language, 
> it certainly isn't hiding in an obscure corner.  If we want to think 
> of it as something other than an over exposed reflection tool, what is 
> it for?.  The best expression of this that I can come up with, is that 
> for-in is a statement that is primarily useful for iterating the "data 
> member keys" of simple record and array like data abstractions.

So far, so good, and latent here is the best case for not elaborating 
the left-hand side to include destructuring, if you can avoid bloating 
the grammar too much. Contra my "regularity plus small use-cases" 
argument, we leave for-in alone. We can even leave it underspecified as 
in ES5.

> From that perspective,  own-ness again is something that should 
> primarily be an implementation concern.

Historically this came up because pre-ES5 people couldn't extend 
Array.prototype, e.g., without making enumerable properties. But of 
course then the wisdom said don't use for-in on arrays. Object.prototype 
is verboten, and at some cost in using custom functional-programming 
style iteration, we're ok (PrototypeJS status quo).

The problem that may remain is that

   for own (k in o) ...;

still beats

   for (k in keys(o)) ...;

not only by a couple of chars not counting import or assuming prelude, 
but in terms of people's historical memory and folk-wisdom learning.

Should we ignore all this and say "just use for-of with the right iterator?"

> To me, for-own-in makes it too easy for client code to create 
> dependencies upon what should inconsequential implementation decisions.

In reality the shoe is on the other foot. Implementation decisions that 
should be without consequence mess up for-in, requiring hasOwnProperty 
testing. People want a shorter-path solution.

Giving a longer-path solution with for-of and iterators is good, we want 
that. Is it good enough to be the only solution we build into the language?

> We have a goal of making ES a language that is better for creating and 
> using abstractions.  For-own-in is a tool that tunnels through 
> abstractions.

I think you're making too sweeping a statement about abstractions. Does 
for-own-in tunnel in ways that violate abstractions, or is it really 
what the doctor ordered for JS close to today's best practices (not 
always followed)? I say more the latter, but not clear cut in any event.

>  Such a tool is fine as part of the reflection facility, but making it 
> a full fledged statement seems like it would be creating an attractive 
> nuisance.

We have a full-fledged statement, for-in. It can't be deprecated quite 
yet. Adding for-of helps, but is it enough? That's the issue I'm raising.


>> We could make just-so meanings for destructuring in for-in, also 
>> inspired by CoffeeScript (and JS1.7, which did this too while 
>> muddying the waters by failing to separate iteration protocol into 
>> for-of):
>>
>>  for ([k, v] in o) ...;
>
> Clearly, this isn't a general destructuring.  It is a special 
> syntactic form that is mimicking array destructuring syntax. We could 
> probably have a long thread about whether such mimicry is clever 
> language design or a confusing creole.  I reserve my opinion on the 
> general questions.

Yes, and CoffeeScript does not use [], so it has a special form: for k, 
v of o ....

I think overloading the [k, v] pattern this way is a mistake, and I'm 
not ready to propose []-free comma-separated key and value special 
casing. Again, es-discuss thought-experiment here, not 
strawman:melt_allens_brain ;-).


>> Our current position is "use for-of and an items helper function":
>>
>>  import items from "@reflect"; // currently @iter; could be part of 
>> standard prelude
>>
>>  for ([k, v] of items(o)) ...;
>
> see the main part of the message I referenced above. Pure functional 
> filters like item are unfriendly to collection abstracton builders.

JS has little of the OO collection building you see in Smalltalk or 
Java. Is this a bug in the language and communities using it? I'm not so 
sure.

Your point that we want functional helpers to delegate to OO methods is 
well-taken, and IIRC that's what Python does. The proposal at

http://wiki.ecmascript.org/doku.php?id=harmony:iterators#standard_api

does not delegate from items(d) to d. at items or whatever it ought to be 
called, but this is easy to add. Cc'ing Jason for his thoughts.


>> But I think detailing the design of ES6 must be allowed to entertain 
>> an even easier-to-use extension to for-in.
>
> Another simplification would be to make default iterator produce 
> {key,value} item objects,
>
> Then we have three concise formulations:
>
> for ({key, value} of o) ...;
> for  ({key} of o) ...;
> for ({value} of o) ...

No, too expensive. We want just the keys or just the values, often 
enough. Narrow is better than wide.


>>  If you want values not keys, then per the current proposal you use
>>
>>  for (v of values(o)) ...;
>>
>> given the necessary values import, or inclusion in a standard prelude.
>>
>> There's a hole vs. CoffeeScript: we do not want for-of on a plain 
>> Object to iterate over enumerable property values. Arrays, yes, 
>> Objects, no -- no Object.prototype. at iterator. So no |for (v of o) 
>> ...| for o = {p:1, q:2, r:3}.
> (I note at this point that there are some huge es-discuss threads on 
> topics such as the above for which the conclusions (if any) have not 
> been extracted. I'm starting to go over them to see what I can extract 
> from them)
>
> I assume that you are arguing that the default @iterator for 
> Object.prototype (however it is provided)

No! I clearly said there is no @iterator in Object.prototype.

> does a key enumeration just like ES1-5 for-in.  Or are you arguing 
> that it produces nothing?

No @iterator in Object.prototype. We've been over this, Jason argued 
convincingly against pre-defining one because it is future-hostile to 
collection iteration.


> I'm not exactly sure why either of those is better than |for (v of o) 
> ...| but I think there are a couple threads that I need to digest that 
> drill into the issues.

No issue here, I think. We want for (x of {p:1, q:2}) to throw, without 
the user first having defined @iterator in Object.prototype (and that 
would not be recommended practice).


>> Also no for each (v in o) as E4X (ECMA-357) promulgated. SpiderMonkey 
>> and Rhino support it and probably will have to carry it, but such 
>> "each" does not say its meaning (values not keys) clearly, and it 
>> doesn't compose with "own" nicely.
>>
>> But with destructuring for-in, you could write
>>
>>  for ([, v] in o) ...;
>
> currently the grammar don't support the above hole syntax.  I wouldn't 
> be a support for adding it.

Wait, holes are useful in destructuring array patterns, to avoid dummy 
bindings. We support holes in array destructuring in JS1.7 up. What is 
the problem? Saying the grammar doesn't support holes and you don't 
support them is no explanation.

>> This is a bit ugly (holes never look pretty, even when they're useful).
>>
>> Not sure what I think of this but I thought I'd throw it out here on 
>> es-discuss. The reason I bring it up is twofold: 1) for own (k in o) 
>> still needs to be discussed; 2) the for ([k, v] of items(o)) ...; tax 
>> is a bit higher than I'd like.
>
> and I'm trying to write spec. language for all of this right now, so 
> it is a good time to make some decisions.

Quoting Jason's reply in the thread you cited:

To address Allen's original question:

I think the Map and Set classes in Harmony mean that not all
structured data is stored as object properties. I think that's a good
thing.

However it does mean that we must have a separation of concerns between
   - iterating over a collection
   - object property inspection
...because I don't see how 'for (p of obj)' can be both the right
syntax for walking an arbitrary object's properties *and* the right
syntax for walking a Set's elements. Someday the "arbitrary object"
will be a Set. And then what?

So if we take that separation of concerns as our maxim, what do we end
up with? Here's a sketch.

Basics:
   for (v of arr)  // each element of an Array
   for (v of set)  // each value in a Set
   for (k of map)  // each key in a Map, following Python

Host objects / libraries:
   for (elt of document.getElementsByTagName('P'))  // DOM
   for (elt of $("div.main p"))  // hypothetical jQuery support

Other styles of iteration:
   for ([i, v] of arr.items())  // index-value pairs (new Array method)
   for ([k, v] of map.items())  // key-value pairs
   for (v of map.values())  // just values, no keys (uncommon use case)

Enumerating properties:
   import * from '@inspect';
   for (p of keys(obj))
   for (v of values(obj))
   for ([p, v] of items(obj))
Or if you don't want to import anything:
   for (p of Object.keys(obj))
   for (p of Object.getOwnPropertyNames(obj))

This makes Map slightly nicer to iterate over than Object. I think Map
is a better, less error-prone Map than Object anyway, so that's
appropriate.

-j


I think we are close, but not quite where we should be. If we define 
keys, values, and items to work in terms of properties (they're 
currently spec'ed using for-in under the hood), are we missing a chance 
to make these universal for collections that have keys and values? Set 
can have values only, but the idea is to delegate from function to OO 
method (with private names @keys, @values, @items). Or do something better.

Less is more in our world, private name objects shedding syntax and 
getting into ES6 is one example. But we then talked about experience 
possibly justifying syntax too (I've used @ above). Could the same thing 
happen with iteration? I don't want to overdesign (who does? wait, 
stop...) but doing too little will just create obligatory library downloads.

/be


More information about the es-discuss mailing list