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.@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.@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
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to