Day 2 meeting notes

# Waldemar Horwat (15 years ago)

Here are my raw notes from today's meeting.

Waldemar

Decimal discussion: Sam: Can decimal be done as a package? Yes, if doing it via function calls is sufficient. Replacing the behavior of "+" would be harder. Mark: Value types not as important as other things that have already been dropped from ES6. Doug: Would want to replace double with decimal, but 754r is too flawed. Mark: Disagree with Doug about double but agree that 754r is a non-starter.

Mark: Proposed proxy changes:

  1. x[y] would call the get method that would not auto-coerce y to a string. Questions about impact of this on performance: when do implementations intern strings for property access? Implementors seem to all do the coercion/interning relatively early, so thing change would be inconvenient. Could this have an effect on the order in which multiple user methods (such as coercions) can be run as side effects of evaluating an expression? Mark withdrew this proposal after discussion.

  2. Recursive fixing. What happens when fixing a proxy makes a recursive call to fix the same proxy? Proposed answer: Throw a type error on the inner call.

  3. Changing the iteration trap: Replace an enumerator with an iterator. An implementation could provide an efficient way of converting one to the other. iterator: function () -> {next: function() -> any}

Debate about why this is an object with a next field that contains a function rather than just a closure. Some rationale: this makes iterators more similar to generators that have several fields. Iterate trap returns an iteration driver. Lots of confusion about trying to understand the iterator API proposal, its terminology, and the code on the wiki. Can't usefully continue to discuss it until the confusion is cleared up. Question about performance implications of using exceptions to terminate iteration. On the other hand, it is more convenient. Mark: Conflict between two levels of abstraction (say nested for-in iterations), where the outer one wants to throw a StopIteration exception through the inner one.

Brendan: Mozilla experimenting with not suppressing deleted properties from enumeration. Instead, they'd make a snapshot and iterate through the snapshot. This is incompatible with ES5 as written but ought to work in practice and is much simpler than trying to track changes in the prototype hierarchy.

Typed arrays: Consensus that aliasing and exposing endinness are problems. Would be more interested in a solution that doesn't suffer from those.

Shallow continuations: Not just a syntax change from the last proposal. The semantics are different: finally clauses will not get called if control escapes out of the function without calling the continuation.

Waldemar's objections: A function does something completely different if anywhere inside it there is a shift or yield expression. These non-function-like things should be distinguished (perhaps with a different keyword than "function"). Would be more amenable to this proposal if try/catch/finally were not allowed in outer scopes of such things.

W3C got a request from Google for an ECMAScript internationalization library. We should invite these folks to the next meeting.

Simple Modules proposal: Banishing global object is done by the script tag, not by modules. module M = Math is similar to const M = Math but with eval-when :compile semantics. Controversy over the local renaming syntax: whether import x.{A:B, C:D} means A is defined with the value B or B is defined with the value A. Inner modules are exported by default.

Remote modules on the web (1) example: json2.js is a module body, not a module:

// json2.js import stdlib.{String, ...} var tmpPrivateThingy = ... export function Stringify(x) {...} module Submodule {export function Foo() ...}

Caller: module JSON = load 'json.org/modules/json2.js'; alert(JSON.stringify({'hi': 'world'})); JSON.Submodule.Foo(...);

Note that json2.js does not name itself at the top level; the caller gets to name it. Allen: Some disagreement with that--the module should get to name itself in the default case. Doing that that way would complicate multiple modules in a file though.

Waldemar: How does the json2.js module refer to itself? Answer: it can't?

Unhygienic name capture concern. Doug: A widget needs to be able to defend itself from the page.

Filesystem modules for offline ES example has a bunch of bugs. Top-level modules are superfluous in the example. Question: what happens if one writes var h = io.File.open(...) instead of var h = open(...)? If it works, then the file system hierarchy is overlaid over the variable environment--creating a file in the current directory will create a variable in the program. If it doesn't work, then import does a different lookup from the normal one.

Cyclic dependencies: Can two script tags have cyclic dependencies? No.

// main file module Even = load "even.js"; module Odd = load "odd.js";

// odd.js import Even.*; export function myOddMethod() ... ...

// even.js foo = Odd.myOddMethod(); // Note that "Odd" is within scope here! ...

Mark: Objects to resolving ModuleLoader.current() using dynamic scoping to get the module loader. Confusion reigns after a bit of discussion. What if the call got initiated by the module eval inside another module eval? Here it's controversial. What if it got initiated by an event handler? The model doesn't seem to work in this case yet. Do we need ModuleLoader.current()?

ModuleLoader uses its "global" parameter only for legacy code uses.

ModuleLoader's "load" parameter is expected to call exactly one of its three Request functions. It's an error if it doesn't call any of them. rewriteMRL never reinvokes some other user module loader; it only uses the system one.

evalScript throws an error if it gets an export statement. However, it permits local modules but does not export them.

Not clear yet how dynamic loading works with mutually recursive modules.

Khronos group joint meeting: The issues are visible endianness and aliasing. Khronos found that the operations that they needed for file i/o were somewhat distinct from those they needed for graphics work.

Possible alternatives that don't expose endianness: Lightweight structs. Khronos: it's less efficient to index into those. Why?

Arrays of structs are preferable to sets of homogeneous element arrays due to cache and convenience effects.

Allen: Trade off lots of different views of scalar values vs. a richer set of SSE-like operations that might be a better semantic match. Example: smalltalk bitblt allowed a rich set of customizations.

Don't currently have scatter/gather operations. Would like to have them.

Well-supported arrays of structs (as schemas) might be a satisfactory compromise. Consensus emerging around that solution.

Khronos would like to continue developing their API as WebGL host objects in the meantime. This may lead to forking of APIs in the developers' minds. The possible danger is failure to reuse common abstractions.

Need a champion. Waldemar offered to work with Khronos to drive a proposal.

# Waldemar Horwat (15 years ago)

ECMA needs permanent documentation of proposals to satisfy WTO requirements. Proposal is to have ECMA staff take snapshots of the wiki once the relevant wiki proposal urls are posted.

Microsoft executed an ECMA software license for the test project.

Google is expected to contribute Sputnik.

Two active projects for contributions: Test262, Harmony. Use separate software contribution pro.

Brendan will look into relicensing the ES3/4 grammar and semantic engine and validator as BSD so it can be part of the Harmony contribution.

John: Easiest for TC39 members to just license contributions with BSD licenses from the start. Cuts out a lot of the legalese for collaborative development, needing only one contributor agreement form from each company for each project.

No response from Philippe to John.

No enthusiasm within the group for traveling to France for a TC39 meeting later this year.

Currently WebIDL has no editor. There's a vacuum there.

Four different organizations are standardizing ECMAScript binary arrays:

  • TC39
  • WebGL typed arrays are happening now. It's too late to affect them.
  • HTML5 binary arrays are in an earlier stage.
  • There's also NodeJS. It's awkward to coordinate all of this.

Rex Jaeschke's SC22 liaison document is somewhat out of date -- it claims that the 3rd edition of ECMA-262 is the current one.

TC39's ES5 comments shall be submitted to JTC1 secretariat. This includes some Technical Corrections. It would be nice if there were no need for a Ballot Resolution Meeting. All this falls under the old ISO procedure because we filed the paperwork before July 1.

If/when ISO approves ES5, we'll backport the changes to the ECMA ES5 document and submit it to the GA as Edition 5.1. Don't want to name it "Edition 6" for small errata like these.

Allen: Should we include the new ECMA BSD software license in ES5.1? Mark, Waldemar: Yes. Istvan: Wants to harmonize with ISO first.

Changes to module proposal from last meeting: Nested modules have access to the complete lexical environment (not just the module bindings) of their parent modules.

The external modules slideshow is new, not yet reflected on the wiki.

module MyLib { function log(x) {...} module Util { module Even = load "even.js"; module Odd = load "odd.js"; } } This is like compile-time textual inclusion except that even.js and odd.js don't have access to the bindings MyLib, log, and Util. However, odd.js has access to the binding of Even and even.js has access to the binding of Odd.

module Even = load "even.js";
module Odd = load "odd.js";
module Imaginary = load "even.js";

This creates two entirely separate copies of even.js.

Load is like textual inclusion, not like an import: even.js: module Odd = load "odd.js"

odd.js: module Even = load "even.js"; This blows up due to infinite inclusion.

Free variables inside odd.js and even.js are assumed to be module references. This stirred up controversy.

even.js: module A = load "foo.js"

foo.js: import Odd.* // Doesn't resolve Odd

To fix it: even.js: module Odd = Odd; module A = load "foo.js"

foo.js: import Odd.* // Doesn't resolve Odd

except that "module Odd = Odd" doesn't work because modules are letrec-bound. Proposed fix: introduce a special syntax for reexporting a module: module Odd;

Now, what if instead of "module Odd = Odd", one wanted to express something with the effect of "module Odd = Odd.SubOdd"?

Evaluation time expository example: module A {...}; module B = C.foo; module C = load "c.js"; works because the module B = ... statement doesn't do anything at run time. However, module A {...}; module B = C.foo; B.doSomething(); module C = load "c.js"; doesn't work because B is not defined by the time doSomething is invoked on it.

This also means that even.js cannot use "Odd" at the top level in the original example.

Question about what the following means in simple_modules on wiki: "A program is a sequence of scripts, evaluated from top to bottom. Each script is evaluated in a nested declarative environment record."

Possible answer: Each subsequent script on a web page is nested inside the prior one. Objections: This is weird. A let statement can shadow a let statement using the same name. This is counterintuitive for sibling scripts on a page. Having a flat scope for the collection of scripts on a web page would be better.

The above means that the top-level scope can be extended dynamically. This might clash with any predefined meaning of free variables such as considering them to be references to modules; it was a major hassle in ES4.

Why do we need overridable enumerators when we have overridable iterators? Point: because one returns only strings that are property names while the other returns arbitrary data. Counterpoint: If that's the criterion, then we shouldn't be using the for-in syntax for both. Leads to exactly the same problems when you think you're doing a for-in over properties when you're in fact getting values.

Error prone: for (x in Iterator.values([1,2,3,4,5])) vs. likely programming error for (x in [1,2,3,4,5])

Waldemar: If they are separate concepts, using the same for-in syntax for iteration and enumeration is too error-prone. If you intend to iterate on object Foo but accidentally pass in a Foo that's not an iterator, you'll get its property names instead of a visible error. It's particularly insidious with arrays.

Mozilla's recent trials: Changing the behavior of enumeration to a pure snapshot order under concurrent modification of the iterated object turned out to be a breaking change. People use object properties as work lists and sometimes delete future work list items. Mozilla's current trial is an "ugly" and hard-to-describe deletion-filtering order that handles deletion on the direct object but not across the prototype chain. Too implementation-specific to make it suitable for a standard.

Conclusion: Futile to exactly specify enumeration order in obscure cases?

Proposed enumeration order:

  1. Numeric order across all prototypes (entries that are considered to be array indices)
  2. Prototype chain order
  3. Creation order Debate and some incredulity over scanning the prototype chain twice. Prototype chain order should be the primary order, not secondary.

Behavior of what happens when the iterated object is modified is still unclear.

Why is there a need for both next() and send() in the iteration protocol? Seems like unnecessary historical complexity.

Generators discussion.

# Peter van der Zee (15 years ago)

On Thu, Jul 29, 2010 at 2:06 AM, Waldemar Horwat <waldemar at google.com>wrote:

Error prone: for (x in Iterator.values([1,2,3,4,5])) vs. likely programming error for (x in [1,2,3,4,5])

Waldemar: If they are separate concepts, using the same for-in syntax for iteration and enumeration is too error-prone. If you intend to iterate on object Foo but accidentally pass in a Foo that's not an iterator, you'll get its property names instead of a visible error. It's particularly insidious with arrays.

Would a keyword "as" be a sufficient alternative to use as an iteration operator in this sense? "as" would (I think?) not be allowed as an identifier in that position so it wouldn't have to be added to the reserved word lists. Because of that, I don't see a parser problem with it either.

Maybe, for somewhat better semantics, the order could be switched (iterator as key would make slightly better sense compared to key as iterator. I'm okay with either way since the switch would mean slightly more complicated syntax, eg. the var statement).

# Brendan Eich (15 years ago)

On Jul 29, 2010, at 1:20 AM, Peter van der Zee wrote:

On Thu, Jul 29, 2010 at 2:06 AM, Waldemar Horwat <waldemar at google.com> wrote: Error prone: for (x in Iterator.values([1,2,3,4,5])) vs. likely programming error for (x in [1,2,3,4,5])

Waldemar: If they are separate concepts, using the same for-in syntax for iteration and enumeration is too error-prone. If you intend to iterate on object Foo but accidentally pass in a Foo that's not an iterator, you'll get its property names instead of a visible error. It's particularly insidious with arrays.

Would a keyword "as" be a sufficient alternative to use as an iteration operator in this sense? "as" would (I think?) not be allowed as an identifier in that position so it wouldn't have to be added to the reserved word lists. Because of that, I don't see a parser problem with it either.

Maybe, for somewhat better semantics, the order could be switched (iterator as key would make slightly better sense compared to key as iterator. I'm okay with either way since the switch would mean slightly more complicated syntax, eg. the var statement).

The whole "fatal flaw" claim is Waldemar's opinion, but we've shipped for-in with (unstratified) meta-programmability since 2006 (Firefox 2) and Python has had it for years. Where are all the corpses?

# Waldemar Horwat (15 years ago)

Here are my raw notes from today's meeting.

Waldemar

Proxies: Discussing open issues on harmony:proxies How much validation to do? Do we do as much as we can stand? (Brendan, Allen, Mark: Depends on who it is that is doing the standing:) Allen: Some validation is expensive (looking for duplicate names in getOwnPropertyNames). Copying and normalizing results (property descriptors etc.) also has cost.

Allen: A script could override Object.getOwnPropertyDescriptor and cause trouble. MarkM: That's why for secure frames you'd have an initializer script that freezes things like Object.getOwnPropertyDescriptor. Allen: If you do that, the initializer script could substitute Object.getOwnPropertyDescriptor with its own, more bulletproof version. This puts the burden of validation on those who want it.

Debate about whether having tight restricitions now and relaxing them later could be called "upwards compatible". Waldemar: It's a stretch to put that label on it, just like I wouldn't call a change of functionality of a safe from having only the right combination open it to having anyone be able to open it an "upwards compatible" change.

Debate about the expense of copying/normalizing for validation.

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))] This is allowed by the proposal but not by the wiki. The proposal has the grammar (for let? if?)+. This doesn't currently allow let-if-let-if combinations, where the second let is valid only if the first if returns true. Would be nice to allow orders like these too. Debate about array comprehensions without a for clause: for (x ...) { ... [x if (isprime(x))] ... } This would generate either a 0- or a 1-element array depending on the if condition. Duly noted.

Generator comprehensions: (foo for (key in obj)) Desugars to functions, not lambdas! (function() { for (let key in obj) { yield foo; } })() Controversy for what happens if the generator expressions contain:

  • arguments (make it work?)
  • this (make it work?)
  • yield (outlaw it?)
  • var, break, return inside a let expression (don't bring back let expressions) Require parentheses around a generator comprehension even if it's used as a function argument? Python does. Here's one thing that could happen if we don't require parentheses in some contexts: Refactor foo(bigexpression for (y in z)) into: let i = bigexpression for (y in z) foo(i) which unfortunately would be transformed by semicolon insertion into something quite different: let i = bigexpression; for (y in z) foo(i)

const functions: Waldemar: Grammatical and readability concerns about confusion between const x and const x(). "const" alone restricts syntactic options for destructuring. Other options: "const function", "def", ... Erik: Common case is not to freeze everything. Freezing won't work with jquery listen functions etc. Brendan: Makes it harder to rely on expandos. Mark: Weak maps will solve the problem that expandos are used for now. Waldemar: That would just bifurcate the problem, since browsers without weak maps will be around for a while. Brendan: Weak maps won't make Firefox 4. Brendan: Education would be good but will take a while. Allen: Not willing to commit to this as a proposal yet because of concerns about its weight in making the language large. Waldemar: OK with this feature, not with using just "const" as the syntax. Doug: OK with this feature.

Internationalization API: Prototype mistakes in the spec. Some aLocale.prototype.foo should be Locale.foo; some should be Locale.prototype.foo. MarkM: removeExtensions sounds too imperative (it actually returns a new copy). Rename it. Waldemar: Don't understand the spec. What is the pattern in parseDate("29. jul 1974.", {"pattern": "yy-MM-dd"}, locale); doing? The supplied order is the opposite of the pattern and there are no dashes in the supplied string. Other pattern examples are "yy/MM" and "yMMM". Since the separators don't necessarily appear in the matched string, what does the choice of "-" vs. "/" mean in the pattern?

Should this be developed by TC39 as a separate library standard?

MarkmM, Allen, Waldemar: Confusing which locale is which in aLocale.prototype.getDisplayLanguage(locale). The wiki documentation contradicts the verbal explanation. Also, the name should be something something different like displayLanguageOf.

Explanation of difference between patterns and skeletons. They use different syntaxes and purposes. Skeletons don't have delimiters or punctuation. A skeleton turns into a locale-appropriate pattern; if the skeleton order would seem weird in the locale, the locale can overrule the skeleton and produce a more appropriate pattern.

No current support for alternate calendards (i.e. Japanese calendar).

Setting collator strength should give a new collator instead of modifying in place. An even better solution would be a settings object instead of a single strength parameter.

someStringArray.sort(col.compare) doesn't work because compare is defined on Collator.prototype. You'd get the generic method not bound to col.

Collators will canonicalize unicode characters (i.e. precomposed ö == decomposed ö). Any single collator will always induce a valid total order. The size of the equivalence classes will vary. For example, depending on strength, it may consider u == ü.

Discussed merits of non-BMP unicode escapes using the syntax \u{xxxxxx}. Generally liked, but we can't use it in character ranges in regular expressions without doing major surgery on regular expressions. Noncontroversial in other contexts.

String.direction questions: There are more than just the two possible directions. Vertical? A string containing parts with different directions? Indeterminate (example: empty string)?

Extended object API: getPropertyDescriptor is useful for encapsulating property lookup on prototypes.

Allen, Waldemar: Concern about getPropertyDescriptors, getOwnPropertyDescriptors, getPropertyNames being too eager and thus asymptotically slow on objects representing large collections. Would want to be able to work with huge or infinite objects.

What would call the getPropertyNames trap? Only another call to getPropertyNames.

Agreed to retain only getPropertyDescriptor and getPropertyNames.

Next meeting (at Mozilla) moved to Sep 30/Oct 1.

# Oliver Hunt (15 years ago)

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.

# Brendan Eich (15 years ago)

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.

# felix (15 years ago)

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) {}

# Douglas Crockford (15 years ago)

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.

# Dmitry A. Soshnikov (15 years ago)

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 bebad 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.

# Brendan Eich (15 years ago)

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

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.

# Brendan Eich (15 years ago)

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 bebad 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.

# felix (15 years ago)

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.

# felix (15 years ago)

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?

# Brendan Eich (15 years ago)

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.

# Brendan Eich (15 years ago)

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.

# Brendan Eich (15 years ago)

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.

# Oliver Hunt (15 years ago)

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.

# Maciej Stachowiak (15 years ago)

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".

, Maciej

# Brendan Eich (15 years ago)

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.

# Oliver Hunt (15 years ago)

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.

# Faisal Vali (15 years ago)

---------- Forwarded message ---------- From: Douglas Crockford <douglas at crockford.com> To: Oliver Hunt <oliver at 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?

, Faisal Vali

# Brendan Eich (15 years ago)

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 ;-).

# Brendan Eich (15 years ago)

On Jul 30, 2010, at 3:53 PM, Faisal Vali wrote:

---------- Forwarded message ---------- From: Douglas Crockford <douglas at crockford.com> To: Oliver Hunt <oliver at 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?

# felix (15 years ago)

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.

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?

yes, I'd like generalized iterators like python, so this is not really an argument.

# Dean Landolt (15 years ago)

On Sat, Jul 31, 2010 at 12:06 AM, felix <felix8a at 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.

# felix (15 years ago)

On 7/30/10 21:33, Dean Landolt wrote:

On Sat, Jul 31, 2010 at 12:06 AM, felix <felix8a at gmail.com <mailto:felix8a at 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.

# Brendan Eich (15 years ago)

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 (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 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 return property names, not arbitrary values. But since this example implements native object semantics, and since o has few properties, it would have been better to implement enumerate as well as iterate -- or instead of iterate if there's no reason to be lazy.

Why two traps, enumerate and iterate? Because a proxy can be a prototype of a non-proxy object, and for-in goes up the prototype chain. Having started with the enumerable properties of the non-proxy, going up the prototype chain to the proxy must not loop over arbitrary values, e.g. Fibonacci numbers. The for-in loop started enumerating property names, so it should continue doing so even if, somewhere up the prototype chain, it hits a proxy.

So, having started by enumerating a non-proxy, the for-in runtime finds a proxy up the prototype chain and calls the enumerate trap, not the iterate trap. This ends the prototype chain traversal, since proxies handle prototype delegation fully in the Harmony proxies design (this has been discussed here, see first cited text and reply at esdiscuss/2009-December/010313).

Implementing both iterate and enumerate allows a proxy to be iterated directly by for-in, but enumerated if on a prototype chain. The enumerate trap would return all the keys eagerly, in an array, that the iterate trap returns one-by-one via the 'next' method of the iterator it constructs.

So what you want can be implemented, but it need not be if only an iterator is needed. Besides the use-cases for iterators that entail lazy computation of a stream of values, the "large proxy" problem raised in the proxies proposal (near the bottom, look for "Waldemar") motivated the development of the iterate trap.

A large proxy is one emulating an object with a great many properties (possibly an unbounded number). A large proxy would have both iterate as well as enumerate in its handler, so if it were directly referenced on the right of 'in' by a for-in loop, the large proxy's keys would not be eagerly stuffed in a large array via its enumerate trap -- instead the iterate trap would be called and provide an iterator returning the keys lazily.

(The large proxy's enumerate trap could indeed fail to complete, being subject to script runtime and memory quotas, if "large" really means "infinite"; such a large proxy should not be the prototype of non-proxy objects, clearly!)

But I expect, as is common (with necessary changes) in Python, that proxies with only an iterate trap will be sufficient for many useful iteration protocols. Such an iterable proxy could use no-ops for its fundamental traps, in order to appear to be an empty object. Or as in the first example, if only the iterate trap is supplied in the handler, the proxy is empty but attempts to get or test its properties (including 'toString') will throw.

We'll work out the remaining details, with help from es-discuss and even JS authors who experiment with Firefox 4 betas. I hope that this message helps by showing some of the options, as well as the rationale for extending proxies to support iteration in the way we've proposed.

yes, I'd like generalized iterators like python, so this is not really an argument.

What's not an argument?

I'm not arguing that we'd like iterators under just about any syntax, details to be decided. I'm arguing specifically, for several reasons, that we would be better off allowing for-in to be customizable as in Python:

  1. Allows developers, not just TC39, to improve for-in semantics in parallel and at scale.

  2. Avoids enlarging the language with another for-in-like construct.

  3. Re-uses Python brainprint

I don't have a crystal ball, so I can't prove that these benefits override possible drawbacks. I'm encouraged by our experience with JS1.7 and up, since 2006. Both SpiderMonkey and Rhino support iterators not as proxies, but with essentially the same semantics with respect to for-in. The Python precedent is also significant and relevant to many developers.

In all this time, no one has complained about for-in sometimes returning keys (or values for sequence types in Python, but keys for other types), other times returning custom iterator results. Note also that proxies as proposed for Harmony, and Python's unstratified meta-programming support, allow all sorts of fundamental opeations, not just for-in, to be customized.

But reason (1) is IMHO most compelling. Between my own mistakes made in haste in 1995 and TC39 TG1's failure in 1996 and 1997 with ES1 to clean up for-in and fully specify it, we have a legacy of confusion in a fundamental looping statement.

Meanwhile the mass of Ajax developers has coped by building some nice libraries on top of the language, including for-in, but of course expressing iteration functionally for want of a way to augment or reform the syntax.

From this history I conclude that giving developers the ability to reform for-in is likelier to bear good fruit than TC39 taking another rare and probably one-time-only, hard-coded-in-the-spec, stab at the problem, using novel yet less desirable syntax.

The Web is a very large evolving system. Giving developers evolutionary paths toward better ways of doing everyday tasks in practice works better than dictating "final answers" via de-jure standards bodies every few years or decades. As noted above, iteration may let authors reform for-in, even as the full power of proxies helps JS authors and DOM developers (certainly Mozilla's, and I believe also IE's) reform the DOM.

But we need these evolutionary pathways. They are necessarily meta-programming APIs, so JS developers can write programs determining the meaning of program elements including objects, properties, and for-in loops.

Without such meta-programming facilities, not much will change, and what does change will come in the form of highly constrained (over-constrained, in my experience), infrequently released, and costly products of design by committee.

# Brendan Eich (15 years ago)

On Jul 30, 2010, at 9:51 PM, felix wrote:

On 7/30/10 21:33, Dean Landolt wrote:

[overcited text trimmed -- please trim too when you reply in future. /be]

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; }

We can make this work even if o is a proxy, by having its handler contain fundamental traps that delegate to Object.prototype. Thus it will appear to be an empty object.

But (see my last reply, sorry for its length) we could also use a proxy whose handler contains only an iterate trap, and let o.hasOwnProperty(k) throw an error for want of any such method. It's not obvious shallowCopy should succeed on a proxy.

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.

Only if the o.hasOwnProperty(k) attempt does not throw.

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.

Many developers (see Prototype's Object.extend) leave out the o.hasOwnProperty(k) test. Such code will iloop on an infinite iterator, and browsers police iloops with slow script watchdogs.

If we add new syntax, ignoring the reasons I gave against doing so, this code can continue to do what it has done -- but when given a proxy for o, it may still consume too much time or memory due to the proxy's eager enumerate hook. And of course a proxy could misbehave if its derived hasOwn trap is mis-implemented, or the fundamental getOwnPropertyDescriptor trap is buggy.

The proxy could also do something unusual from its derived get or fundamental getPropertyDescriptor trap, invoked by reading o[k] in the |p[k] = o[k];| statement.

Even now, host objects in certain DOMs will frustrate shallowCopy.

The upshot is that you can't migrate code into the Harmony script type naively. First, because Harmony is built on ES5 strict, which is not runtime-compatible (if no early errors stop you) with older editions. Second, because of new syntax and semantics, including proxies.

At this point I argue meta-programmable for-in via the iterate trap extension to proxies is a distant third.

# Dmitry A. Soshnikov (15 years ago)

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

# Dmitry A. Soshnikov (15 years ago)

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.

# Kevin Curtis (15 years ago)

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?

# Brendan Eich (15 years ago)

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.

# Brendan Eich (15 years ago)

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).