for-of statement of sparse array
On Thu, Jul 5, 2012 at 6:11 AM, teramako <teramako at gmail.com> wrote:
Firefox 15 or later can use for-of statement. I have a question about this.
var array = new Array(3); for (var i in array) { console.log(i); } for (var v of array){ console.log(v); }
This for-in code logs nothing of course but for-of code logs 3 of undefined. [...] Are these behaviors correct ?
Well, they're certainly intentional. There's no final specification yet, so it could change.
The rationale for this is that for-of should act like the canonical for-loop over an array:
var array = new Array(3);
for (var i = 0; i < array.length; i++)
console.log(array[i]); // "undefined" 3 times
for (var v of array)
console.log(v); // "undefined" 3 times
The corresponding for-in loop is much rarer, with good reason. Note that "for-in" on an Array is underspecified (in terms of the order in which the property names are visited and what happens when properties are added or removed during iteration). The behavior we implemented for Array iterators is straightforward and easy to use, and it's easier to specify fully. See harmony:iterators.
I think Map will end up being better than Array for sparse collections.
Allen privately observed that Array forEach skips holes, matching for-in. That counts for a lot with me -- we have only a blind for(i=0;i<a.length;i++)... loop not skipping holes, but of course it wouldn't. That is weak precedent on which to build for-of.
Map may win at some point, who knows? It's not winning if one wants an array, numeric indexing, .length, the usual prototype methods.
On Jul 5, 2012, at 10:54 AM, Brendan Eich wrote:
Allen privately observed that Array forEach skips holes, matching for-in. That counts for a lot with me -- we have only a blind for(i=0;i<a.length;i++)... loop not skipping holes, but of course it wouldn't. That is weak precedent on which to build for-of.
Specifically, for consistency, I think
array.forEach((v)=>console.log(v));
and
for (let v of array) console.log(v);
should yield the same results.
[].forEach skips "holes" so the default iterator for arrays should too. All the other "Array extras" also have the skipping behavior.
Map may win at some point, who knows? It's not winning if one wants an array, numeric indexing, .length, the usual prototype methods.
We could consider also have "dense" interators available:
for (let v of array.denseValues) console.log(v);
On Thu, Jul 5, 2012 at 12:54 PM, Brendan Eich <brendan at mozilla.org> wrote:
Allen privately observed that Array forEach skips holes, matching for-in. That counts for a lot with me -- we have only a blind for(i=0;i<a.length;i++)... loop not skipping holes, but of course it wouldn't. That is weak precedent on which to build for-of.
In what sense is that precedent weak? It seems a stronger precedent than forEach on every axis; arguably it's one of the most-trodden cowpaths in programming, in any language.
for-in is a kind of anti-precedent, for array iteration.
Map may win at some point, who knows? It's not winning if one wants an array, numeric indexing, .length, the usual prototype methods.
I agree Map is somewhat beside the point. TC39 should spec what's best for developers net of everything. Even if that's matching for-in. But the purpose of for-of is to address the shortcomings of for-in; following it just for the sake of consistency would be self-defeating.
On Thu, Jul 5, 2012 at 2:09 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
Map may win at some point, who knows? It's not winning if one wants an array, numeric indexing, .length, the usual prototype methods.
We could consider also have "dense" interators available:
for (let v of array.denseValues) console.log(v);
This makes sense to me, but most arrays are meant to be dense in practice. So perhaps it makes more sense to add methods specifically for sparse arrays, making developers type more characters in the rare case rather than the common case:
for (let v of array.sparseValues()) ...;
for (let [i, v] of array.sparseItems()) ...;
Jason Orendorff wrote:
On Thu, Jul 5, 2012 at 12:54 PM, Brendan Eich<brendan at mozilla.org> wrote:
Allen privately observed that Array forEach skips holes, matching for-in. That counts for a lot with me -- we have only a blind for(i=0;i<a.length;i++)... loop not skipping holes, but of course it wouldn't. That is weak precedent on which to build for-of.
In what sense is that precedent weak? It seems a stronger precedent than forEach on every axis; arguably it's one of the most-trodden cowpaths in programming, in any language.
That's too general a claim to be self-certifying. I <3 C but in JS, lots of a.forEach(function (e) {...}) usage these days. Kids are into FP.
for-in is a kind of anti-precedent, for array iteration.
The point is for-in in addition to forEach and other Array extras skip holes. Not for-in by itself as motivation, really, just consistency of all but for-of as implemented.
Map may win at some point, who knows? It's not winning if one wants an array, numeric indexing, .length, the usual prototype methods.
I agree Map is somewhat beside the point. TC39 should spec what's best for developers net of everything. Even if that's matching for-in. But the purpose of for-of is to address the shortcomings of for-in; following it just for the sake of consistency would be self-defeating.
I think your focus is pulled by for-in too much. It happens to match the Array extras and this makes a happy consistency, but the main precedent is forEach, along with the rest of the extras which agree on skipping holes.
If there's a botch here, it is holes in arrays, or rather: arrays not being like Python lists, instead being barely-specialized objects-with-properties. But that's a done deal. From that, holes follow.
From holes, skipping or not skipping consistently must follow. So far we've been consistent over the many years: for-in, forEach, etc. all skip.
The C-style loop is blind to property existence so is really a different animal. It's true people are told not to use for-in on arrays, but that does not make them use for(;;). Instead they seem to reach for forEach. Which skips holes.
Reasoning about latent bugs due to skipping holes is perilous and wants empirical studies, some evidence, a few burning anecdotes. Got any?
Jason Orendorff wrote:
On Thu, Jul 5, 2012 at 2:09 PM, Allen Wirfs-Brock<allen at wirfs-brock.com> wrote:
Map may win at some point, who knows? It's not winning if one wants an array, numeric indexing, .length, the usual prototype methods. We could consider also have "dense" interators available:
for (let v of array.denseValues) console.log(v);
This makes sense to me, but most arrays are meant to be dense in practice. So perhaps it makes more sense to add methods specifically for sparse arrays, making developers type more characters in the rare case rather than the common case:
for (let v of array.sparseValues()) ...; for (let [i, v] of array.sparseItems()) ...;
No, this is human-hostile. Programmers don't generally make holes on purpose. You're right that accidental holes may mean bugs, but not necessarily -- especially if skipped. It's unclear and (I agree) messy, but given holes and 1JS compatibility, we can't really change the main iteration facilities used predominantly on arrays, and also used generically on all objects including arrays, to skip holes.
The JSFixed request for Object.prototype.forEach wants nothing like for(i=0;i<a.length;i++), when used on an Array instance. It must skip holes. This upholes the Array forEach (and all other extras) hole-skipping. The deck is stacked against for(;;) iteration in my view.
But to get back to your suggestion, that does not mean programmers think about sparse arrays usefully or specifically. They want generic iteration; they do not want holes to be iterated. At least, based on my experience and what I see others writing.
Brendan Eich wrote:
This upholes the Array forEach (and all other extras) hole-skipping. The deck is stacked against for(;;) iteration in my view.
LOL, "This upholds", of course.
On Thursday, July 5, 2012 at 9:18 PM, Brendan Eich wrote:
Brendan Eich wrote:
This upholes the Array forEach (and all other extras) hole-skipping. The deck is stacked against for(;;) iteration in my view.
LOL, "This upholds", of course.
I had hoped this was a clever pun :)
Currently, devs expect for-loop and while (assuming common patterns in play here) to be "the expected" way that sparse array holes are exposed -- so from the "give me what I most likely expect" perspective, I agree with the consistency wins argument: for-of should act like for
On Thu, Jul 5, 2012 at 6:30 PM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Thursday, July 5, 2012 at 9:18 PM, Brendan Eich wrote:
Brendan Eich wrote:
This upholes the Array forEach (and all other extras) hole-skipping. The deck is stacked against for(;;) iteration in my view.
LOL, "This upholds", of course.
I had hoped this was a clever pun :)
Currently, devs expect for-loop and while (assuming common patterns in play here) to be "the expected" way that sparse array holes are exposed -- so from the "give me what I most likely expect" perspective, I agree with the consistency wins argument: for-of should act like for-in
To clarify, the behaviours I'm comparing are as follows:
var i, a = [1, 2, , 4]; for ( i in a ) { console.log( i, a[i] ); }
0 1 1 2 3 4
var i, a = [1, 2, , 4]; for ( i = 0; i < a.length; i++ ) { console.log( i, a[i] ); }
0 1 1 2 2 undefined 3 4
var i = 0, a = [1, 2, , 4]; while ( i < a.length ) { console.log( i, a[i] ); i++; }
0 1 1 2 2 undefined 3 4
Where the latter 2 require an explicit check (not present) against holes. So I would assume that for-of would behave like...
var i, a = [1, 2, , 4]; for ( i of a ) { console.log( i ); }
1 2 4
The only case where I've had a problem with forEach, map and friends skipping holes is when I want a quick (to type) way to create a populated array, say I wanted to do something like
var powersOf2 = Array(16).map((item, index) => Math.pow(2, index))
But that leads me to suggest Array.create() that would be another FP
goodie, simplifies things as item
becomes irrelevant:
var powersOf2 = Array.create(16, (index) => Math.pow(2, index))
What do you think?
This being instead of the current:
var powersOf2 = []
for (var index=0; index<16; index++) { powersOf2.push(Math.pow(2, index)) }
I think it would align well with all these other array helpers.
On Thu, Jul 5, 2012 at 11:49 PM, Jussi Kalliokoski <jussi.kalliokoski at gmail.com> wrote:
The only case where I've had a problem with forEach, map and friends skipping holes is when I want a quick (to type) way to create a populated array, say I wanted to do something like
var powersOf2 = Array(16).map((item, index) => Math.pow(2, index))
But that leads me to suggest Array.create() that would be another FP goodie, simplifies things as
item
becomes irrelevant:var powersOf2 = Array.create(16, (index) => Math.pow(2, index))
What do you think?
This being instead of the current:
var powersOf2 = []
for (var index=0; index<16; index++) { powersOf2.push(Math.pow(2, index)) }
I think it would align well with all these other array helpers.
Once we have for-of and generators, making a range() generator that accomplishes the same thing is trivial, and more powerful. (Arbitrary start/end/step.)
Hey look, it's my favorite example.
var powersOf2 = Array.apply(null, new Array(32)).map(Function.prototype.call.bind(Number)).map(Math.pow.bind(null, 2))
Or as a general purpose function:
var makeDense = Function.apply.bind(Array, null); makeDense(Array(20)); makeDense([,,,,5,,,,])
On Fri, Jul 6, 2012 at 10:28 AM, Tab Atkins Jr. <jackalmage at gmail.com>wrote:
On Thu, Jul 5, 2012 at 11:49 PM, Jussi Kalliokoski <jussi.kalliokoski at gmail.com> wrote:
The only case where I've had a problem with forEach, map and friends skipping holes is when I want a quick (to type) way to create a populated array, say I wanted to do something like
var powersOf2 = Array(16).map((item, index) => Math.pow(2, index))
But that leads me to suggest Array.create() that would be another FP goodie, simplifies things as
item
becomes irrelevant:var powersOf2 = Array.create(16, (index) => Math.pow(2, index))
What do you think?
This being instead of the current:
var powersOf2 = []
for (var index=0; index<16; index++) { powersOf2.push(Math.pow(2, index)) }
I think it would align well with all these other array helpers.
Once we have for-of and generators, making a range() generator that accomplishes the same thing is trivial, and more powerful. (Arbitrary start/end/step.)
~TJ
Yes, maybe it isn't a worthy addition as is.
Hahah, sweet, but it doesn't make it faster to type needed at all! Quite the opposite even, it requires more thinking. ;)
But maybe I'll just add yet another function to my boilerplate:
function createArray (l, cb) { return Array.apply(null, new Array(l)) .map(Function.call.bind(Number)) .map(cb) }
On Fri, Jul 6, 2012 at 3:35 AM, Jussi Kalliokoski <jussi.kalliokoski at gmail.com> wrote:
Hahah, sweet, but it doesn't make it faster to type needed at all! Quite the opposite even, it requires more thinking. ;)
But maybe I'll just add yet another function to my boilerplate:
function createArray (l, cb) { return Array.apply(null, new Array(l)) .map(Function.call.bind(Number)) .map(cb) }
If you're hiding it behind a function abstraction, there's absolutely no need to carry over that obtuse functional paradigm. Just put a for-loop in that function and be efficient.
Firefox 15 or later can use for-of statement. I have a question about this.
This for-in code logs nothing of course but for-of code logs 3 of undefined. And also following code is same.
Are these behaviors correct ?