-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Normative: Evaluate all computed names before any values in object literals #945
Conversation
…terals To make object literals consistent with decorator and field proposals, Brian Terlson and Yehuda Katz proposed in the object evaluation order proposal that computed property names in object literals be evaluated before any of the right-hand sides. This patch implements that proposal. The specification is organized by making PropertyDeclarationEvaluation return a List of "fields", which for object literals, means key-value pairs which are not methods Methods are excluded because they are evaluated in a way which cannot have side effects or observe the world.
Could you provide an example where this would currently be observable? |
@ljharb See tc39/test262#739. |
Thanks, so in that test (which currently asserts 1,2,3,4,5,6), it'd need to change to assert 1,4,2,5,3,6? |
I would hope not many people are doing side effects with their computed expressions, but... wow, this new proposed behavior looks super confusing to my eyes -- IIUC, to compute the whole set of property name expressions up-front before consulting their value assignments individually in order. In the linked test, As an aside, another convenient way to illustrate the surprise of the proposed order is: o = {
[console.log(1)]: console.log(2),
[console.log(3)]: console.log(4),
[console.log(5)]: console.log(6)
}; Shown that code, I'd bet 999 out of 1000 JS developers will expect Whatever (valid, I'm sure) reason there is for proposing this (potentially breaking) change, I wonder if this unintuitive outcome is worse than whatever inconsistency will result from preserving the current behavior. |
By the way, the proposal-class-fields specification is based on this PR; I meant to upload it a couple months ago but seem to have forgotten somehow. @getify This proposal is part of a larger program, proposed by Yehuda and Brian, where it turns out that class declarations cannot be evaluated strictly left-to-right, top-to-bottom for several reasons once there are decorators and/or static public fields with computed property names. In that context, the hope is that these semantics would be simpler to understand as a whole, not more complicated. |
@ljharb That is correct. |
@getify The way it is presented to developers produce bias against expectations:
I believe this is a reasonable breaking change, as some are pragmatic and opens a good path. This change affects class fields positively. BTW, Safari does not conform with the current spec or even with the current changes. |
Let's get this on the agenda for July! |
EDIT: OK, I can't read. Here's the eshost output:
|
@leobalter I'd say that object destructuring already strongly "presented" this assumption to developers. Destructuring works left-to-right, then top-down. I (and others) literally teach destructuring as just the declarative form of the individual assignments happening, in order. And right or wrong, that's the mental model I've also used to think about computed properties. This proposal will mark a significant divergence between those two, and that's the thing I think that's a bad idea. And this proposal appears to only be doing so in an effort to make more exotic use cases like decorators work. IMO we shouldn't be prioritizing a more sophisticated use-case over a more direct/straightforward one. If decorators can't work in a way that's consistent with how an established feature like destructuring works, then maybe that's a sign that decorators are ill-conceived and need more thought -- not just casually breaking the established mental precedent. |
@littledan I think object literals are much more closely aligned, mental-model wise, with destructuring than are class declarations. Class declarations are a different beast and already break several precedents, like for example not needing/allowing intervening commas between items, etc. So, I personally think if the inconsistency needs to happen, it should happen with the form that's already different than on one which is so closely similar. |
BTW, to elaborate slightly on my assertion earlier about the relationship to destructuring (which I implied but didn't explain): let x = 1;
let {
[++x]: y = ++x,
[++x]: z = ++x
} = { 1: 1, 3: 3, 4: 4 };
x; // 4
y; // 3
z; // 4 This is thought of and taught -- most commonly, I'd wager -- as syntactic sugar for: let x = 1;
let o = { 1: 1, 3: 3, 4: 4 };
let tmp = o[++x];
let y = tmp != undefined ? tmp: ++x;
let tmp2 = o[++x];
let z = tmp2 != undefined ? tmp2 : ++x;
x; // 4
y; // 3
z; // 4 In other words, each destructuring pattern is processed top-down, one at a time, and within each, left-to-right, exactly as if each is its own statement. That syntax is unmistakably similar to object literals, and certainly on purpose as such. So when you do something almost the same in an object literal, it's not a stretch of the imagination at all to relate the two and have similar expectations: let x = 1;
let y;
let z = 3;
let o = {
[++x]: y != undefined ? y : ++x,
[++x]: z != undefined ? z : ++x
};
o; // { 2: 3, 4: 3 } If you went and started messing with object literals' computed properties order, according to this proposal, but didn't change how destructuring works, you break that relationship in ways I find quite surprising. |
@littledan @getify note that one reason that classes are different is that they add an additional time dimension to left-right, top-bottom. It is at class definition time or at instance construction time. |
Just dropping some more scenarios here to consider based on TC39 discussions. let user, profile = {
user: user = {
name: 'Sebastian',
},
[user.name]: 'Name'
}; let {
user: {
name
},
[name]: label
} = profile; let i = offset, {
[i++]: {
[i++]: a
},
[i++]: {
[i++]: b
}
} = nestedMatrix; |
We discussed this PR at the July 2017 TC39 meeting, but we didn't come to any sort of consensus. There seemed to be a bunch of support, but also a bunch of concern about losing the ordering (as discussed in this thread). Do people here think it's worth continuing with this proposal and bringing it up for further discussion in the September meeting? |
My understanding is that we got consensus on these semantics in Munich so the lack of "consensus" is around a delta from this PR not the PR itself. This PR is waiting on learning from implementations if this is web-compatible. However, there were two possible tweaks raised to this PR that as-yet have not been fleshed out:
|
I'm not really a fan of either of those tweaks. If we think people will care about evaluation order here at all, the worst thing we could do is make it subtly context-dependent. |
We're continuing to discuss class decorators in TC39, though object decorators are left for a follow-on proposal. Where should we go from here on this patch? Should we make this change? |
I haven't yet seen any response from implementers in this thread and I think for this case we definitely want to know how this effects VMs. And, because methods are unaffected by this PR, we also need to consider the changes on property iteration order this PR will cause. |
@anba Thanks so much for taking a close look at this patch. It was an unintentional bug to exclude methods in object literals here. Due to the organization of the specification, it'll be a somewhat bigger change to treat methods similarly, but the text should be similar to what's found in the private methods proposal. It'll be a few weeks until I can take the time to write all this out. If anyone wants to take on this exercise, which can help this proposal move forward more quickly, you'd be more than welcome. Let me know here if you have any questions. |
A couple years after I wrote this, we haven't discussed this PR in committee again, and it hasn't been implemented. I think it might've been web-compatible to make these sorts of changes in the past, but this window is closing and may have already closed. Does anyone want to take this proposal up? If not, I'll close the PR. |
Wasn’t the consistency between objects and classes considered an important thing to ensure as part of class fields? |
@ljharb That was, in a way, the motivation for this PR (especially when thinking about decorators). However, this PR is not part of the class fields proposal, and they should not be conflated. Computed class field names must be evaluated before, since they are evaluated different numbers of times. There are several differences between classes and objects, e.g., classes have their own inner scopes, so I don't think a difference here is fatal. |
The committee discussed this PR today and decided not to go forward with it, due to the increased risk of web breakage since this was initially planned. |
To make object literals consistent with decorator and field proposals, Brian
Terlson and Yehuda Katz proposed in the object evaluation order proposal that
computed property names in object literals be evaluated before any of the
right-hand sides. This patch implements that proposal.
The specification is organized by making PropertyDeclarationEvaluation return
a List of "fields", which for object literals, means key-value pairs which are
not methods Methods are excluded because they are evaluated in a way which cannot
have side effects or observe the world.