Re: Guards are now ready for discussion (was: Nov 18 notes)
On Nov 22, 2010, at 12:37 AM, Peter van der Zee wrote: On Mon, Nov 22, 2010 at 6:54 AM, Brendan Eich bren...@mozilla.com wrote: The ML-ish solution, which is forward compatible, is to parenthesize: let typedObj = { (prop1:type1): value1, (prop2:type2): value2, ... }; The parentheses hurt too, but only this case. And it still may win to annotate the entire ObjectLiteral sometimes, which would avoid (or move to one common guard-type definition) the property-wise annotation overhead. Comments? I wouldn't want to introduce forced parens for the sake of disambiguation for the coder. I'd rather go with ::, # or @ ... Those are not all equally good, and you're again sacrificing the common case (especially with ::) where there's no need to parenthesize, for the uncommon case of property-wise type annotation in an object initialiser. Actually, one can use : just fine in an initialiser provided one or two colons are required per property initialiser. There's no need for :: or anything else except to avoid a confusing re-use of colon. Which is an issue, but :: doesn't exactly address it! Fundamenally, though, you have to weight each use-case by frequency. If as I argue var x : T, function f(a: T, b: U): V {...}, etc. annotations dominate the rare var typedObj = ... case, then parens or whatever is needed (even a separated type annotation for the whole initialiser, as in ES4) is worth it. Actually, for object literal I'd go for an equal sign (=), but that obviously won't fix var declarations where the colon is fine. We're not changing object initialiser syntax. Can't the guards proposal be applied to function parameters as well? Or am I stepping on land mines there? :) It seems like the same logic could be applied. In that case the equal sign is maybe even more preferable. What are you proposing = be used for, if not default parameter values (see the wiki) or assignment? No way does = make sense for type annotation. But yes: type annotation (guard, contract, whateve -- not static types but that's not at issue) should be possible for function parameters, return types, local variables, let bindings, catch vars, etc. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Mon, Nov 22, 2010 at 8:37 AM, David Herman dher...@mozilla.com wrote: for (var i : x) ... // must be new iteration for (var i : T : x) ... // iteration again, but parsed how? for (var i : T in x) ... // for-in with annotated var Bummer! I'm beginning to feel more strongly again that overloading for-in, as opposed to introducing yet another syntactic variant of `for', is the right way to go. thought experiment Imagine we made Harmony backwards-incompatible in the following way: for-in loops would *only* work on iterable objects, and would dynamically fail on non-iterable objects. So if you wanted the legacy keys behavior, you would have to explicitly call a `keys' library function. /thought experiment If we're gonna go invent new keywords why not use the obvious? iterate (var x in y) ... ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 12:49 AM, Peter van der Zee wrote: On Mon, Nov 22, 2010 at 8:37 AM, David Herman dher...@mozilla.com wrote: for (var i : x) ... // must be new iteration for (var i : T : x) ... // iteration again, but parsed how? for (var i : T in x) ... // for-in with annotated var Bummer! I'm beginning to feel more strongly again that overloading for-in, as opposed to introducing yet another syntactic variant of `for', is the right way to go. thought experiment Imagine we made Harmony backwards-incompatible in the following way: for-in loops would *only* work on iterable objects, and would dynamically fail on non-iterable objects. So if you wanted the legacy keys behavior, you would have to explicitly call a `keys' library function. /thought experiment If we're gonna go invent new keywords why not use the obvious? iterate (var x in y) ... Did you read Dave's previous post, or mine? We are not going to go invent some new overlong non-reserved word. for is the right word. One might try retasking do but it's ambiguous: do-while loops do not require braces (KR style favors them even for one-liners but it's just a convention). Dave's point about getting people to use one universal quantifying loop structure is good. If that loop syntax is not for-in, it will have to be something new that works even when there is no iterate trapping proxy. But that will just be a suckier version of the universal (enumerating if not custom-iterating) for-in from JS1.7. So what good are we really doing by forking for-in into bastard children (the one we have, definitely a bastard -- I should know! :-/) and a new one with red hair? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Guards are now ready for discussion (was: Nov 18 notes)
On Mon, Nov 22, 2010 at 9:43 AM, Brendan Eich bren...@mozilla.com wrote: On Nov 22, 2010, at 12:37 AM, Peter van der Zee wrote: On Mon, Nov 22, 2010 at 6:54 AM, Brendan Eich bren...@mozilla.com wrote: The ML-ish solution, which is forward compatible, is to parenthesize: let typedObj = { (prop1:type1): value1, (prop2:type2): value2, ... }; The parentheses hurt too, but only this case. And it still may win to annotate the entire ObjectLiteral sometimes, which would avoid (or move to one common guard-type definition) the property-wise annotation overhead. Comments? I wouldn't want to introduce forced parens for the sake of disambiguation for the coder. I'd rather go with ::, # or @ ... Those are not all equally good, and you're again sacrificing the common case (especially with ::) where there's no need to parenthesize, for the uncommon case of property-wise type annotation in an object initialiser. Actually, one can use : just fine in an initialiser provided one or two colons are required per property initialiser. There's no need for :: or anything else except to avoid a confusing re-use of colon. Which is an issue, but :: doesn't exactly address it! As you noted yourself in the other thread, if iterators take on the syntax of `for (var x : y : z)`, the single colon for guards would introduce ambiguity. (And I believe :: could fix that?) Fundamenally, though, you have to weight each use-case by frequency. If as I argue var x : T, function f(a: T, b: U): V {...}, etc. annotations dominate the rare var typedObj = ... case, then parens or whatever is needed (even a separated type annotation for the whole initialiser, as in ES4) is worth it. Actually, for object literal I'd go for an equal sign (=), but that obviously won't fix var declarations where the colon is fine. We're not changing object initialiser syntax. Can't the guards proposal be applied to function parameters as well? Or am I stepping on land mines there? :) It seems like the same logic could be applied. In that case the equal sign is maybe even more preferable. What are you proposing = be used for, if not default parameter values (see the wiki) or assignment? No way does = make sense for type annotation. I'm sorry, I did mean = for guard. But reading it back, you are right; it doesn't make sense. But yes: type annotation (guard, contract, whateve -- not static types but that's not at issue) should be possible for function parameters, return types, local variables, let bindings, catch vars, etc. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Guards are now ready for discussion (was: Nov 18 notes)
On Nov 22, 2010, at 1:03 AM, Peter van der Zee wrote: On Mon, Nov 22, 2010 at 9:43 AM, Brendan Eich bren...@mozilla.com wrote: On Nov 22, 2010, at 12:37 AM, Peter van der Zee wrote: On Mon, Nov 22, 2010 at 6:54 AM, Brendan Eich bren...@mozilla.com wrote: The ML-ish solution, which is forward compatible, is to parenthesize: let typedObj = { (prop1:type1): value1, (prop2:type2): value2, ... }; The parentheses hurt too, but only this case. And it still may win to annotate the entire ObjectLiteral sometimes, which would avoid (or move to one common guard-type definition) the property-wise annotation overhead. Comments? I wouldn't want to introduce forced parens for the sake of disambiguation for the coder. I'd rather go with ::, # or @ ... Those are not all equally good, and you're again sacrificing the common case (especially with ::) where there's no need to parenthesize, for the uncommon case of property-wise type annotation in an object initialiser. Actually, one can use : just fine in an initialiser provided one or two colons are required per property initialiser. There's no need for :: or anything else except to avoid a confusing re-use of colon. Which is an issue, but :: doesn't exactly address it! As you noted yourself in the other thread, if iterators take on the syntax of `for (var x : y : z)`, the single colon for guards would introduce ambiguity. (And I believe :: could fix that?) First, I did not argue that there was a formal grammar ambiguity. There may not be, but it still is ugly and confusing. Second, you're arguing backwards -- we do not have for (x : z) today, it is not obvious we should add it to the language, so it does not follow that we must use :: for type annotations. The precedent for : as type annotation punctuator is stronger than the precedent for :: in the same role; meanwhile, from E4X and AS3 and other old-ES4 offshoots, we have :: for namespace qualification (C++ has that too). So :: is just wrong all around. Before reaching in a reactive way for @, let's slow down and consider what got us here: over-eager bikeshedding of : instead of in to try to split for-in into two loops, to make programmers prejudge whether they want enumeration or something potentially meta-programmable (but not necessarily -- we did not decide that for (x : z) fails if z is not a proxy with an iterate trap, and that doesn't make much sense). So as dherman pointed out, we're really just trying to replace for-in with for-: as the new for-in. But if the syntax is ugly and confusion, if not ambiguous, and if the new form won't work in old browsers anyway, what good are we doing? Defending against someone porting or mixing JS code into Harmony context and having a for-in loop that expects string keys fail because it got Fibonacci numbers? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: instanceof trap - Useful to implement promises through proxies
Ok, I see. So it's true that the current Proxy API doesn't support promises that would throw on instanceof when unresolved and return a boolean when resolved. The main reason is that we didn't want proxies to meddle with the inheritance chain, in particular giving proxies the ability to implement dynamic inheritance. While intercepting instanceof doesn't truly introduce dynamic inheritance in the language, you could definitely create proxies that are `instanceof` prototypes from unrelated hierarchies, which could spell trouble. If the prototype or constructor function of the to-be-calculated value of a promise is known to the promise creator, it is possible to make `instanceof` work somewhat sensibly for promises: var pet = promiseFor(Cat); // promise implementor can now write: Proxy.create(Cat.prototype, ...) ... // assume pet is still unresolved: pet instanceof Cat // true since the promise proxy already has the correct prototype I said somewhat sensibly since this may or may not be the behavior you'd want for unresolved promises. Note that even the proposed instanceof trap would not help if the constructor function (Cat in the above example) is not in bed with the promise abstraction, since `obj instanceof F` would trap on F's handler, not on obj's handler. If F is a promise-aware function proxy, it could then check whether obj is an unresolved promise and throw. But it would require user code to write at least: Cat = PromiseAwareWrapperFor(function () { ... }); // declare that my instances can be promises Cheers, Tom 2010/11/19 Sebastian Markbåge sebast...@calyptus.eu Hi Tom, The idea here is that it's obj that's the proxy/promise. For example: var pet = future(); var closure = function(){ return pet instanceof Cat; }; resolve(pet); // later closure(); //? I should've clarified that it's the [Prototype] of pet that needs to be delayed. Not prototype of Cat. It may be resolved to any number of different prototype chains depending on how the future/promise is resolved. Sebastian On Fri, Nov 19, 2010 at 4:38 PM, Tom Van Cutsem tomvc...@gmail.comwrote: Hi Sebastian, Could you clarify why trapping instanceof is required for promises? It seems your use case can be addressed already using the current Proxy API: if `FP` is a function proxy, then `obj instanceof FP` will query `FP` for its `prototype` property (as per the default algorithm) by calling the function proxy's `get` trap. Inside that `get` trap, you could check whether the prototype is unresolved or far and if so, throw an exception. Cheers, Tom 2010/11/18 Sebastian Markbåge sebast...@calyptus.eu I thought I'd make the case for the instanceof trap in strawman:proxy_extensions - beyond multiple inheritance. I'm sure this point has been made before at some point. This is useful to implement various forms of promises using proxies. The prototype can be resolved at a later time perhaps after some IO or lazy operation. This should be able to fail if the promise is currently unresolved or far. It's the final piece missing to preserve the invariants that keep the JS lucid dream. I'm also curious to hear any objections to this proposal. I don't see the same issues as with mutable prototypes being applicable to proxies. Sebastian Markbåge ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
My arguments in favor of keeping the existing for-in syntax and making it meta-programmable: - Simplicity. Don't underestimate the complexity creep of introducing a new looping construct. Many small changes add up quickly. Especially for novices, having two looping constructs that are so similar will be terribly confusing. - Keeping the existing syntax means that the tons of JS code already out there can instantly become client-code to iterators. As Brendan noted: you can use iterators to generate a stream of keys (strings) without confusing clients. - Client code to proxies that emulate objects with lots of properties can continue to use for-in. It just feels wrong that client code should change because of pure implementation details of an object (i.e. whether it eagerly or lazily generates its keys). Of course, if an object changes its behavior from iterating keys to iterating values, this breaks clients and they should be modified to use for (var k in keys(obj)). But I don't see how this differs from any other such changes made to an object. The important thing to note here is that turning an object into an iterator requires explicit action by the programmer. If iterators were implemented ala Python using a magical __iterate__ hook, then I'd complain because Harmony code could silently turn normal objects into iterators. But there's no such risk with this proposal. I think that's a key point worth re-iterating: iterators and regular objects are sufficiently distinct so that there's no risk of automatically converting one into the other. There is only a risk to existing client-code if a Harmony programmer changes a normal object into an iterator. But at that point the programmer knows he's making a non-upwards-compatible change and clients should be changed accordingly. I don't see how the for-in case differs in this respect, fundamentally, from say, renaming a method. Cheers, Tom 2010/11/22 Brendan Eich bren...@mozilla.com On Nov 22, 2010, at 12:49 AM, Peter van der Zee wrote: On Mon, Nov 22, 2010 at 8:37 AM, David Herman dher...@mozilla.com wrote: for (var i : x) ... // must be new iteration for (var i : T : x) ... // iteration again, but parsed how? for (var i : T in x) ... // for-in with annotated var Bummer! I'm beginning to feel more strongly again that overloading for-in, as opposed to introducing yet another syntactic variant of `for', is the right way to go. thought experiment Imagine we made Harmony backwards-incompatible in the following way: for-in loops would *only* work on iterable objects, and would dynamically fail on non-iterable objects. So if you wanted the legacy keys behavior, you would have to explicitly call a `keys' library function. /thought experiment If we're gonna go invent new keywords why not use the obvious? iterate (var x in y) ... Did you read Dave's previous post, or mine? We are not going to go invent some new overlong non-reserved word. for is the right word. One might try retasking do but it's ambiguous: do-while loops do not require braces (KR style favors them even for one-liners but it's just a convention). Dave's point about getting people to use one universal quantifying loop structure is good. If that loop syntax is not for-in, it will have to be something new that works even when there is no iterate trapping proxy. But that will just be a suckier version of the universal (enumerating if not custom-iterating) for-in from JS1.7. So what good are we really doing by forking for-in into bastard children (the one we have, definitely a bastard -- I should know! :-/) and a new one with red hair? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Guards are now ready for discussion (was: Nov 18 notes)
On Mon, Nov 22, 2010 at 12:37 AM, Peter van der Zee e...@qfox.nl wrote: Can't the guards proposal be applied to function parameters as well? Or am I stepping on land mines there? :) Yes. Please look at the guards strawman. It's in there. -- Cheers, --MarkM ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Guards are now ready for discussion (was: Nov 18 notes)
On Nov 22, 2010, at 5:42 AM, Mark S. Miller wrote: On Mon, Nov 22, 2010 at 1:09 AM, Brendan Eich bren...@mozilla.com wrote: [...] Before reaching in a reactive way for @, let's slow down and consider what got us here: over-eager bikeshedding of : instead of in [...] That's not what got me here. For me the issue is uniformity of guard syntax across all contexts where we need them -- including properties of object literals. I agree it is the uncommon case. But the non-uniformity of using one syntax for this case and another syntax for the more common cases bugs me. Likewise, the technically unambiguous { foo : G : 33 } I find too visually confusing and ugly. Agreed. My point in reply to Peter was that it may be formally unambiguous, but it's still ugly and confusing. As you are fond of saying, notation is user interface. And how! But doubling a colon is not better interface. Especially when that double-tax hits all declarations, not just object initialisers. Let's see if I can write down some alternatives and list trade-offs: 0. let typedObj = { foo : 33 } : { foo : G }; // a la ES4 1. let typedObj = { foo :: G : 33 }; // the guards strawman 2. let typedObj = { (foo : G) : 33 }; // the ML-ish way 3. let typedObj = { foo @ G : 33 }; // funny cartoon chars Trade-offs: 0+ avoids running type^H^H^H^Hguard together with property name and value using :, ::, @, or whatever, instead paralleling the whole initialiser 0+ allows commoning the guard initialiser expression so it can be reused among multiple object value initialisers 0- parallel structure duplicates all property names (and order? not clear), which is tedious at any scale and error-prone for large initialisers 1+ avoids restating names (and order?) 1- imposes double-colon tax on all annotation contexts, even though only this one wants it for visual distinctiveness (not disambiguation of formal grammar) 1- :: is an eyesore (yet arguably not distinct enough from single : -- see last 1- item), and has quite different precedent as namespace qualification operator in Ecma and other standards 2+ keeps : as annotation punctuator 2- at the price of parenthesizing the property name in object initialisers 3+ avoids overloading colon in object initialisers 3- flouts programming language (including JS derivatives) precedent for using : as annotation punctuator 3- may collide with JScript's preprocessor, also may collide with @attributes in E4X, uses up @ in a novel way where we might want to reserve it for another use Comments: We don't have too many other funny chars (# is wanted for concise function syntax). This suggests trying to use : and not breaking the common annotation case over this initialiser hard case. Sometimes a (commoned, reusable) annotation at the end of the whole object initialiser is pure win. It's really not clear at this point that *only* at the end, or *only* interspersed guards, are the one way we should standardize. If guards are uncommon in object initialisers, parenthesization is arguably fine, not only as a tax users can pay but as a visual flag that something is up. Let's consider the likely far more common use-cases: var x : T = v; function f(a: U, b: V): W {...} ... { ... let x : T = v; ... } while @ or another char could work, the tradition favoring : combined with the scarcity of unused punctuators makes me want to stick with colon. That, plus the relative rarity of annotated initialisers (in my best guess -- we can argue about this or try to estimate by adding guards to existing code), make me still favor (2). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Guards are now ready for discussion (was: Nov 18 notes)
On 2010-11-22, at 13:13, Brendan Eich wrote: That, plus the relative rarity of annotated initialisers (in my best guess -- we can argue about this or try to estimate by adding guards to existing code), make me still favor (2). The idea of guards as adjectives (ala C) is a non-starter? Would keyword arguments ever be added to JS? Any chance that would influence the choices made here? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Guards are now ready for discussion (was: Nov 18 notes)
On Nov 22, 2010, at 10:58 AM, P T Withington wrote: On 2010-11-22, at 13:13, Brendan Eich wrote: That, plus the relative rarity of annotated initialisers (in my best guess -- we can argue about this or try to estimate by adding guards to existing code), make me still favor (2). The idea of guards as adjectives (ala C) is a non-starter? That is awkward if you do require a leading var/let/const in addition to the annotation: var Int32Like i = 0; and we don't want to reserve the names of all possible guards. Also, guards or runtime types are not static types, so it seems better to avoid reusing type declarator order from static languages in the C family. Functions are easier to extend this way: function foo(MyGuardA a, MyGuardB b) GuardForReturnValue { ... } but the point about not copying type-then-declarator stands. Would keyword arguments ever be added to JS? Any chance that would influence the choices made here? Imponderable unless we reserve : in call expressions such as foo(arg1: val1, arg3: val3); to mean named formal parameter association -- keyword arguments if I understand what you meant by that. Yet this is both too close to an object initialiser, and also not ambiguous with annotations in declarations. This is an expression, not a declaration. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 2:08 AM, Tom Van Cutsem wrote: My arguments in favor of keeping the existing for-in syntax and making it meta-programmable: - Simplicity. Don't underestimate the complexity creep of introducing a new looping construct. Many small changes add up quickly. Especially for novices, having two looping constructs that are so similar will be terribly confusing. You're not saving the addition of a looping construct, in making for-in behave differently depending on what is on the right hand side of 'in' you're merely adding an additional looping that is not syntactically observable. - Keeping the existing syntax means that the tons of JS code already out there can instantly become client-code to iterators. As Brendan noted: you can use iterators to generate a stream of keys (strings) without confusing clients. And all existing standards compliant code can no longer rely on for in doing what it has done for years. Suddently for in _behaviour_ may change based on the prototype chain. - Client code to proxies that emulate objects with lots of properties can continue to use for-in. It just feels wrong that client code should change because of pure implementation details of an object (i.e. whether it eagerly or lazily generates its keys). My understanding was that proxies would have a trap to allow them to generate an array of strings to be used. Of course, if an object changes its behavior from iterating keys to iterating values, this breaks clients and they should be modified to use for (var k in keys(obj)). But I don't see how this differs from any other such changes made to an object. The important thing to note here is that turning an object into an iterator requires explicit action by the programmer. If iterators were implemented ala Python using a magical __iterate__ hook, then I'd complain because Harmony code could silently turn normal objects into iterators. But there's no such risk with this proposal. I think that's a key point worth re-iterating: iterators and regular objects are sufficiently distinct so that there's no risk of automatically converting one into the other. There is only a risk to existing client-code if a Harmony programmer changes a normal object into an iterator. But at that point the programmer knows he's making a non-upwards-compatible change and clients should be changed accordingly. I don't see how the for-in case differs in this respect, fundamentally, from say, renaming a method. I do not expect the behaviour of for(in) to change from harmony to non-harmony code. Ignoring all other concerns it would mean the behaviour of objects passed from a harmony context to a non-harmony context would be unexpected. I'm kind of frustrated that after discussing all this last week, and apparently getting to some kind of consensus, we've more or less instantly gone back to what we had at the beginning of the meeting, where for(in) gets magically overloaded and a developer has some magical way to understand what a given for(in) loop is going to do. If this was going to be the outcome why did we even spend time discussing this? --Oliver ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Guards are now ready for discussion (was: Nov 18 notes)
On Nov 22, 2010, at 11:12 AM, Brendan Eich wrote: Would keyword arguments ever be added to JS? Any chance that would influence the choices made here? Imponderable unless we reserve : in call expressions such as foo(arg1: val1, arg3: val3); to mean named formal parameter association -- keyword arguments if I understand what you meant by that. Yet this is both too close to an object initialiser by which I mean: what's the point? Just pass an object initialiser to a function that destructures it. Can be optimized to avoid constructing an object, in advanced implementations. But whatever the optimization, the language is crowded if we have both of foo(arg1: val1, arg3: val3); and today's works-in-all-browsers form: foo({arg1: val1, arg3: val3}); Is losing the braces really worth the added complexity? /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 11:14 AM, Oliver Hunt wrote: On Nov 22, 2010, at 2:08 AM, Tom Van Cutsem wrote: My arguments in favor of keeping the existing for-in syntax and making it meta-programmable: - Simplicity. Don't underestimate the complexity creep of introducing a new looping construct. Many small changes add up quickly. Especially for novices, having two looping constructs that are so similar will be terribly confusing. You're not saving the addition of a looping construct, in making for-in behave differently depending on what is on the right hand side of 'in' you're merely adding an additional looping that is not syntactically observable. Is the additional aspect important enough to split syntax over? If so, would you make the new form *only* work on proxies and throw given a non-proxy on the right of in? If not, why not? - Keeping the existing syntax means that the tons of JS code already out there can instantly become client-code to iterators. As Brendan noted: you can use iterators to generate a stream of keys (strings) without confusing clients. And all existing standards compliant code can no longer rely on for in doing what it has done for years. Suddently for in _behaviour_ may change based on the prototype chain. That's true in Harmony, and as you noted on the list a while ago, also true in ES5 strict. I decried too much migration tax but allowed we want to break compatibility for important wins. Lexical scope all the way up is one such proposed win, justifying removing the global object from the scope chain. In my view, letting for-in be reformed by library authors is another case where the migration tax is worth it. Now, I need to ask whether you are making an absolute statement: Harmony must be runtime as well as syntactically compatible with ES5 strict, i.e., a superset language? - Client code to proxies that emulate objects with lots of properties can continue to use for-in. It just feels wrong that client code should change because of pure implementation details of an object (i.e. whether it eagerly or lazily generates its keys). My understanding was that proxies would have a trap to allow them to generate an array of strings to be used. That's true, the fundamental trap is enumerate, but as noted on the wiki and pointed out by Waldemar when we reviewed proxies and moved them to harmony:proposals status, this does not work well for large and infinite objects. We moved proxies to harmony:proposals status with the agreement that the iteration protocol would address such hard cases. The iterators proposal does that, including details about proxies as prototypes of other objects (you don't want to switch to iterate if you start with enumeration -- you must call the proxy handler's enumerate trap). Last week we agreed toward the end of the meeting, with some lack of clarity about *how* to do this, to recast for (x in y) as for (x : keys(y)) and allow either enumerate or (if provided) iterate to be used to handle large/infinite objects. Now the for-: syntax looks like a mistake, and we still haven't reworked things to address the required large/infinite cases. I'm reviewing all this because I do not think everyone has kept up with the details. But the details matter, and they have some irreducible complexity we can't wish away. They motivate more than just the enumerate trap which eagerly returns all the property keys Of course, if an object changes its behavior from iterating keys to iterating values, this breaks clients and they should be modified to use for (var k in keys(obj)). But I don't see how this differs from any other such changes made to an object. The important thing to note here is that turning an object into an iterator requires explicit action by the programmer. If iterators were implemented ala Python using a magical __iterate__ hook, then I'd complain because Harmony code could silently turn normal objects into iterators. But there's no such risk with this proposal. I think that's a key point worth re-iterating: iterators and regular objects are sufficiently distinct so that there's no risk of automatically converting one into the other. There is only a risk to existing client-code if a Harmony programmer changes a normal object into an iterator. But at that point the programmer knows he's making a non-upwards-compatible change and clients should be changed accordingly. I don't see how the for-in case differs in this respect, fundamentally, from say, renaming a method. I do not expect the behaviour of for(in) to change from harmony to non-harmony code. Ignoring all other concerns it would mean the behaviour of objects passed from a harmony context to a non-harmony context would be unexpected. That is a risk but it is not an absolute. It is one end of a trade-off. The other end is the benefit of avoiding new and hard-to-make-winning syntax,
Re: Guards are now ready for discussion (was: Nov 18 notes)
On 2010-11-22, at 14:33, Brendan Eich wrote: Is losing the braces really worth the added complexity? Perhaps not. Braces are surely as good a way to denote keyword args as any other flag. It occurred to me that there is no equivalent of object literals in the languages I know that have keyword arguments. There are strong parallels, which is probably why the discussion of the guarded literal syntax brought it to my mind. But that makes me wonder how I would write a function with keyword arguments that were both guarded and had default values? function f (a:V, b:W=w {c:X=x, d:Y=y}) ... or, the less parallel: function f (a:V, b:W=w {(c:X):x, (d:Y):y}) ... ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 11:49 AM, Brendan Eich wrote: On Nov 22, 2010, at 11:14 AM, Oliver Hunt wrote: On Nov 22, 2010, at 2:08 AM, Tom Van Cutsem wrote: My arguments in favor of keeping the existing for-in syntax and making it meta-programmable: - Simplicity. Don't underestimate the complexity creep of introducing a new looping construct. Many small changes add up quickly. Especially for novices, having two looping constructs that are so similar will be terribly confusing. You're not saving the addition of a looping construct, in making for-in behave differently depending on what is on the right hand side of 'in' you're merely adding an additional looping that is not syntactically observable. Is the additional aspect important enough to split syntax over? If so, would you make the new form *only* work on proxies and throw given a non-proxy on the right of in? If not, why not? Proxies need to have a way to work with for(in) which is the only reason I believe they should be allowed to have a trap for for(in) enumeration. It's necessary if you ever want to have DOM objects like NodeList be defined in terms of proxies. - Keeping the existing syntax means that the tons of JS code already out there can instantly become client-code to iterators. As Brendan noted: you can use iterators to generate a stream of keys (strings) without confusing clients. And all existing standards compliant code can no longer rely on for in doing what it has done for years. Suddently for in _behaviour_ may change based on the prototype chain. That's true in Harmony, and as you noted on the list a while ago, also true in ES5 strict. I decried too much migration tax but allowed we want to break compatibility for important wins. Lexical scope all the way up is one such proposed win, justifying removing the global object from the scope chain. In my view, letting for-in be reformed by library authors is another case where the migration tax is worth it. Now, I need to ask whether you are making an absolute statement: Harmony must be runtime as well as syntactically compatible with ES5 strict, i.e., a superset language? I'm not sure what relevance pure lexical scoping in harmony has to a discussion on the behaviour of for(in). If we're saying that every new API and language feature being discussed for harmony will only be available in harmony, then yes harmony can do whatever it wants, but you've also caused me to lose any interest in implementing harmony at that point. If every feature being discussed is only usable inside harmony code then the migration cost of individual features won't be relevant as it's a distinct language with no impact on ES. - Client code to proxies that emulate objects with lots of properties can continue to use for-in. It just feels wrong that client code should change because of pure implementation details of an object (i.e. whether it eagerly or lazily generates its keys). My understanding was that proxies would have a trap to allow them to generate an array of strings to be used. That's true, the fundamental trap is enumerate, but as noted on the wiki and pointed out by Waldemar when we reviewed proxies and moved them to harmony:proposals status, this does not work well for large and infinite objects. We moved proxies to harmony:proposals status with the agreement that the iteration protocol would address such hard cases. The iterators proposal does that, including details about proxies as prototypes of other objects (you don't want to switch to iterate if you start with enumeration -- you must call the proxy handler's enumerate trap). Last week we agreed toward the end of the meeting, with some lack of clarity about *how* to do this, to recast for (x in y) as for (x : keys(y)) and allow either enumerate or (if provided) iterate to be used to handle large/infinite objects. Now the for-: syntax looks like a mistake, and we still haven't reworked things to address the required large/infinite cases. I don't understand why : was a mistake, the only counter argument i saw was that it didn't work well with type annotations, which i see no value in and aren't being discussed for harmony or es-next. Honestly I wish people would stop treating type annotations as something important -- ES is a dynamically typed language, trying to shoe-horn static typing in seems strange. I'm reviewing all this because I do not think everyone has kept up with the details. But the details matter, and they have some irreducible complexity we can't wish away. They motivate more than just the enumerate trap which eagerly returns all the property keys Of course, if an object changes its behavior from iterating keys to iterating values, this breaks clients and they should be modified to use for (var k in keys(obj)). But I don't see how this differs from any other such changes made to an
Re: Nov 18 notes
On Nov 22, 2010, at 12:09 PM, Oliver Hunt wrote: On Nov 22, 2010, at 11:49 AM, Brendan Eich wrote: On Nov 22, 2010, at 11:14 AM, Oliver Hunt wrote: On Nov 22, 2010, at 2:08 AM, Tom Van Cutsem wrote: My arguments in favor of keeping the existing for-in syntax and making it meta-programmable: - Simplicity. Don't underestimate the complexity creep of introducing a new looping construct. Many small changes add up quickly. Especially for novices, having two looping constructs that are so similar will be terribly confusing. You're not saving the addition of a looping construct, in making for-in behave differently depending on what is on the right hand side of 'in' you're merely adding an additional looping that is not syntactically observable. Is the additional aspect important enough to split syntax over? If so, would you make the new form *only* work on proxies and throw given a non-proxy on the right of in? If not, why not? Proxies need to have a way to work with for(in) which is the only reason I believe they should be allowed to have a trap for for(in) enumeration. It's necessary if you ever want to have DOM objects like NodeList be defined in terms of proxies. Yes, that's already in the harmony:proxies design via the enumerate trap. You didn't answer my questions, though. If we were to add for (x : y) as you among others seemed to want last week, would it throw on a non-proxy on the right of :? The answer matters both so we can hope to define for-: (whatever its syntax, if not for-in), and so we can reach agreement on premises. One premise: new users of Harmony implementations can just always use for-: as dherman said. They never have to use for-in. For enumeration, they use for (k in keys(o)). Agree or disagree? If you are indifferent then you're not really participating :-/. - Keeping the existing syntax means that the tons of JS code already out there can instantly become client-code to iterators. As Brendan noted: you can use iterators to generate a stream of keys (strings) without confusing clients. And all existing standards compliant code can no longer rely on for in doing what it has done for years. Suddently for in _behaviour_ may change based on the prototype chain. That's true in Harmony, and as you noted on the list a while ago, also true in ES5 strict. I decried too much migration tax but allowed we want to break compatibility for important wins. Lexical scope all the way up is one such proposed win, justifying removing the global object from the scope chain. In my view, letting for-in be reformed by library authors is another case where the migration tax is worth it. Now, I need to ask whether you are making an absolute statement: Harmony must be runtime as well as syntactically compatible with ES5 strict, i.e., a superset language? I'm not sure what relevance pure lexical scoping in harmony has to a discussion on the behaviour of for(in). It's a breaking change to the language's runtime semantics, but not to any syntax. It seems entirely analogous to the case of migrating for-in code from pre-Harmony into Harmony, and as I guessed last time, losing the global object looks strictly riskier in terms of unintended global property aliasing breakage. If we're saying that every new API and language feature being discussed for harmony will only be available in harmony, then yes harmony can do whatever it wants, We are not saying that because we do not want gratuitous differences for users or implementors. This is pretty clear from all our work. If we really wanted a totally new language, not only would it make for roughly 2x the learning curve for users, and lots of confused-mode bugs, it would make implementors do a bunch more work, approaching 2x. And as I also argued last week, TC39 would never pull it off. It would be design by committee and it would fail. So (in case this isn't obvious; I thought it was), we are trying to extend ES5 strict mode with only a few well-chosen runtime semantic shifts. Possibly few will be one: the global object removal in favor of lexical scope. New syntax brings new semantics of course. but you've also caused me to lose any interest in implementing harmony at that point. That's a straw man of your own devising, and I just knocked it down. Can we please get back to the crucial issues? If every feature being discussed is only usable inside harmony code then the migration cost of individual features won't be relevant as it's a distinct language with no impact on ES. There's still a migration tax in porting from old to new language, however similar they are. You yourself raised this re: ES5 strict, which changes runtime semantics without any syntactic change (eval-of-var, arguments). It is the same point I'm making about lexical scope removing the global object. I hope it is clear now, both as a risk and an opportunity!
Re: Nov 18 notes
On Nov 22, 2010, at 12:39 PM, Brendan Eich wrote: On Nov 22, 2010, at 12:09 PM, Oliver Hunt wrote: How do library authors help? They can't add value enumeration of anything as that will break any existing code that uses for(in) over any of their objects. As Tom pointed out (re-read his message :-|), they can make enumeration work for large/lazy/infinite objects. No non-string non-keys required. The important point here is that for-in won't choke old browsers. New library code in the near term (when Harmony and pre-Harmony impls are in the field), assuming we let for-in be metaprogrammed in Harmony, can object-detect and meta-program for-in, and client *and* library code can use for-in and it will fail soft or fall back by other means. Not so if we add new syntax (for-: or anything old browsers will choke on). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 11:49 AM, Brendan Eich wrote: On Nov 22, 2010, at 11:14 AM, Oliver Hunt wrote: And all existing standards compliant code can no longer rely on for in doing what it has done for years. Suddently for in _behaviour_ may change based on the prototype chain. That's true in Harmony, I think I misread your based on the prototype chain words. I was making the general case for runtime semantic fixes -- few and worth making -- in Harmony as in ES5 strict. But I think you were supposing a proxy on a non-proxy's prototype chain could start returning non-string non-keys via its iterate trap. Not so. Here's a session with a fresh js shell (Firefox 4 latest code): js var proxy = Proxy.create({ has: function () { return false; }, enumerate: function () { return [a, b, c]; }, iterate: function () { for (let i = 0; i 10; i++) yield i; } }); js js var obj = Object.create(proxy); js js for (var i in proxy) print(i); 0 1 2 3 4 5 6 7 8 9 js js for (var i in obj) print(i); a b c Once for-in starts up a non-proxy object's prototype chain, only enumerate traps -- never iterate. And enumerate is fundamental, so if missing, the for in fails: js var proxy = Proxy.create({ has: function () { return false; }, iterate: function () { for (let i = 0; i 10; i++) yield i; } }); js js var obj = Object.create(proxy); js js for (var i in proxy) print(i); 0 1 2 3 4 5 6 7 8 9 js js for (var i in obj) print(i); typein:11: TypeError: enumerate is not a function /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Colons and other annotative characters
On 11/22/10 10:13, Brendan Eich wrote: { foo : G : 33 } 0. let typedObj = { foo : 33 } : { foo : G }; // a la ES4 1. let typedObj = { foo :: G : 33 }; // the guards strawman 2. let typedObj = { (foo : G) : 33 }; // the ML-ish way 3. let typedObj = { foo @ G : 33 }; // funny cartoon chars Good list. Out of those five, the only one I'd find compelling would be the first: { foo : G : 33 } Alternatives 0, 1, and 3 have the problems that were already well-stated. 2 has strange placement of parentheses. If we're brainstorming, I'd throw out an alternative for the object initializer syntax: {name: value} // What we have now, for compatibility {name = value} // Identical behavior to {name: value} {name: type = value} // Adding a type annotation If we're looking at other ASCII characters for type annotations, a few other characters such as the backtick might be an option: {name`type: value} {name!type: value} {name%type: value} {name~type: value} Waldemar ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Colons and other annotative characters
I'd expect that o = { a : b = 5 } Was legal now, setting both o.a and b to 5. Not at a shell, is there an exception in the grammar for assignment expressions in the value position? { a = 5 : T } might work, though. Mike On Nov 22, 2010 6:09 PM, Waldemar Horwat walde...@google.com wrote: On 11/22/10 10:13, Brendan Eich wrote: { foo : G : 33 } 0. let typedObj = { foo : 33 } : { foo : G }; // a la ES4 1. let typedObj = { foo :: G : 33 }; // the guards strawman 2. let typedObj = { (foo : G) : 33 }; // the ML-ish way 3. let typedObj = { foo @ G : 33 }; // funny cartoon chars Good list. Out of those five, the only one I'd find compelling would be the first: { foo : G : 33 } Alternatives 0, 1, and 3 have the problems that were already well-stated. 2 has strange placement of parentheses. If we're brainstorming, I'd throw out an alternative for the object initializer syntax: {name: value} // What we have now, for compatibility {name = value} // Identical behavior to {name: value} {name: type = value} // Adding a type annotation If we're looking at other ASCII characters for type annotations, a few other characters such as the backtick might be an option: {name`type: value} {name!type: value} {name%type: value} {name~type: value} Waldemar ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Colons and other annotative characters
On Nov 22, 2010, at 3:08 PM, Waldemar Horwat wrote: On 11/22/10 10:13, Brendan Eich wrote: { foo : G : 33 } 0. let typedObj = { foo : 33 } : { foo : G }; // a la ES4 1. let typedObj = { foo :: G : 33 }; // the guards strawman 2. let typedObj = { (foo : G) : 33 }; // the ML-ish way 3. let typedObj = { foo @ G : 33 }; // funny cartoon chars Good list. Out of those five, the only one I'd find compelling would be the first: { foo : G : 33 } Alternatives 0, 1, and 3 have the problems that were already well-stated. 2 has strange placement of parentheses. Parens do hurt, I always thought so -- I find 2 the least evil of 0-3 though. If we're brainstorming, I'd throw out an alternative for the object initializer syntax: {name: value} // What we have now, for compatibility {name = value} // Identical behavior to {name: value} {name: type = value} // Adding a type annotation Isn't the last ambiguous with legal JS today (well, with const, but that's not important AFAICT): js const T = {} js function f(x) { return {p: T = x}; } js o = f(42) ({p:42}) ES5 strict mode is the basis for Harmony, so the assignment would be an early error, but are guard bindings required to be const? If we're looking at other ASCII characters for type annotations, a few other characters such as the backtick might be an option: {name`type: value} {name!type: value} {name%type: value} {name~type: value} Do you mean only for initialisers, or for all declarations. In a var x!T = v; example, he unary operator (! or ~) seems problematic here in light of ASI unless the production is restricted before the operator. % as mod has some metaphorical appeal but really, these all seem like Perlish line noise compared to colon. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
Rather than adding additional confusion by trying to comment on snippets from this thread, I think I'll just state my current opinions on the subject and the principles that they are based upon: 1a) New semantics should use new syntax in a manner that clearly avoids confusion with existing syntax. 1b) Syntax should generally be suggestive of a reasonable interpretation of the semantic 1c) Harmony is not being designed using the no new syntax rule 1d) There is nothing sacred about for as the initial keyword of an enumeration statement. From these I conclude that new iteration semantics should be syntactically distinct from the current for-in and probably the greater the syntactic distance from for-in the better. Following these principles, here is a suggestion for a new enumeration statement that makes use of existing reserved words: enum key with keys(x) { alert(key) } enum val with values(x) { alert(val) } enum [key, val] with properties(x) { alert(x. + key + = + val); } Since this is a new form, it can impose new syntactic conventions. For example, it always creates new bindings in the block that it iterates. An explicit var or let keyword is not needed. Also with has much broader semantic implications than in 2) Whenever possible, less general pre-existing syntactic forms should be redefined to desugar into new more general forms. ES1-5 for-in can be defined via desugaring to enum-with, for example: //desugar: for (var x in foo) {alert(foo[x])} var x; enum __x with __ES5forinEnumerator(x) { x=__x; {alert(foo[x])} } 3) Proxy traps should be defined based upon the new, more general semantics not legacy less general semantics. Define the traps necessary to support enum-with and depend upon the desugaring to take care of legacy for-in. 4) Provide builtin-library alternatives for new statements that can be used without down-rev syntax errors: Iterator.prototype.enumWith = function (func) { enum each with this { func(each) } } keys(x).enumWith(function(key){alert(key)}); values(x).enumWith(function(val){alert(val)}); properties(x).enumWith(function([key,val]){alert(x. + key + = + val)}); Leave it to library writers as to whether or not down-rev libraries are actually implemented. -Original Message- From: Brendan Eich Sent: Monday, November 22, 2010 12:48 PM To: Oliver Hunt Cc: es-discuss Subject: Re: Nov 18 notes On Nov 22, 2010, at 12:39 PM, Brendan Eich wrote: On Nov 22, 2010, at 12:09 PM, Oliver Hunt wrote: How do library authors help? They can't add value enumeration of anything as that will break any existing code that uses for(in) over any of their objects. As Tom pointed out (re-read his message :-|), they can make enumeration work for large/lazy/infinite objects. No non-string non-keys required. The important point here is that for-in won't choke old browsers. New library code in the near term (when Harmony and pre-Harmony impls are in the field), assuming we let for-in be metaprogrammed in Harmony, can object-detect and meta-program for-in, and client *and* library code can use for-in and it will fail soft or fall back by other means. Not so if we add new syntax (for-: or anything old browsers will choke on). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Colons and other annotative characters
On 11/22/10 15:16, Brendan Eich wrote: {name: type = value} // Adding a type annotation Isn't the last ambiguous with legal JS today (well, with const, but that's not important AFAICT): Yeah, you're right. This won't work. Waldemar ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
1a) New semantics should use new syntax in a manner that clearly avoids confusion with existing syntax. 1b) Syntax should generally be suggestive of a reasonable interpretation of the semantic 1c) Harmony is not being designed using the no new syntax rule 1d) There is nothing sacred about for as the initial keyword of an enumeration statement. Nobody said sacred -- I'm not genuflecting. :) Seriously, the reason for using for is that it's one of the most stable, common, and universally-used keywords for iteration in almost all languages. Introducing a new initial keyword is straying really far from precedent, both in JS and other imperative languages. But I appreciate your spelled-out premises. I think my main quibble is with 1a as a rule. I might prefer something like: 2a) If existing syntax is given new semantics, it should extend the existing semantics conservatively. Otherwise, the new semantics should get new syntax. From these I conclude that new iteration semantics should be syntactically distinct from the current for-in and probably the greater the syntactic distance from for-in the better. Following these principles, here is a suggestion for a new enumeration statement that makes use of existing reserved words: enum key with keys(x) { alert(key) } This is clever, but it just seems to go off the deep end: the syntax is too inconsistent with JS precedent. Also, enum is the wrong keyword -- in JS parlance, this is iteration not enumeration. I guess I'm still open to new syntaxes, but I also still feel that when you step back and weigh the trade-offs, the cost of all this new syntax is incommensurate with the amount of new semantics, and moreover the traditional for-in syntax is still the sweetest I've seen for custom iteration. I would rather extend the best syntax and leave the legacy special case as a very small wart than have a warty syntax with a supremely orthogonal semantics. 2) Whenever possible, less general pre-existing syntactic forms should be redefined to desugar into new more general forms. I think this is pretty uncontroversial; whatever syntax we decide on, the specific legacy construct can be defined in terms of the more general new construct. 3) Proxy traps should be defined based upon the new, more general semantics not legacy less general semantics. Define the traps necessary to support enum-with and depend upon the desugaring to take care of legacy for-in. You don't think for-in should even allow the enumerate trap? This seems to go against the design approach of proxies; it's not just for introducing new meta-programmable constructs, but also for meta-programming existing facilities. 4) Provide builtin-library alternatives for new statements that can be used without down-rev syntax errors: This seems like a good idea. Dave ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
2a) If existing syntax is given new semantics, it should extend the existing semantics conservatively. Otherwise, the new semantics should get new syntax. Perhaps I should have numbered that 1a'). :) Dave ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Colons and other annotative characters
I think there is too much fear of changing the semantics of for-in. Its semantics is already rather fuzzy. Allowing meta-programmability of for-in (instead of creating a new for-:) syntax doesn't create any additional surprises or hazards for JS programmers. 1. We have already agreed on an enumerate() hook which is triggered by traditional for-in. It returns a list of strings, but those are not necessarily keys. 2. enumerate() can contain arbitrary JS code, so for-in can already trigger all sorts of random behavior, and people seem to be comfortable with it. 3. iterate() is a natural extension of enumerate(). As enumerate(), it should be triggered by for-in. The only difference to the proxy enumerate trap is that iterate() doesn't eagerly force the creation of the list of values to be returned, which may not be possible for very large meta objects (databases, i.e.). Its seems entirely random to cut off between 2) and 3). We are fine with enumerate(), but iterate() is somehow evil? The only see 2 semantic differences between the outcomes of enumerate() and iterate(). a) enumerate guarantees that all values are strings b) enumerate guarantees that all strings are unique So it seems that some people feel that unique strings is an appropriate approximation for keys. There isn't much I can say about a). I think it would be more flexible and future-proof to allow non-string return values from iterators, but if we have to stringify non-string values returned from iterators, I could live with that. As for b), the guarantees for it are already extremely weak. With proxies one can already today write code that breaks the uniqueness assumption of for-in. function visit_keys(obj, f) { for (i in obj) f(i); } visit_keys(obj, function(i) { delete obj.i; obj.i = 1; }) From my understand of the spec, a spec-conformant implementation is allowed to endless-loop on this code, continuously returning the same property name. So why do we cling to uniqueness where it doesn't exist in the first place? Executive summary: iterate() is equivalent to enumerate(), both should work with for-in. We already agreed to accept enumerate, forcing iterate() into its own syntax is arbitrary. We can discuss sacrificing future-proofness and stringify return values of iterators, but rejecting iterators because they don't guarantee unique strings is bogus--that uniques doesn't exist elsewhere either. Andreas On Nov 22, 2010, at 3:40 PM, Waldemar Horwat wrote: On 11/22/10 15:16, Brendan Eich wrote: {name: type = value} // Adding a type annotation Isn't the last ambiguous with legal JS today (well, with const, but that's not important AFAICT): Yeah, you're right. This won't work. Waldemar ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 3:17 PM, Allen Wirfs-Brock wrote: Rather than adding additional confusion by trying to comment on snippets from this thread, I think I'll just state my current opinions on the subject and the principles that they are based upon: 1a) New semantics should use new syntax in a manner that clearly avoids confusion with existing syntax. 1b) Syntax should generally be suggestive of a reasonable interpretation of the semantic 1c) Harmony is not being designed using the no new syntax rule 1d) There is nothing sacred about for as the initial keyword of an enumeration statement. I will echo Dave in saying thanks for spelling out principles. Also that sacred is a bit one-sided -- I do not think your (1a) is sacred either. Are we even? :-| The forest-for-the-trees meta-point is that if we conservatively add new syntax when meta-programming existing would have gone over without incident, we've made the language irrevocably bigger. We can't ever take back the new syntax or get rid of the old. We will have a long wait switching developers over to the new, which has ripple effects as Tom's post pointed out. But no need to beat that drum again, I will stifle. From these I conclude that new iteration semantics should be syntactically distinct from the current for-in and probably the greater the syntactic distance from for-in the better. Following these principles, here is a suggestion for a new enumeration statement that makes use of existing reserved words: enum key with keys(x) { This is a clever homage to with :-P. The parenthesis-free head is unusual and invites Go-like innovation elsewhere: will Harmony drop mandatory parens around if, while, etc. heads? Iteration is not numbering or naming, so taking enum for this purpose promulgates a misnomer *and* precludes us using enum for a categorical sum declaration of some sort. 2) Whenever possible, less general pre-existing syntactic forms should be redefined to desugar into new more general forms. ES1-5 for-in can be defined via desugaring to enum-with, for example: //desugar: for (var x in foo) {alert(foo[x])} var x; enum __x with __ES5forinEnumerator(x) { x=__x; {alert(foo[x])} } This goes against the Proxy design. A proxy shouldn't have to special case __ES5forinEnumerator to customize for-in as opposed to enum-with, and customizing for-in should not affect enum-with when up the proto chain as one of my recent posts showed with a js shell session. If we must have new syntax to get customized iteration through TC39, we can spend way too much time coming up with acceptable syntax. But before we dive into that fun timekilling process, let's be really sure we are not missing the forest for the trees. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Colons and other annotative characters
On Nov 22, 2010, at 4:07 PM, Andreas Gal wrote: Executive summary: iterate() is equivalent to enumerate(), both should work with for-in. We already agreed to accept enumerate, forcing iterate() into its own syntax is arbitrary. We can discuss sacrificing future-proofness and stringify return values of iterators Stringify return values of iterators *only* when for-in is driving the iteration (I'm sure you meant ;-). I somehow suspect stringifying the iterator next() return value from for-in machinery will not placate folks who want for-in not to be metaprogrammable. But harmony:proxies is already spec'ed with an enumerate trap. Something does not add up. You're right that the line between your items (2) and (3) is arbitrary. BTW, I found a v8 implementation of proxies by Sam Shull, via TomVC's site: https://github.com/brickysam26/node-proxy/tree/master/src/ Thought I'd pass that on along to es-discuss to make up for the usual interminable iteration arguments :-P. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Colons and other annotative characters
I somehow suspect stringifying the iterator next() return value from for-in machinery will not placate folks who want for-in not to be metaprogrammable. Nor would it work -- you wouldn't be able to get the values() or properties() iteration behavior, for example. It would be the worst of all possible compromises. But harmony:proxies is already spec'ed with an enumerate trap. Something does not add up. You're right that the line between your items (2) and (3) is arbitrary. Agreed. Dave ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
-Original Message- From: David Herman Sent: Monday, November 22, 2010 3:55 PM To: Allen Wirfs-Brock Cc: Brendan Eich ; Oliver Hunt ; es-discuss Subject: Re: Nov 18 notes 1a) New semantics should use new syntax in a manner that clearly avoids confusion with existing syntax. 1b) Syntax should generally be suggestive of a reasonable interpretation of the semantic 1c) Harmony is not being designed using the no new syntax rule 1d) There is nothing sacred about for as the initial keyword of an enumeration statement. Nobody said sacred -- I'm not genuflecting. :) Seriously, the reason for using for is that it's one of the most stable, common, and universally-used keywords for iteration in almost all languages. Introducing a new initial keyword is straying really far from precedent, both in JS and other imperative languages. AWB: Perhaps I should have been a bit more general in 1d and said something like: respect precedent, except when it gets in the way. Precedent can mislead users as easily as it can help help them. One think I suspect that we have all observed (and probably experience) is the experienced programmers who starts trying to write JavaScript programmers using invalid assumptions based upon their experience with other languages. But I appreciate your spelled-out premises. I think my main quibble is with 1a as a rule. I might prefer something like: 2a) If existing syntax is given new semantics, it should extend the existing semantics conservatively. Otherwise, the new semantics should get new syntax. AWB: Thanks, I like that. Trying to articulate some principles was the main point. I felt the thread was feeling like it getting hung up on unstated and/or unshared prinicples. From these I conclude that new iteration semantics should be syntactically distinct from the current for-in and probably the greater the syntactic distance from for-in the better. Following these principles, here is a suggestion for a new enumeration statement that makes use of existing reserved words: enum key with keys(x) { alert(key) } This is clever, but it just seems to go off the deep end: the syntax is too inconsistent with JS precedent. Also, enum is the wrong keyword -- in JS parlance, this is iteration not enumeration. AWB: This was mainly a first cut trying to look at the problem from a slightly different angle but starting by only using the currently available set of reserved words. Other syntactic issues like usage of parenthesis could tweaked and in the end wouldn't seem particularly inconsistent with the syntax conventions for C-like languages. I agree that we have generally called this semantics iteration rather than enumeration, but I was working from out available set of reserved words and enum seems like the closest fit. I suspect that the majority of Javascript programmers couldn't really articulate the difference between enumeration and iteration (over). They will use whatever meaning we feed them via the language design. Of course, we should be consistent in our terminology and if we went this route we might want to adjust other uses of these terms. Also, it there are JS 1.8 precedent issues--I'm just not sure whether that usage is broad enough to matter that much. I guess I'm still open to new syntaxes, but I also still feel that when you step back and weigh the trade-offs, the cost of all this new syntax is incommensurate with the amount of new semantics, and moreover the traditional for-in syntax is still the sweetest I've seen for custom iteration. I would rather extend the best syntax and leave the legacy special case as a very small wart than have a warty syntax with a supremely orthogonal semantics. AWB: I worry that it is hard for people to learn when the same name is applied to different concepts. We already have two forms of the for statement that people have to learn. Further complicating it feels like it would be a pedagogical nightmare. 2) Whenever possible, less general pre-existing syntactic forms should be redefined to desugar into new more general forms. I think this is pretty uncontroversial; whatever syntax we decide on, the specific legacy construct can be defined in terms of the more general new construct. 3) Proxy traps should be defined based upon the new, more general semantics not legacy less general semantics. Define the traps necessary to support enum-with and depend upon the desugaring to take care of legacy for-in. You don't think for-in should even allow the enumerate trap? This seems to go against the design approach of proxies; it's not just for introducing new meta-programmable constructs, but also for meta-programming existing facilities. I haven't really studied the specific Proxy traps in detail. However, if we had a more generate iterate trap(s) that semantically subsumes the enumerate trap when used with appropriate desugaring, why would we need an
Re: Nov 18 notes
-Original Message- From: Brendan Eich Sent: Monday, November 22, 2010 4:10 PM To: Allen Wirfs-Brock Cc: Oliver Hunt ; es-discuss Subject: Re: Nov 18 notes On Nov 22, 2010, at 3:17 PM, Allen Wirfs-Brock wrote: ... enum key with keys(x) { This is a clever homage to with :-P. AWB: actually, when I made this up I was a bit concerned using with because of its heritage. I got over it... The parenthesis-free head is unusual and invites Go-like innovation elsewhere: will Harmony drop mandatory parens around if, while, etc. heads? AWB: Since what comes immediately after enum is a declaration from there isn't any precedent for parens there. They could be added after the with if that felt more familiar. Iteration is not numbering or naming, so taking enum for this purpose promulgates a misnomer *and* precludes us using enum for a categorical sum declaration of some sort. AWB: See my reply to Dave Herman on this issue. 2) Whenever possible, less general pre-existing syntactic forms should be redefined to desugar into new more general forms. ES1-5 for-in can be defined via desugaring to enum-with, for example: //desugar: for (var x in foo) {alert(foo[x])} var x; enum __x with __ES5forinEnumerator(x) { x=__x; {alert(foo[x])} } This goes against the Proxy design. A proxy shouldn't have to special case __ES5forinEnumerator to customize for-in as opposed to enum-with, and customizing for-in should not affect enum-with when up the proto chain as one of my recent posts showed with a js shell session. AWB: Perhaps and we have other use cases of property name enumeration (Object.key and getOwnPropertyNames, the in operator) that may need the enumerate trap. I know that trap design was one of the starting points of this discussion so I probably should look more closely at this. If we must have new syntax to get customized iteration through TC39, we can spend way too much time coming up with acceptable syntax. But before we dive into that fun timekilling process, let's be really sure we are not missing the forest for the trees. AWB: For what it's worth, in the Northwest we have a long tradition of cutting down trees and then latter realizing that we don't have a forest any more... /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 5:39 PM, Allen Wirfs-Brock wrote: AWB: Thanks, I like that. Trying to articulate some principles was the main point. I felt the thread was feeling like it getting hung up on unstated and/or unshared prinicples. I agree, and I try to make mine clear (let me know where I fail). It's important not only for reaching agreement (backing up and trying a different deductive path forward) but also in rejecting bogus premises, or reprioritizing. AWB: I worry that it is hard for people to learn when the same name is applied to different concepts. We already have two forms of the for statement that people have to learn. The C-based for(;;) loop is not really relevant in my experience. JS hackers use it (more below on why they might rather not), but they always distinguish it from for-in. Some good fraction of JS hackers know Python, where for-in has enumeration like behavior on dictionaries (just like JS objects), but value iteration on lists. The most frequent (in my hearing) unsatisfied expectation of users of for-in is not that it is a second form after the C-based for(;;) -- there's no mention of for(;;). Rather, it is that for (v in [1,2,3]) fails to iterate 1,2,3, instead enumerating 0,1,2. We can't satisfy this Pythonic expectation directly, but with meta-programmable for-in, users and library authors can help themselves, using keys, values, and other iterator factory functions. Further complicating it feels like it would be a pedagogical nightmare. I think this is exaggerated -- there's really no relation to for(;;), and the first-year students move up and on. The nightmare of unprogrammable for-in, if I may use that word, is both the non-interoperable mess of for-in today in ES3-ES5-level browsers, plus the enumerate trap of tomorrow -- never mind an iterate trap. But most programmers do not need to know about proxies. So weigh the nightmares by how many students reach JS grad school, where they learn about proxies at all. And give some weight to middling students of the future libraries, who will be able to for-in over array and other array-like objects' values. The C-style for (var i=0; i a.length; i++) { var v = a[i]; ... } alternative-nightmare would be vanquished by the dawn of for (var v in values(a)) { ... }. This is all kind of fluffy, on both sides of our argument. Are we really concerned about cognitive load of for (;;) vs. for-in? That ship sailed long ago and I argue it's as likely that meta-programmable for-in heals the rift, as it is that iterate on top of enumerate makes a new rift. /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
The most frequent (in my hearing) unsatisfied expectation of users of for-in is not that it is a second form after the C-based for(;;) -- there's no mention of for(;;). Rather, it is that for (v in [1,2,3]) fails to iterate 1,2,3, instead enumerating 0,1,2. We can't satisfy this Pythonic expectation directly, but with meta-programmable for-in, users and library authors can help themselves, using keys, values, and other iterator factory functions. Library authors can't really do this without providing their own array classes, modifying the array prototype would be too dangerous in a web environment to be done by a library as it would not work with other code that may expect current for-in behaviour, same applies to NodeList's, etc in the DOM. This is another reason i'm in favour of a different syntax - we could make it iterate values as people expect. Further complicating it feels like it would be a pedagogical nightmare. I think this is exaggerated -- there's really no relation to for(;;), and the first-year students move up and on. The nightmare of unprogrammable for-in, if I may use that word, is both the non-interoperable mess of for-in today in ES3-ES5-level browsers, plus the enumerate trap of tomorrow -- never mind an iterate trap. But most programmers do not need to know about proxies. I am most concerned about this making for-in much more complicated; Yes python has had meta-programmable objects forever (give or take a few years :D) which shows people can understand the behaviour, but the default behaviour exhibited in python is different, and more in line with what people expect. By changing for(in) behaviour in harmony you're trying to give python like meta-programming but with less than ideal defaults for new developers, and confusion for experienced developers. I think the other problem i have is that what people really seem to want is value enumeration of arrays (and array-likes), and by re-using the syntax you basically ensure that at the very least arrays (and other array-likes) will never get that behaviour by default. In addition to that it would be very dangerous for any library to override the iterate trap as libraries want to interact well with other libraries, and also provide a consistent development environment across multiple engines and versions. That leads me to expect that libraries would not override the enumerate trap. If we had a new syntax, in addition to allowing meta-programming from the get-go, we could also provide default enumerate traps for arrays, etc that is inline with what people expect. --Oliver ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 7:07 PM, Oliver Hunt wrote: The most frequent (in my hearing) unsatisfied expectation of users of for-in is not that it is a second form after the C-based for(;;) -- there's no mention of for(;;). Rather, it is that for (v in [1,2,3]) fails to iterate 1,2,3, instead enumerating 0,1,2. We can't satisfy this Pythonic expectation directly, but with meta-programmable for-in, users and library authors can help themselves, using keys, values, and other iterator factory functions. Library authors can't really do this without providing their own array classes, modifying the array prototype would be too dangerous Modifying the array prototype isn't relevant here (proxies on the prototype do not iterate, they enumerate -- see prior mail with js shell session excerpts). Not sure what you mean if not this -- adding methods to Array.protoype? PrototypeJS does this. in a web environment to be done by a library as it would not work with other code that may expect current for-in behaviour, same applies to NodeList's, etc in the DOM. I think you're arguing against something not proposed. for (var v in values(a)) ... requires no prototype monkeypatching. Somehow inserting a proxy with a handler having an iterate trap at the front of the Array.prototype chain won't work as noted already. If you thought I meant this: for (var v in [1,2,3]) print(v); printing 1 2 3, as I wrote in words cited at the top of this message We can't satisfy this Pythonic expectation directly. Something like values would need to be called around the [1,2,3]. This is another reason i'm in favour of a different syntax - we could make it iterate values as people expect. I'm sure you are in favor of different syntax, but the argument you just made doesn't support that and it seems to have been based on a misunderstanding. Further complicating it feels like it would be a pedagogical nightmare. I think this is exaggerated -- there's really no relation to for(;;), and the first-year students move up and on. The nightmare of unprogrammable for-in, if I may use that word, is both the non-interoperable mess of for-in today in ES3-ES5-level browsers, plus the enumerate trap of tomorrow -- never mind an iterate trap. But most programmers do not need to know about proxies. I am most concerned about this making for-in much more complicated; Yes python has had meta-programmable objects forever (give or take a few years :D) which shows people can understand the behaviour, but the default behaviour exhibited in python is different, and more in line with what people expect. By changing for(in) behaviour in harmony you're trying to give python like meta-programming but with less than ideal defaults for new developers, and confusion for experienced developers. Let's not argue by asserting who will be confused. I've already replied to Allen that the sword has two edges, one pointed away from skilled programmers. It's simply selective arguing to say only bad things happen in one direction. You can get cut; you can cut someone not intended. The issue is not whether for-in should be meta-programmable -- Proxy has an enumerate trap -- but how much. I think the other problem i have is that what people really seem to want is value enumeration of arrays (and array-likes), and by re-using the syntax you basically ensure that at the very least arrays (and other array-likes) will never get that behaviour by default. If we wanted to change Harmony's runtime semantics to be even less compatible than it is with no global object, we could wave a spec-wand and do that. Why don't we? The reasons won't go away. The best we can do in any mostly-compatible spec edition is add new forms, whether syntax or API, and see if the cows beat new paths. Dave's early message then observed that if all the developer cows happily use the new iteration form, the old one is basically dead. Reforming for (var v in [1,2,3]) at that very late date to iterate over values, assuming such a compatibility break is acceptable, simply won't matter by the premise: the cows moved to the new field. In addition to that it would be very dangerous for any library to override the iterate trap as libraries want to interact well with other libraries, You can't override the iterate trap. Handlers are stratified and encapsulated. This is basic to the harmony:proxies design. and also provide a consistent development environment across multiple engines and versions. That leads me to expect that libraries would not override the enumerate trap. Do you mean wrap a proxy with a different proxy having a different enumerate or iterate trap? Wrapping is not overriding. No mutation, different object identities. I'm worried we are talking past each other now. If we had a new syntax, in addition to allowing meta-programming from the get-go, we could also provide default enumerate traps
`static` keyword from C/C++ as own closured var declaration
// es3 way (function() { var x = 1; return function() { return ++x; } })(); // current es6/SM1.8 way let(x = 1) function() { return ++x; } // new more readable sugar function() { static x = 1; // hello c/c++ return ++x; } Implementation, when compiling source code, just collects 'static' vars from scope and wraps current scope to closure scope with collected vars ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 21, 2010, at 7:05 PM, Brendan Eich wrote: On Nov 18, 2010, at 4:08 PM, Waldemar Horwat wrote: Consensus that we should have iterators. For this, after all these years (JS1.7 added meta-programmable for-in in 2006), I'm grateful, although I wanted to add something your notes did not report: To get consensus, we made a tentative agreement to leave for-in as it was and not enable any meta-programmability of it, consigning it to the historically-underspecified and non-interoperable enumeration mystery-meat status. Instead, we did the committee-plays-it-safe thing of inventing new syntax for meta-programmable iteration: for (var i : x) ... This is a break from Python and JS1.7+ in SpiderMonkey and Rhino -- although it matches very late Java and C++ extensions that are similar (but not the same), and really not relevant to JS. Worse, the use of colon in this new for syntax is confusingly similar to long-standing future-proofing intentions around runtime type annotations (aka guards or contracts). (BTW, I don't think :: is a good type annotation or guard punctuator, btw -- it's the C++ namespace qualification operator, also specified for namespacing by ECMA-357 (E4X) and used that way in ActionScript 3 (and in ES4, RIP). So I was surprised to see :: used for annotation-like syntax in http://wiki.ecmascript.org/doku.php?id=strawman:guards and http://wiki.ecmascript.org/doku.php?id=strawman:trademarks.) for (var i : x) ... // must be new iteration for (var i : T : x) ... // iteration again, but parsed how? for (var i : T in x) ... // for-in with annotated var Depending on what T might be, grammatically, this could get sticky for top-down parsers. It is confusing and ugly in any event, IMHO. Probably we need to take our time and not rush into a meta-programming-here syntax variant of for-in. I'll not propose anything better right now. If the colon is less future-compatible than we like, why not: foreach (var i in x) While for is slightly more common for a loop that iterates a container, foreach is also quite common, and indeed is often used to name the construct even in languages where the keyword is spelled for: http://en.wikipedia.org/wiki/Foreach. This would allow us to reserve the colon, and also avoid a surprising change to the semantics of the existing for..in construct. It could even DTRT for arrays. Also, it will likely be easier for programmers to talk about the distinction between for..in loops and foreach..in loops than for..in loops and for..colon loops. Regards, Maciej ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Nov 18 notes
On Nov 22, 2010, at 11:19 PM, Maciej Stachowiak wrote: Probably we need to take our time and not rush into a meta-programming-here syntax variant of for-in. I'll not propose anything better right now. If the colon is less future-compatible than we like, why not: foreach (var i in x) All the new words are not reserved, so they could begin a function call expression in extant code: hi = there foreach (i in x) print(i) means hi = there; foreach(i in x); print(i); today. Same if you s/foreach/iterate/ or any non-reserved identifier. Wherefore Allen's co-opting of enum (plus with, a decent preposition given enum but 8 chars in two keywords hurt kittens everywhere). Anyway, the bikeshed is secondary. We need to agree on what meta-programmable for-in means with the enumerate trap (specified by the wiki pages on harmony:proxies), how that changes with the optional iterate trap (strawman:iterators), and when it might matter (for all for-in loops, or only those in Harmony code?). /be ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss