Re: Day 2 meeting notes
for (k in lst) { ... } // key for (v, in lst) { ... } // value - trailing comma for (v,k in lst) { ...} // value, key And/Or using some symbol to indicate an iterator: for ([k,v] in @lst) {...} // with destructuring //@ = sugar for the method call to return the iterator. let it = @lst; How much leeway is there for adding new keywords? -- ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Aug 1, 2010, at 10:55 AM, Kevin Curtis wrote: for (k in lst) { ... } // key for (v, in lst) { ... } // value - trailing comma for (v,k in lst) { ...} // value, key We already have destructuring, which looks similar but with less ambiguity or lightness (square or curly brackets, helpful to avoid typos and misreadings by code reviewers). Typically key comes before value when destructuring items (see Python and other languages). And/Or using some symbol to indicate an iterator: for ([k,v] in @lst) {...} // with destructuring //@ = sugar for the method call to return the iterator. let it = @lst; And if the @ operand is not a proxy with an iterate trap in its handler? If the answer is to throw an error for want of an iterable, then some (allegedly, see shallowCopy) generic code will work well, while other would-be generic code will not. Adding a way to opt into iteration instead of enumeration only helps if enumeration is right -- but enumeration is broken in several ways, so the long-term trade-offs are not so clearly in favor of keeping it under-specified yet sacrosanct. There are three parties (at least) involved in the evolving system: the for-in loop author; JS hackers who might pass in any native, host, and proxy object; and Ecma TC39 / the browser vendors and other JS implementors. 1. The for-in loop author may know exactly what objects could ever be on the right of 'in', but let's assume not. The really challenging case is where the loop is in a reusable function taking the object as a parameter. Such an author has a hard time defending against host objects even today, never mind against proxies tomorrow. Again, this applies to . and [] as well as for-in -- all can give unusual answers down the road, although competition should (knock on wood) keep the insane objects from proliferating. 2. The JS hackers who might pass in unexpected objects may not simply err or do evil. Often they make clever use of the for-in-based library function, and would like to smooth other any differences between implementations in how for-in works. These users want more meta-programmability. 3. The TC39 committee can try to bikeshed syntax for new fixed points of semantics, but as I've written, we are the least likely to get it right and make it stick. Which of the three parties is most likely to provide the right object for a given for-in-based library function? My money is on party #2, the JS hackers at large and in the future. BTW @foo is already in use in ECMA-357 (E4X). How much leeway is there for adding new keywords? We can add new keywords, especially contextual ones (although not in verbose ways that users will hate). It is hard to prove new reserved identifiers won't impose an oversized migration tax compared to the alleged benefit. No one wants to add if there's a good way not to add (e.g., via a module or other library API) that has as good or better usability than a keyword. Hence our rejection of 'each' in favor of library (module) based standard iterators. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Aug 1, 2010, at 11:29 AM, Brendan Eich wrote: And if the @ operand is not a proxy with an iterate trap in its handler? If the answer is to throw an error for want of an iterable, then some (allegedly, see shallowCopy) generic code will work well, while other would-be generic code will not. I should have written work well, in the sense of not copying a potentially large number of iterated values, but getting an exception instead usually means a hard stop. Existing code would not defend with try-catch and try some fallback logic. Really, generic code is hard to write without more meta-programming control, both inspection and intercession. That's what we are aiming at with proxies (including iteration). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 9:06 PM, felix wrote: it seems odd to me that if 'a' is an iterator, it will iterate over the iterator's value stream instead of the iterator's properties, unless you define the two to be identical, which would be strange. eg, if you have an input stream iterator f, would f.hasOwnProperty('bacon') work or not? Sorry, I misspoke -- without having specified keys fully (we'll get to that task on the wiki), let me try to make up for it by implementing some variations on keys using proxies, in TraceMonkey. The object returned by keys, called a in your revision, is a proxy. Let's call it p to avoid confusion. Let's say that the handler for proxy p has no traps emulating properties, only an iterate trap. The iterate trap is a derived trap (it is optional; if missing, the default enumerate behavior is supplied by the proxy support in the runtime). The for-in loop calls the iterate trap on p, which returns an iterator object. This iterator, call it it, is an object with a 'next' property whose value is a function taking no arguments and returning the next value in the iteration. This iterator object it is not necessarily a proxy -- it could be, but it need not be. Ok, so the proxy denoted by p returned by keys(o) has been subject to for-in by being on the right of 'in', so its iterate trap has been called, and now the for-in runtime has the returned iterator it. The for-in loop runtime therefore calls it.next() implicitly at the top of each iteration of the loop body. If it.next() throws StopIteration, the for-in loop terminates normally (any other exception thrown by the body terminates the loop and is of course propagated). Here's a keys implementation in bleeding edge SpiderMonkey (http://hg.mozilla.org/tracemonkey): js function keys(o) { const handler = { iterate: function () { for (let i in o) yield i; } }; return Proxy.create(handler); } js let o = {a:1, b:2, c:3}; js for (let k in keys(o)) print(k); a b c js (The strawman at http://wiki.ecmascript.org/doku.php?id=strawman:iterators provides a convenience Iterator.create function to make a proxy given just the handler iterate trap function. Using it, we could simplify keys to just function keys(o) { return Iterator.create(function () { for (let i in o) yield i; }); } but the full expansion above, using Proxy.create, is not much longer.) What if you wanted a proxy whose handler uses other traps in addition to iterate to emulate enumerable properties in full? That is doable too, thanks to the power of proxies: js function keys2(o) { const handler = { iterate: function () { for (let i in o) yield i; }, getPropertyDescriptor: function (name) { if (o.hasOwnProperty(name)) return Object.getOwnPropertyDescriptor(o, name); return undefined; }, getOwnPropertyDescriptor: function (name) { return Object.getOwnPropertyDescriptor(o, name); }, defineProperty: function (name, pd) { return Object.defineProperty(o, name, pd); }, getOwnPropertyNames: function () { return Object.getOwnPropertyNames(o); }, delete: function (name) { return delete o[name]; }, /*enumerate:*/ fix: function () { return Object.freeze(o); } }; return Proxy.create(handler); } js let o = {a:1, b:2, c:3}; js let p = keys2(o); js for (let k in p) print(k); a b c js print(p.a); 1 js print(p.b); 2 js print(p.c); 3 js delete p.a; true js for (let k in p) print(k); b c There's a workaround in this handler's getPropertyDescriptor trap, because we have not yet implemented Object.getPropertyDescriptor (it was ratified only this week at the TC39 meeting). I took the easy way out and instead of walking o's prototype chain, being careful to shadow, I handle only own properties (let's assume that Object.prototype has not been monkey-patched ;-). This example shows how delete p.a followed by another for-in loop over p reveals that 'a' has been deleted. The proxy is implementing native object semantics. Of course it is possible to implement inconsistent object semantics with proxies -- this is true of host objects in general (especially prior to ES5, but even in ES5 -- see ES5 8.6.2, the paragraphs toward the end -- and of course implementations may defy the standard, or simply be buggy or old). Proxies are thus an in-language facility for implementing host objects. This is an evolutionary path toward cleaning up host objects in various DOM implementations. TC39 thinks this is important; it may save us from the worst of the browser DOMs (the IE DOM can claim that title, but all browsers have quirky host objects in my experience). I also implemented the iterate trap, which is lazy, in preference to implementing the commented-out enumerate trap, which is eager and used only to
Re: Day 2 meeting notes
On 31.07.2010 1:37, Brendan Eich wrote: On Jul 30, 2010, at 1:38 PM, Dmitry A. Soshnikov wrote: Another thing to mention regarding array comprehensions is /pattern matching/ (in less common, but related to JS, case -- /destructing assignment/). Currently, it's implemented in JS 1.7 in simple for/each-in loops: for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) { alert(x); // 10, 20, 30 } Yes, it can be useful to pattern match {a: x} extracting a value of the a property into the x variable. Array comprehensions of JS 1.7 in contrast with a loop do not have such sugar: let a = [x for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) if (x 2)]; // SyntaxError You don't need each to make that work in JS1.7: js let a = [x.a for each (x in [{a: 10}, {a: 20}, {a: 30}]) if (x.a 2)]; js a [10, 20, 30] Notice that you can use each in JS1.7 after for (E4X was in JS1.6 and up). But you are quite right that we did not allow the same left-hand sides of 'in' in comprehensions as we did in equivalent loops: js let a = []; for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) if (x 2) a[a.length] = x; a [10, 20, 30] That is just a flaw in JS1.7, possibly even not a design flaw but an implementation bug (I honestly don't remember). Yeah, I just mention exactly an element's structure filtering, not touching _real_ filtering within already /structure filtered/ elements. From the other hand, maybe current JS destructuring (but not irrefutable match) even better -- in respect that it's more flexible -- structure filtering can be moved to the if-filter section (i.e. technically it's easy to achieve, I just meant some sugar, but at the same time I thought, this sugar (irrefutable match) is not so flexible for JS): // only first element will be in a array let a = [x for each (x in [{a: 10, b: 20}, {a: 1, b: 20}, skip, {a: 30}]) if ( // filtering needed structure first (typeof x == object) (a in x) (b in x) // and then already real filters within needed structure elements x.a 1 ) ]; It's more flexible, because lets (without exact structure filtering) get {a: x} from {a: 10, b: 20} and {a: 30}. For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. So, they (keys, values, properties) result iterators. Are the keys(o) is the same, but just more explicit version of a simple iteration over the `o' object? for (k in o) and for (k in keys(o)) -- are they the same (excluding implementation, e.g. lazy evaluation possibly or sort of, only the end result is interesting)? If `keys`, `values` and `properties` simple functions are not global functions, does a user should always import them from basic iteration module (i.e. if you want this sugar -- then import, if not -- use old for-in over properties)? Or will they be imported automatically before evaluation every module/context? If the later, then they are (semantically) global functions. Actually, for programmers familiar with iterators, the following case shouldn't cause ambiguity: for (k in o) // default -- always over keys for (k in keys(o)) // the same, explicitly mention keys let value = values(o); for (v in values) // over values, because a programmer see that he iterates over the special iterator object etc. It seems convenient. For not so familiar -- yeah, it may cause some ambiguities when we can't say exactly looking on just one line -- for (z in o) -- over what does it iterate (in case if `o' was set something else of course) -- keys, values, items? So the programmer should back and look what is `o' -- in contrast with `for each' or `for (values of object)', etc. additional syntax keywords. Also using pattern matching, it's useful sometimes to filter needed values of an array in the pattern matching parameter itself, but not using filter section (actually, it's hard to use filter section to filter exactly values which are not of the needed structure). For example (code on Erlang with its list comprehensions): List = [{1,2}, skip, {3,4}], FilteredList = [X + Y || {X, Y}- List]. Result is: [3, 7]. Pattern matching filtered atom `skip' and took only {X, Y} structures (1st and 3rd elements in list) extracting values into the X and Y variables. This is useful feature, I was needed it on practice. In contrast lists:map + lists:filter (that's desugared list comprehensions) cannot handle this case, because for the `skip' atom will be `bad match' error and we can't map a list the same elegant as with
Re: Day 2 meeting notes
On 31.07.2010 15:45, Dmitry A. Soshnikov wrote: for (k in o) and for (k in keys(o)) -- are they the same (excluding implementation, e.g. lazy evaluation possibly or sort of, only the end result is interesting)? Sorry, already have read later explanation in this thread. Dmitry. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On 7/29/10 16:55, Oliver Hunt wrote: On Jul 29, 2010, at 2:47 PM, Waldemar Horwat wrote: Comprehensions: To be useful, need to have a bunch of basic helpers such as range(), properties(), values(), etc. with short names that can be used to construct comprehensions. These will be added to the proposal. Would like to be able to interleave for and if clauses: [x*y for (x in range(...)) if (isprime(x)) for (y in range(x+1...)) if (isprime(y))] I keep seeing code like this, I simply don't see it as viable to have for (.. in ...) sometimes enumerate property names, and some times enumerate keys, it seems like it could be both confusing and error prone. esp. given the simplest example: [x for (value in [1,2,3])] you would not get the desired behaviour, unless in comprehensions for(in) behaves differently from everywhere else. It seems far better to just define a distinct syntax for enumerating values of an object. actionscript 3 has for (key in a) {} for each (val in a) {} ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: Day 2 meeting notes
On 11:59 AM, Oliver Hunt wrote: I keep seeing code like this, I simply don't see it as viable to have for (.. in ...) sometimes enumerate property names, and some times enumerate keys, it seems like it could be both confusing and error prone. esp. given the simplest example: [x for (value in [1,2,3])] you would not get the desired behaviour, unless in comprehensions for(in) behaves differently from everywhere else. It seems far better to just define a distinct syntax for enumerating values of an object. I agree. We talked about swapping out the preposition, so for..in produces keys, and for..of or for..from produces values. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On 30.07.2010 23:06, felix wrote: On 7/29/10 16:55, Oliver Hunt wrote: On Jul 29, 2010, at 2:47 PM, Waldemar Horwat wrote: Comprehensions: To be useful, need to have a bunch of basic helpers such as range(), properties(), values(), etc. with short names that can be used to construct comprehensions. These will be added to the proposal. Would like to be able to interleave for and if clauses: [x*y for (x in range(...)) if (isprime(x)) for (y in range(x+1...)) if (isprime(y))] I keep seeing code like this, I simply don't see it as viable to have for (.. in ...) sometimes enumerate property names, and some times enumerate keys, it seems like it could be both confusing and error prone. esp. given the simplest example: [x for (value in [1,2,3])] you would not get the desired behaviour, unless in comprehensions for(in) behaves differently from everywhere else. It seems far better to just define a distinct syntax for enumerating values of an object. actionscript 3 has for (key in a) {} for each (val in a) {} Not only ActionScript, but JavaScript too (e.g. SpiderMonkey 1.7). Another thing to mention regarding array comprehensions is /pattern matching/ (in less common, but related to JS, case -- /destructing assignment/). Currently, it's implemented in JS 1.7 in simple for/each-in loops: for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) { alert(x); // 10, 20, 30 } Yes, it can be useful to pattern match {a: x} extracting a value of the a property into the x variable. Array comprehensions of JS 1.7 in contrast with a loop do not have such sugar: let a = [x for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) if (x 2)]; // SyntaxError alert(a); Also using pattern matching, it's useful sometimes to filter needed values of an array in the pattern matching parameter itself, but not using filter section (actually, it's hard to use filter section to filter exactly values which are not of the needed structure). For example (code on Erlang with its list comprehensions): List = [{1,2}, skip, {3,4}], FilteredList = [X + Y || {X, Y} - List]. Result is: [3, 7]. Pattern matching filtered atom `skip' and took only {X, Y} structures (1st and 3rd elements in list) extracting values into the X and Y variables. This is useful feature, I was needed it on practice. In contrast lists:map + lists:filter (that's desugared list comprehensions) cannot handle this case, because for the `skip' atom will be `bad match' error and we can't map a list the same elegant as with list comprehension: List = [{1,2}, skip, {3,4}], lists:map(fun({X, Y}) - X + Y end, List). % bad_match error for the second element - `skip' atom Syntactically JavaScript has similar construction, but semantically result differs: for each ({a: x, b: y} in [{a: 10, b: 20}, skip, {a: 30, b: 40}]) { alert(x, y); // 10, 20 | undefined, undefined | 30, 40 } String skip isn't pattern matched, but the object is created with `undefined' values for x and y. And again for array comprehensions this shows SyntaxError (that, for consistency with for/each-in loop should not). Dmitry. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 12:06 PM, felix wrote: actionscript 3 has for (key in a) {} for each (val in a) {} This is from ECMA-357, E4X, and it is in SpiderMonkey and Rhino too -- it's not original to AS3. It's also something we agreed (crock too, IIRC) was too vague: each does not scream value, not key. But beyond that, iteration in general is not necessarily visiting properties in an object, and then you have to choose an obscure keyword or preposition to choose value or key. Iteration is a stream of values, no properties needed at all. In that light, and in light of Mathematics as well as Python and JS1.7+, it is hard to beat for ... in For all elements in set, for all items in list, for all [key, value] pairs in dict, etc. This might suggest using all, but that too is vague, and it also may suggest eagerness or completeness. But iteration is needed in JS precisely for laziness. Infinite sequences or streams where the consumer decides when to stop asking for the next value are currently underserved use-cases. This point came up when reviewing http://wiki.ecmascript.org/doku.php?id=harmony:proxies Waldemar raised objections against the current Proxy API to proxy objects with a large number of properties. The problematic traps are fix and enumerate. W.r.t enumerate, a proxy could return a proxy for an array. Additionally, enumerate should be modified as soon as there is a solid proposal for generators/iterators. Proxies for large objects could resist being fixed. This solution is satisfactory as long as no part of the spec depends on an object being non-extensible/sealed/frozen. Of course this does not say what the syntax for a meta-programmable iteration construct should be, but laziness suggests all is not precisely on target. /be___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 1:38 PM, Dmitry A. Soshnikov wrote: Another thing to mention regarding array comprehensions is /pattern matching/ (in less common, but related to JS, case -- /destructing assignment/). Currently, it's implemented in JS 1.7 in simple for/each-in loops: for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) { alert(x); // 10, 20, 30 } Yes, it can be useful to pattern match {a: x} extracting a value of the a property into the x variable. Array comprehensions of JS 1.7 in contrast with a loop do not have such sugar: let a = [x for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) if (x 2)]; // SyntaxError You don't need each to make that work in JS1.7: js let a = [x.a for each (x in [{a: 10}, {a: 20}, {a: 30}]) if (x.a 2)]; js a [10, 20, 30] Notice that you can use each in JS1.7 after for (E4X was in JS1.6 and up). But you are quite right that we did not allow the same left-hand sides of 'in' in comprehensions as we did in equivalent loops: js let a = []; for each ({a: x} in [{a: 10}, {a: 20}, {a: 30}]) if (x 2) a[a.length] = x; a [10, 20, 30] That is just a flaw in JS1.7, possibly even not a design flaw but an implementation bug (I honestly don't remember). For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. Also using pattern matching, it's useful sometimes to filter needed values of an array in the pattern matching parameter itself, but not using filter section (actually, it's hard to use filter section to filter exactly values which are not of the needed structure). For example (code on Erlang with its list comprehensions): List = [{1,2}, skip, {3,4}], FilteredList = [X + Y || {X, Y} - List]. Result is: [3, 7]. Pattern matching filtered atom `skip' and took only {X, Y} structures (1st and 3rd elements in list) extracting values into the X and Y variables. This is useful feature, I was needed it on practice. In contrast lists:map + lists:filter (that's desugared list comprehensions) cannot handle this case, because for the `skip' atom will be `bad match' error and we can't map a list the same elegant as with list comprehension: List = [{1,2}, skip, {3,4}], lists:map(fun({X, Y}) - X + Y end, List). % bad_match error for the second element - `skip' atom Syntactically JavaScript has similar construction, but semantically result differs: for each ({a: x, b: y} in [{a: 10, b: 20}, skip, {a: 30, b: 40}]) { alert(x, y); // 10, 20 | undefined, undefined | 30, 40 } Syntax aside, destructuring in JS is not irrefutable match. It is simply shorthand for assigning (and declaring) variables whose names are supplied in the value position in array and object initialisers, where the assigned values for these variables come from property values named by the keys in the corresponding key position. Failure in the sense of pulling undefined out of a non-existing property *is* an option: js let [x, y] = {not_an_array: 42}; js x js y and of course if you dig deeper, you can get failure dereferencing undefined: js let [x, [y, z]] = {nor_here: 99}; typein:20: TypeError: (void 0) is undefined (SpiderMonkey uses (void 0) not undefined since prior to ES5, the global property named undefined was writable and could be spoofed.) String skip isn't pattern matched, but the object is created with `undefined' values for x and y. And again for array comprehensions this shows SyntaxError (that, for consistency with for/each-in loop should not). I agree that some fairly common JS use-cases want irrefutable match. Dave Herman pointed out how the SpiderMonkey and Rhino extended catch syntax, guarded catches, is a kind of matching: try { throw random_exception_generator(); } catch (e if typeof e == number) { ... } catch (e if typeof e == string) { ... } catch { // default case, if you forget it e will be rethrown for you } This was proposed for ES3 but not accepted. It has the advantage cited by the comment in the default catch clause. Perhaps there's a generalization of such guards, which could re-use the initialiser-derived pattern syntax from destructuring, and which would provide irrefutable match as a primitive with good compositionality. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On 7/30/10 14:10, Brendan Eich wrote: On Jul 30, 2010, at 12:06 PM, felix wrote: actionscript 3 has for (key in a) {} for each (val in a) {} This is from ECMA-357, E4X, and it is in SpiderMonkey and Rhino too -- it's not original to AS3. It's also something we agreed (crock too, IIRC) was too vague: each does not scream value, not key. oh, right, forgot about e4x. Of course this does not say what the syntax for a meta-programmable iteration construct should be, but laziness suggests all is not precisely on target. so why not make it for each? for-each iterates over a stream, and in the case of arrays the stream is the array values. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On 7/30/10 14:37, Brendan Eich wrote: For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. I'm wary of that because this looks to me confusing: a = keys(o); for (k in a) ... or is keys(o) special syntax that only works within a for() statement? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 2:37 PM, Brendan Eich wrote: For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. In case it's unclear, these keys, values, etc. iterator factories are just functions. Of course their names would not be reserved identifiers, and they wouldn't be global functions. The reader should assume they've been imported from a standard module. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 2:43 PM, felix wrote: Of course this does not say what the syntax for a meta-programmable iteration construct should be, but laziness suggests all is not precisely on target. so why not make it for each? for-each iterates over a stream, and in the case of arrays the stream is the array values. Reasons not to add each: 1. Avoid another contextual keyword (few implemented E4X). 2. Avoid confusion over what each means. 3. Even assuming we added each based on E4X, the objection that |for each (x in y)| loops would need to cope with y being a proxy that customizes value-iteration exists. Point 3 is Waldemar's objection w.r.t. |for-in| becoming customizable (as it is in JS1.7 and Python). It applies equally to |for-each-in| given the E4X precedent of non-proxy object on right of 'in' being enumerated (iterated over its enumerable keys), and its enumerable keys' values returned. One can bikeshed for days on all sorts of vague and wrongly general words such as each. Instead, focusing on the right side of in, providing an explicit iterator-factory call, seems strictly better. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 2:47 PM, felix wrote: On 7/30/10 14:37, Brendan Eich wrote: For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. I'm wary of that because this looks to me confusing: a = keys(o); for (k in a) ... The confusion here seems to be assuming that |a| is an Array instance. It's not. It is an iterator, so you'll get the keys (property names) found in o -- you won't get 0, 1, ... a.length-1. To avoid this confusion you can add new syntax (|for each| or whatever, doesn't matter). I've argued in recent posts that it is better from a global and long-term point of view to reform for-in after Python, than to condemn it and grow the language with new and generally more verbose, yet similar, syntax. or is keys(o) special syntax that only works within a for() statement? No. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 2:56 PM, Brendan Eich wrote: On Jul 30, 2010, at 2:47 PM, felix wrote: On 7/30/10 14:37, Brendan Eich wrote: For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. I'm wary of that because this looks to me confusing: a = keys(o); for (k in a) ... The confusion here seems to be assuming that |a| is an Array instance. It's not. It is an iterator, so you'll get the keys (property names) found in o -- you won't get 0, 1, ... a.length-1. To avoid this confusion you can add new syntax (|for each| or whatever, doesn't matter). I've argued in recent posts that it is better from a global and long-term point of view to reform for-in after Python, than to condemn it and grow the language with new and generally more verbose, yet similar, syntax. It annoys me that this doesn't leave a convenient way to iterate arrays, for (... in someArray) will forever do the braindead thing. If we look to pythonic behaviour we see that python's for..in syntax has always iterated arrays by value rather than index. By overloading for(in) we are effectively saying that there will never be a simple way to iterate arrays by value directly, because no one can even extend the builtin array type be have a generator for iteration because doing so would be too fragile. --Oliver ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 2:53 PM, Brendan Eich wrote: On Jul 30, 2010, at 2:43 PM, felix wrote: Of course this does not say what the syntax for a meta-programmable iteration construct should be, but laziness suggests all is not precisely on target. so why not make it for each? for-each iterates over a stream, and in the case of arrays the stream is the array values. Reasons not to add each: 1. Avoid another contextual keyword (few implemented E4X). 2. Avoid confusion over what each means. 3. Even assuming we added each based on E4X, the objection that |for each (x in y)| loops would need to cope with y being a proxy that customizes value-iteration exists. Point 3 is Waldemar's objection w.r.t. |for-in| becoming customizable (as it is in JS1.7 and Python). It applies equally to |for-each-in| given the E4X precedent of non-proxy object on right of 'in' being enumerated (iterated over its enumerable keys), and its enumerable keys' values returned. One can bikeshed for days on all sorts of vague and wrongly general words such as each. Instead, focusing on the right side of in, providing an explicit iterator-factory call, seems strictly better. I think that while for each ... in or for all ... in is a potentially confusing distinction vs. for ... in, it still seems less potentially confusing to me than having no distinction at all. Another entry for the bikeshedding pile: for values ... in. That definitely screams values, not keys. Regards, Maciej ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 3:02 PM, Oliver Hunt wrote: On Jul 30, 2010, at 2:56 PM, Brendan Eich wrote: To avoid this confusion you can add new syntax (|for each| or whatever, doesn't matter). I've argued in recent posts that it is better from a global and long-term point of view to reform for-in after Python, than to condemn it and grow the language with new and generally more verbose, yet similar, syntax. It annoys me that this doesn't leave a convenient way to iterate arrays, for (... in someArray) will forever do the braindead thing. Me too, and I've seen people (myself included) make the mistake of writing for (i in [0,1,1,2,3,5,8,13]) ... wanting the values not the keys. If we look to pythonic behaviour we see that python's for..in syntax has always iterated arrays by value rather than index. Indeed. By overloading for(in) we are effectively saying that there will never be a simple way to iterate arrays by value directly, because no one can even extend the builtin array type be have a generator for iteration because doing so would be too fragile. This never be a simple way is not true in JS1.7+: js Array.prototype.__iterator__ = function () { for (let i = 0; i this.length; i++) yield this[i]; }; (function () {for (let i = 0; i this.length; i++) {yield this[i];}}) js for (v in [3,4,5]) print(v) 3 4 5 The unstratified, ugly-named __iterator__ meta-method is the getter or factory for finding or creating an appropriate iterator. I use a generator, since it is the simplest way of writing such a factory. With Harmony we stratify such meta-programming using proxies, so you can't use the prototype chain to find the iterate handler-trap -- you must directly reference a Proxy instance after 'in' in the for-in head. Could we fix Harmony to iterate values not keys for Arrays, by fiat, and say opt-in versioning, pay this migration tax, it is worth it? I don't think so, but we can discuss. Perhaps there's a modular way to allow one-time, explicit opting into a mode where for-in iterates array values, not keys. The obvious criticism is modes are bad, don't add more knobs to the language. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 3:35 PM, Brendan Eich wrote: On Jul 30, 2010, at 3:02 PM, Oliver Hunt wrote: On Jul 30, 2010, at 2:56 PM, Brendan Eich wrote: To avoid this confusion you can add new syntax (|for each| or whatever, doesn't matter). I've argued in recent posts that it is better from a global and long-term point of view to reform for-in after Python, than to condemn it and grow the language with new and generally more verbose, yet similar, syntax. It annoys me that this doesn't leave a convenient way to iterate arrays, for (... in someArray) will forever do the braindead thing. Me too, and I've seen people (myself included) make the mistake of writing for (i in [0,1,1,2,3,5,8,13]) ... wanting the values not the keys. If we look to pythonic behaviour we see that python's for..in syntax has always iterated arrays by value rather than index. Indeed. By overloading for(in) we are effectively saying that there will never be a simple way to iterate arrays by value directly, because no one can even extend the builtin array type be have a generator for iteration because doing so would be too fragile. This never be a simple way is not true in JS1.7+: js Array.prototype.__iterator__ = function () { for (let i = 0; i this.length; i++) yield this[i]; }; (function () {for (let i = 0; i this.length; i++) {yield this[i];}}) js for (v in [3,4,5]) print(v) 3 4 5 The unstratified, ugly-named __iterator__ meta-method is the getter or factory for finding or creating an appropriate iterator. I use a generator, since it is the simplest way of writing such a factory. I recognised that was possible -- the problem i was saying is that you can't do that due to it polluting the global array prototype in away that effects language semantics I'll need to read the spec behaviour more carefully before i'm willing to go too deeply down that rabbit hole though. --Oliver ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
-- Forwarded message -- From: Douglas Crockford doug...@crockford.com To: Oliver Hunt oli...@apple.com Date: Fri, 30 Jul 2010 13:09:33 -0700 Subject: Re: Re: Day 2 meeting notes On 11:59 AM, Oliver Hunt wrote: I keep seeing code like this, I simply don't see it as viable to have for (.. in ...) sometimes enumerate property names, and some times enumerate keys, it seems like it could be both confusing and error prone. esp. given the simplest example: [x for (value in [1,2,3])] you would not get the desired behaviour, unless in comprehensions for(in) behaves differently from everywhere else. It seems far better to just define a distinct syntax for enumerating values of an object. I agree. We talked about swapping out the preposition, so for..in produces keys, and for..of or for..from produces values. What about using 'for .. vin' - i.e. value-in for value enumeration And adding a redundant 'for .. kin' - i.e. key-in for key enumeration (for those who prefer explicitness over using the ambiguously named 'for .. in' which would retain its original key iteration behavior)? Or are 'vin' and 'kin' too nonsensical to gain any acceptance? regards, Faisal Vali ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 3:49 PM, Oliver Hunt wrote: By overloading for(in) we are effectively saying that there will never be a simple way to iterate arrays by value directly, because no one can even extend the builtin array type be have a generator for iteration because doing so would be too fragile. This never be a simple way is not true in JS1.7+: js Array.prototype.__iterator__ = function () { for (let i = 0; i this.length; i++) yield this[i]; }; (function () {for (let i = 0; i this.length; i++) {yield this[i];}}) js for (v in [3,4,5]) print(v) 3 4 5 The unstratified, ugly-named __iterator__ meta-method is the getter or factory for finding or creating an appropriate iterator. I use a generator, since it is the simplest way of writing such a factory. I recognised that was possible -- the problem i was saying is that you can't do that due to it polluting the global array prototype in away that effects language semantics Yeah, and it was hardly a simple way as I wrote it, but these little headaches are fixable: js Object.defineProperty(Array.prototype, '__iterator__', {value: function () { for (let i = 0; i this.length; i++) yield this[i]; }}); [] js for (v in [3,4,5])print(v) 3 4 5 And put the Object.defineProperty call into some init-time code in JQuery ;-). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 30, 2010, at 3:53 PM, Faisal Vali wrote: -- Forwarded message -- From: Douglas Crockford doug...@crockford.com To: Oliver Hunt oli...@apple.com Date: Fri, 30 Jul 2010 13:09:33 -0700 Subject: Re: Re: Day 2 meeting notes On 11:59 AM, Oliver Hunt wrote: I keep seeing code like this, I simply don't see it as viable to have for (.. in ...) sometimes enumerate property names, and some times enumerate keys, it seems like it could be both confusing and error prone. esp. given the simplest example: [x for (value in [1,2,3])] you would not get the desired behaviour, unless in comprehensions for(in) behaves differently from everywhere else. It seems far better to just define a distinct syntax for enumerating values of an object. I agree. We talked about swapping out the preposition, so for..in produces keys, and for..of or for..from produces values. What about using 'for .. vin' - i.e. value-in for value enumeration And adding a redundant 'for .. kin' - i.e. key-in for key enumeration (for those who prefer explicitness over using the ambiguously named 'for .. in' which would retain its original key iteration behavior)? Or are 'vin' and 'kin' too nonsensical to gain any acceptance? Yes, they're too nonsensical. Sorry, but you asked (and answered ;-). Let's avoid bikeshedding just yet. It's really not going to resolve any usability concerns, and the prior question of whether we should design for the longer term and the bigger picture, rather than just adding more syntax to mitigate the concern about old for-in code running an iterator unexpectedly, should be addressed first (if possible). There is a usability problem, I think everyone agrees. It has several aspects: 1. for-in is underspecified and not interoperably implemented, yet it is useful and used in spite of this -- its syntax is sweet, and (at least in small-world / my own code settings), reliable if used with some care. 2. for-in on arrays enumerates keys, just as with other objects, but this is often not what users want or expect. 3. for-in walks up the prototype chain. This is independent of 1 and 2, in the sense that even if we specify enumeration interoperably and engines do it, and even with ES5's Object.defineProperty to control enumerability, often enough users just Do Not Want. With Harmony building on ES5 strict mode by default and inevitably adding new syntax, therefore requiring opt-in versioning, we hope to fix at least (1), which may impose a migration tax on some web content that works cross-browser today. Here's some detail on how: We experimented in Firefox 4 nightlies with a pure snapshot model, where at the start of the for-in loop, the direct object and its prototypes are enumerated, with shadowing, and the property names saved. We left out suppression of names deleted after the loop starts but before the loop would visit the name in the snapshot -- we let those names be visited. This broke too much, so we added delete suppression, but only for the direct object. This seems web-compatbile enough, pending new negative results. TC39 favored the snapshot model at the May meeting. This week's meeting was where we Mozillans relayed the need for delete suppression, at least for the direct object. The use-case is the iterate a worklist one, but not with adding to the worklist, only deleting. It's not formally nice, but it is done (e.g. event listeners in library-created listener arrays or maps). We do not yet propose to standardize snapshot-with-direct-object-only-delete-suppression, BTW. I suspect we can't afford the migration tax imposed by fixing (2) and (3) above, especially (2). It's hard to know without running the experiment at scale -- and then it's hard to change course. Nevertheless, it is conceivable that one day, for-in will be significantly more usable under a future Edition. I believe that making for-in meta-programmable increases the odds of that day arriving, without in practice creating proxy vs. non-proxy enumeration confusion, because it lets JS authors and not just TC39 (with a few ugly new and hardcoded contextual keywords) add their good ideas and run local experiments, using iterators provided in the future Edition or by their own libraries. This worked for Python. Why not for JS? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Sat, Jul 31, 2010 at 12:06 AM, felix feli...@gmail.com wrote: On 7/30/10 14:56, Brendan Eich wrote: On Jul 30, 2010, at 2:47 PM, felix wrote: On 7/30/10 14:37, Brendan Eich wrote: For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. I'm wary of that because this looks to me confusing: a = keys(o); for (k in a) ... The confusion here seems to be assuming that |a| is an Array instance. It's not. It is an iterator, so you'll get the keys (property names) found in o -- you won't get 0, 1, ... a.length-1. To avoid this confusion you can add new syntax (|for each| or whatever, doesn't matter). I've argued in recent posts that it is better from a global and long-term point of view to reform for-in after Python, than to condemn it and grow the language with new and generally more verbose, yet similar, syntax. I should have used a letter other than 'a'. my expectation from current javascript is that 'a' is an object, and that 'for (k in a)' will iterate over the object's properties. That's a fair assumption given the current state of the language, but assumptions will change with time. Perhaps if you named a something like aIter (wow, this example makes for a good argument for the snakecase a_iter), you're expectations would be re-framed. I think the common usage pattern will be using these iteration protocols at the point of consumption, as with the examples Brendan's given -- and in this case they're effectively anonymous and naming is a non-issue. You could expect functions like keys(a), values(a) -- or whatever protocol functions your library-of-choice provides -- to do *just *what they say. In this light I don't believe an iterator's for..in behavior is surprising at all, even w/ collective our es3 baggage. it seems odd to me that if 'a' is an iterator, it will iterate over the iterator's value stream instead of the iterator's properties, unless you define the two to be identical, which would be strange. eg, if you have an input stream iterator f, would f.hasOwnProperty('bacon') work or not? But they're different objects entirely. If a's a values iterator it's not iterating over it's *own* value stream -- it's iterating over some other object's value stream. Thus, f.hasOwnProperty('bacon') shouldn't work (unless you've bacon'd your iterator, of course, or done some magic with proxy objects). So if f is iterating over an object's value stream then you have a reference to the underlying object if you want to do anything like hasOwnProperty. yes, I'd like generalized iterators like python, so this is not really an argument. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On 7/30/10 21:33, Dean Landolt wrote: On Sat, Jul 31, 2010 at 12:06 AM, felix feli...@gmail.com mailto:feli...@gmail.com wrote: On 7/30/10 14:56, Brendan Eich wrote: On Jul 30, 2010, at 2:47 PM, felix wrote: On 7/30/10 14:37, Brendan Eich wrote: For Harmony, we do not propose to standardize |for each|. Instead, the iteration and array comprehensions proposals for Harmony (see the wiki) propose that programmers choose keys, values, items (properties), or other iteration protocols by saying what they mean more precisely on the right-hand side of 'in': for (k in keys(o)) ... for (v in values(o)) ... for ([k, v] in properties(o)) ... // Python's items This seems better in TC39 members' views than adding ambiguous 'each' as a contextual keyword. I'm wary of that because this looks to me confusing: a = keys(o); for (k in a) ... The confusion here seems to be assuming that |a| is an Array instance. It's not. It is an iterator, so you'll get the keys (property names) found in o -- you won't get 0, 1, ... a.length-1. To avoid this confusion you can add new syntax (|for each| or whatever, doesn't matter). I've argued in recent posts that it is better from a global and long-term point of view to reform for-in after Python, than to condemn it and grow the language with new and generally more verbose, yet similar, syntax. I should have used a letter other than 'a'. my expectation from current javascript is that 'a' is an object, and that 'for (k in a)' will iterate over the object's properties. That's a fair assumption given the current state of the language, but assumptions will change with time. Perhaps if you named a something like aIter (wow, this example makes for a good argument for the snakecase a_iter), you're expectations would be re-framed. I think the common usage pattern will be using these iteration protocols at the point of consumption, as with the examples Brendan's given -- and in this case they're effectively anonymous and naming is a non-issue. You could expect functions like keys(a), values(a) -- or whatever protocol functions your library-of-choice provides -- to do /just /what they say. In this light I don't believe an iterator's for..in behavior is surprising at all, even w/ collective our es3 baggage. it seems odd to me that if 'a' is an iterator, it will iterate over the iterator's value stream instead of the iterator's properties, unless you define the two to be identical, which would be strange. eg, if you have an input stream iterator f, would f.hasOwnProperty('bacon') work or not? But they're different objects entirely. If a's a values iterator it's not iterating over it's /own/ value stream -- it's iterating over some other object's value stream. Thus, f.hasOwnProperty('bacon') shouldn't work (unless you've bacon'd your iterator, of course, or done some magic with proxy objects). So if f is iterating over an object's value stream then you have a reference to the underlying object if you want to do anything like hasOwnProperty. but that breaks functions like function shallowCopy(o) { var p = {}; for (var k in o) { if (o.hasOwnProperty(k)) { p[k] = o[k]; } } return p; } now I have to add a test to that function for the unexpected case that o is an infinite iterator and the for..in loop goes forever instead of enumerating some finite property list. that's just one example. I expect that if iteration uses the existing for..in syntax, there will be many similar bits of code that will need to be modified to treat iterators as a special case. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 29, 2010, at 2:47 PM, Waldemar Horwat wrote: Comprehensions: To be useful, need to have a bunch of basic helpers such as range(), properties(), values(), etc. with short names that can be used to construct comprehensions. These will be added to the proposal. Would like to be able to interleave for and if clauses: [x*y for (x in range(...)) if (isprime(x)) for (y in range(x+1...)) if (isprime(y))] I keep seeing code like this, I simply don't see it as viable to have for (.. in ...) sometimes enumerate property names, and some times enumerate keys, it seems like it could be both confusing and error prone. esp. given the simplest example: [x for (value in [1,2,3])] you would not get the desired behaviour, unless in comprehensions for(in) behaves differently from everywhere else. It seems far better to just define a distinct syntax for enumerating values of an object. --Oliver ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Day 2 meeting notes
On Jul 29, 2010, at 4:55 PM, Oliver Hunt wrote: On Jul 29, 2010, at 2:47 PM, Waldemar Horwat wrote: Comprehensions: To be useful, need to have a bunch of basic helpers such as range(), properties(), values(), etc. with short names that can be used to construct comprehensions. These will be added to the proposal. Would like to be able to interleave for and if clauses: [x*y for (x in range(...)) if (isprime(x)) for (y in range(x+1...)) if (isprime(y))] I keep seeing code like this, I simply don't see it as viable to have for (.. in ...) sometimes enumerate property names, and some times enumerate keys, it seems like it could be both confusing and error prone. esp. given the simplest example: [x for (value in [1,2,3])] you would not get the desired behaviour, unless in comprehensions for(in) behaves differently from everywhere else. It seems far better to just define a distinct syntax for enumerating values of an object. This, if I understand correctly, is Waldemar's position as well. On the other side both Python and JS1.7 and up in SpiderMonkey and Rhino make for-in meta-programmable. Python however makes sequence types by fiat iterate values, not keys, where sequence type includes list, which is []-bracketed like JS array initialisers. The counter-argument is that the language has coveted syntax, for-in, which has broken semantics, and rather than essentially abandon it, we should *reform* it. To the extent that JS1.7+ is meaningful, I argue we have done so without user confusion. Yes, users wish arrays were iterated by for-in returning values, not keys, but you can say for (i in values(a)) if you need to be sure. Likewise, if you must have property names, for (k in keys(o)) for any object o will guarantee keys, not values, and not Fibonacci numbers. The bottom line is that adding new syntax for custom iteration is missing the forest for the trees, arguing about how one more tree is safer and less confusing. In reality, the forest needs to stay small, and reform efforts can result in users understanding how to harvest the trees that we have, instead of having to understand and make good use of yet more trees. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss