-
Notifications
You must be signed in to change notification settings - Fork 106
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
Was there a purpose for non-lexical ordering of decorator applications? #524
Comments
Note to clarify: This is not about lexical ordering of decorators for a given element. If multiple decorators are applied to an element, those should definitely be applied in reverse-lexical order (e.g. from innermost to outermost), that was very intentional. This issue is solely discussing the ordering of application to elements. |
IIRC, the class element ordering just mirrors the order in which class elements are defined, which maintains lexical ordering within a group, where the groups and order are:
This order can potentially cause confusion with respect to side-effects during decorator application: let counter = 1;
function foo(target, context) {
const incr = counter++;
const init = value => value + counter;
return context.kind === "field" ? init : { init };
}
class C {
@foo x = 0;
@foo accessor y = 0;
@foo z = 0;
}
const obj = new C();
console.log(obj.x, obj.y, obj.z); // prints: ??? Here, the user might intuit that the Changing element order for decorator application to be document order instead of by group would restore this intuition. In addition, decorator application order affects the order in which "extra" initializers are evaluated. Currently, class C {
@((t, c) => c.addInitializer(() => console.log("A")))
static x() {}
@((t, c) => c.addInitializer(() => console.log("B"), "static")) // override placement
y() {}
@((t, c) => c.addInitializer(() => console.log("C")))
static z() {}
} A user might expect these callbacks to be evaluated in the order It's important to note that, if we do change the order from groups to lexical ordering, class decorators will still continue to be applied after all class element decorators. Since class decorators receive a reference to the class constructor, all replacements resulting from static and prototype method decorators must already have been applied. In summary, I would support changing application order to reflect the following:
|
Given how many discussions we've had around ordering, I want to be clear up front that this issue is NOT a intended re-litigation of any of the previous discussions.
This is about a specific ordering that happens in the spec currently that does not appear to have been really discussed anywhere, and that I can't remember the reason for. I do not think this ordering must change, but it is a bit odd and I thought I'd open this issue to see if anyone does remember the reason for this, or if it was an oversight during the writing of the spec.
To recap, these are the main steps of decorator application:
We're talking about step 3 in this issue, which is the following in spec:
As you can see, we iterate through the static elements of the class twice, and the instance elements of the class twice, so the ordering is:
I can't remember the reason for this change, vs running all decorator applications in lexical order. This change happened around the time we added
addInitializer
and removed@init:
from the proposal, so I think that it could have been that I was trying to encode the ordering of initializers added viaaddInitializer
here, and I didn't realize at the time that this would impact decorator application order in an observable way (or potentially I thought that it would, and it seemed correct at the time to do so).Reviewing this again, I can't really see a good reason to order these non-lexically. Applications happen at class definition time, so they shouldn't impact the actual order of assignment to the class/prototype/instance, nor the order of initialization via
addInitializer
, especially now thataddInitializer
for fields and accessors run immediately after the field/accessor is assigned. The only observable differences between this ordering and purely lexical ordering are:I'm not sure if there are any real use cases here though. In theory, you could side-channel information like type info from one decorator to another via metadata, which is a totally valid use case, but it's hard to imagine that this ordering would help that in particular. Would you really need to have all method metadata before fields run? What about
accessor
fields? They behave more like fields anyways, so wouldn't they also benefit from method metadata as well? It feels like for any case where you would want to side channel this information, you might run into issues with ordering (e.g.method2
wants metadata frommethod1
, which would be an issue in either case) so I'm not sure how ensuring fields have access to method metadata would help that.Like I said at the beginning, I'm NOT saying this is a critical issue, I just wanted to see if anyone else remembers the context for this decision, if it made sense at the time, still makes sense, etc. My feeling is that the current ordering vs lexical ordering won't have much of an impact in the majority of use cases, it just might be a bit odd for those edge cases where a decorator does want to rely on it, and it could be more complex to implement and makes the spec a bit more complex. I also don't think it would be a massive deal to change, because again, I don't think many decorators would actually be relying on this ordering, but I could definitely be wrong there!
The text was updated successfully, but these errors were encountered: