Nov 18 notes
brendan at mozilla.com
Mon Nov 22 12:39:03 PST 2010
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!
But it was never an opportunity to make a totally new and different 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 don't understand why : was a mistake, the only counter argument i saw was that it didn't work well with type annotations,
No, you just replied to Tom's arguments against for-: that did not depend on annotation syntax. That is another counter-argument, and you must have read it since you replied directly to his message.
> which i see no value in and aren't being discussed for harmony or es-next.
Mark is putting guards up as a candidate proposal.
> 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.
Guards, not types. Mark is right to want to avoid anything connoting static type.
>> 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, splitting for-in and for-: by dynamic type of what is on the right-hand side of in vs. :, complicating the surface language, and precluding library authors from helping in the near term.
> 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.
> And given the desire that the library writers have to make their libraries paper over browser differences they're not going to introduce changes that completely change object behaviour from one engine to the next.
That's a nice argument for not changing anything, but of course (as I keep saying ad nauseum here, since it is a perniciously selective argument), we are designing for the longer run. ES1 came after original JS; ES2 next; ES3 soon enough; long stretch of de-facto standards; then ES5. Progress happens, and we are planning on it and participating in it, not just giving up or trying to freeze things at some point.
> If there were a separate syntax, they could provide a custom iterator, and tell devs it was there, so devs would be able to use the nicer enumeration feature when they know that the syntax is available.
That's not what I meant by "near term", since the near term includes browsers that don't grok the new syntax. The use-cases of large/lazy/infinite proxies exist in all time frames, short and long run.
>>> If this was going to be the outcome why did we even spend time discussing this?
>> Because we were trying to reach agreement. Who says failure is not an option? Failure is important. It also is not permanent. Making this into a one side must win and the other must lose game would be a mistake. The game is still on, and there can be more winners than losers.
>> I said at the meeting that we couldn't prove someone wouldn't mix Harmony and non-Harmony code and objects, and have non-Harmony code run for-in over a proxy with an iterate trap, and find some kind of surprise.
>> This is the down side. It is not so obviously terrible and fatal that we all agree to abandon meta-programmability of for-in. It's an unquantified risk, probably small -- greater than the ES5 strict migration tax risks (eval of var, arguments aliasing, caller/callee) and less than the Harmony lexical scope all the way up (no global object on scope chain) risks, in my best guess.
>> Suggestion: we make old for-in loops never run the proxy iterate trap. Only for-in code that opts into Harmony (analogous to "use strict" changing eval-of-var and arguments) gets the meta-programmability. What say you?
> I don't believe harmony-only is reasonable, it would mean that for a developer to use just the new enumerator they would have to rewrite everything to deal with the now missing global object.
That's not true in general. I can write for (k in keys(o)) ... in Harmony without rewriting everything to avoid keys being redefined if I'm using only code I control or know well.
So why is it an absolute -- why is it not "reasonable", ever, period full stop?
Migrating into Harmony, losing one's dependencies on the global object with early errors from the lexical scope checker built into the JS compiler, could be well worth the trouble.
Likewise, migrating to a meta-programmable for-in could be much better than having to rewrite to use new syntax, teach it to users, implement it, standardize it, wait for it to be deployed, etc.
These are trade-offs, not absolutes.
You write about what you "expect" and "believe", but that's no argument, it's an statement of your expectation or belief. It's circular unless you're arguing from authority.
We have to reason together, debate technical merits and demerits, argue about big picture and little, not lose the forest for the trees. Just saying "no" doesn't lead anywhere, and if you can say "yes" to lexical scope all the way up, then I think you're not being consistent.
> And once again, i think using "harmony" as a mode switch to change behaviour of things unrelated to lexical scoping is the wrong approach.
Can you please say why it's "wrong".
Why was lexical scope the only possible change?
From that global object removal a bunch of new issues arise around the standard library and modules. It's not a simple "just this one [big] change" and anyway, we do not have agreement to stop all further evolution there.
I proposed meta-programming for-in code that opts into Harmony, to address your specific concern about a proxy with an iterate trap on its handler leaking into pre-Harmony for-in code and being evaluated on the right of "in".
You responded by arguing that we don't want to change runtime semantics apart from removing the global object.
I reply that this is arbitrary and incomplete. We still have a world of trade-offs. We can only get so many right in committee and for the next edition.
Lexical scope is a big trade-off (semantics-breaking change); we seem to agree on that being worthwhile.
The committee already agreed that large/lazy/infinite objects are another, and that needs to be resolved for proxies, which are already in harmony:proposals status.
The solution offered for large/lazy objects (dropping infinite, lazy covers it better) is iterators. There's no counterproposal. The iterators proposal makes for-in metaprogarmmable, which leads directly to the concern about old for-in code being given a proxy.
I've addressed that with a quick "opt-in" solution. I really don't want to goto step 1 under Recap: and iloop. To avoid that, I think it's incumbent on you to give an argument, not just an assertion of belief.
I value your opinion, I'm sorry if I sound mean or pushy. If you don't want to make that argument, someone else who agrees with you may, or we'll just have to soldier on somehow. But since we came this far, I hope you can rally and respond to my query by saying *why* even opt-in-only for-in metaprogrammability is the "wrong approach".
More information about the es-discuss