Please thank littledan and the other champions for (in short) doing everything right.
I'm convinced static ("v3") decorators are not the way forward.
I think the concrete disadvantages of a static design should be taken more into account:
- Complexity - There's currently only a sketch of the spec; it's going to be big.
- Amount of new syntax.
- New primitive decorators are impossible to polyfill.
- Decorators aren't values and thus mostly can't interact with the
rest of the language. For example, they can't be arguments, so no higher-order
decorators. And this forces duplication in the spec everywhere we do
want to be able to talk about decorators: decorator-specific syntax
and semantics in
import
andexport
, for example. - No dynamic access to decorators, e.g. via module namespace objects.
You can't dynamically
import()
a module to use a decorator. - No conditional or other dynamic definition of decorators.
- So primitive decorators can't meaningfully be feature-tested. (You can test, but what good is it knowing the feature isn't there?)
- TC39 and implementors impose an unwanted delay on, and have an unwanted role as gatekeeper over, new primitive decorators.
- Hard choice for users between accepting the delay and running everything through Babel forever.
Maybe I don't properly understand the purported benefits of static decorators.
-
No specific performance benefits are claimed, but the proposal suggests that Babel sees significant code size improvements, for example. I'll be surprised if similar gains could not be had with less dramatic changes (and without the difficulties listed above).
Measuring the size of Babel output probably isn't a good metric by itself anyway. Consider: The main thrust of this design is to make some information statically available at a particular moment in time: after decorator import dependencies are loaded, but before the code starts running. The proposal, effectively, is that all high-performance implementations converge on doing bytecode generation at this time. It's possible that Babel benefits, yet in engines where modules load over the Web, this change actually punishes code that uses decorators (by delaying some work).
Delaying bytecode generation has other technical problems. Currently ASTs are short-lived; the proposed implementation technique requires us to keep them around while dependencies are loading. Currently module bytecode is a function of the module source, so it is easy to cache; the proposal suggests making bytecode depend on decorators imported from other modules.
Stepping back a bit, high-level features should not be warped around implementation details to this extent.
-
The new design is meant to make it easier to define new decorators. But again, a modest change to "v2 decorators" with the same goal would yield similar benefits, right? The proposal includes this example code:
// bound.mjs export decorator @bound { @register((target, name) => { ... }) }
But we could just as well say it like this:
// bound.mjs export bound = Decorator.register((target, name) => { ... });
which is already syntactically valid ES.
A possible way forward is to evolve v2 decorators. Change the interface
so that all decorators work like v3 @register
. More intermediate
states of the class are visible, but there's less messing around with
descriptors. Maybe add a few helper functions, like
Decorator.wrapMethod(f => f')
and Decorator.compose(...decorators)
.
Jason Orendorff
20 March 2019