Skip to content

Instantly share code, notes, and snippets.

@littledan
Last active October 31, 2024 00:22
Show Gist options
  • Save littledan/a590784a72f2e1b8cc633ff5ff8a9dc2 to your computer and use it in GitHub Desktop.
Save littledan/a590784a72f2e1b8cc633ff5ff8a9dc2 to your computer and use it in GitHub Desktop.
Dan's feedback on JSSugar

Thoughts on the JSSugar/JS0 proposal

by Daniel Ehrenberg

In the October 2024 TC39 meeting, Shu-yu Guo is presenting on problems related to JavaScript language evolution (slides). These slides starkly expose an important fact about the world: JavaScript engines do not want to keep adding all the language features that TC39 is developing--they analyze the native implementation of some features to not be in end users' interest, when all costs are considered. We in TC39 need to respect this position and consider how to evolve the language in the future within an environment where some JavaScript engines will be more selective with what they implement.

Right now, there is an expectation that engines will eventually “catch up”, and there is only a temporary window of unsupported features. Under Shu’s proposal, TC39 would collectively define which parts of the language would not be supported by JS engines, going indefinitely into the future, by defining a feature as “JSSugar”. The committee can also decide to shift a particular feature to “JS0”, meaning it is on track for engine implementation. Both JS0 and JSSugar would be standardized by TC39, ensuring alignment within each class of implementations.

If certain JavaScript engines will not implement certain new features, I’m all for acknowledging that reality and allowing some features to proceed to standardization while some engines omit them. I like the idea of clearly documenting which features are not yet broadly supported across web browsers, as with Baseline. My main concerns:

  1. TC39 should maintain its quality standards, which JS engine maintainers have so far been helping to uphold. Features should be developed to be implementable everywhere, even if the implementation won't happen for some environments in the next couple years.
  2. We should think of JavaScript as a single language, even if some implementations are partial. We should have the same strong "Stage 4" stability guarantees for the whole language, even if some implementations are missing some features. Language changes need to consider ecosystem compatibility broadly.

The design space for JSSugar vs JS0 isn’t that different

In general, there actually isn’t much difference between what’s implementable in a transpiler and an interpreter: they are both limited to doing very local, very conservative analysis (if they are trying to be correct and consistent, which they should!).

Sometimes, in the past, there’s been magical thinking from TC39 that JS engines can optimize everything; this turned out to be false because it didn’t consider the startup/interpreter case. We shouldn't fall into the opposite error by expecting too much out of tools.

Two examples of analysis which would be a bad idea to expect out of tools:

  1. Cross-module analysis: Transpilers today generally work on a file-by-file basis. Optimizations may cross module boundaries, but it would add a lot of complexity if semantics weren't possible to determine without linking modules together.
  2. Type-driven transforms: Successive TypeScript versions getting better at type checking and inference. Change across versions is OK when it means that TS catches an error that it previously let through, but harder to manage when the runtime behavior might change–the former just requires that you get TS’s analysis to work, whereas the latter requires tests to ensure non-regression.

Stability requirements for JavaScript

Shu notes that, sometimes, proposals which were originally imagined for tools ("JSSugar") could later move to browsers ("JS0"). How should we do this?

One answer I don’t like: standardize the feature in JSSugar now, then later figure out the rigorous, natively-implementable version for JS0. Iterating on prototypes is good, but if we iterate on standard features broadly shipped in tools, it would cause real problems:

  1. If new syntax features come with a weaker stability guarantee, the instability will be a barrier to adoption. While some people adopt pre-Stage-4 features, uptake is gradual and becomes stronger over time, partly because shipping in browsers derisks compatibility over time.
  2. JavaScript transpilers and other tools need to remain stable over time to support their users. If they ship a particular version of a feature, they will need to support that version for a long time. If there are changes later, then both versions need to be supported side-by-side for some time. We've seen this dynamic in the gradual transition in class fields semantics from Set to Define. TC39 should be clear in what is stable, and let tools target a stable language. The current process, where Stage 4 requires consensus to change, suits these stability goals well.
  3. Modern JavaScript libraries need to be usable across a wide variety of runtimes and build toolchains. The movement in the ecosystem today is towards shipping original source (or just type-stripped source) in npm, rather than something which is already downleveled or polyfilled, to allow these runtimes and build systems to most efficiently deal with the code. Libraries targeting this deployment style will need to write their code at the intersection of what’s supported by a variety of toolchains (so this is influenced by the previous point).

If we want to move features from JSSugar to JS0 while preserving compatibility for tools, then we need to figure out the right design before tools ship broadly. Something that works well about our current process is that native JavaScript implementations enforce rigor on the TC39 process by blocking Stage 3 when something seems not implementable, and holding back Stage 4 until there are multiple native implementations. If we want to weaken some of these gates as part of defining JSSugar, we should do so with care.

My hope is that, for all features which might later make sense in JS0, we would still have implementers deeply engaged, raising concerns that something would not work for them based on careful analysis of each feature, based on the understanding that they may be asked to implement it later (as part of a possible upgrade to JS0).

I don’t know whether implementers want to do this analysis consistently for all potential future JS0 features, raising all appropriate objections. Maybe they don’t want to be gatekeepers, or maybe it’s just too much work to analyze everything that TC39 works on. In that case, we in the rest of the committee should do the best we can to perform a similar kind of analysis, and not let things through that would not work later. But we’d get a more accurate outcome if implementers can devote resources to this work.

Using our current process to continue development of JavaScript

If some JavaScript engines do not implement certain features, then do we need changes in our process to keep going, such as splitting the language into separate JSSugar vs JS0 tiers? The TC39 process document does not require this. The current requirements for Stage 4 are:

  • Two compatible implementations which pass the test262 acceptance tests
  • Significant in-the-field experience with shipping implementations, such as that provided by two independent VMs
  • A pull request has been sent to tc39/ecma262 or tc39/ecma402, as appropriate, with the integrated spec text
  • The relevant editor group has signed off on the pull request

So far, TC39 has mostly been interpreting the second bullet as requiring multiple (shipping) browser implementations. This has been useful for multiple reasons:

  • Requiring browser implementations encourages the definition of JavaScript to be aligned with what is available to web developers (once two browsers implement, the third is likely to follow).
  • Browsers often have particularly advanced, optimized JavaScript JIT implementations, which expose implementation issues which may not otherwise be visible to simpler implementations, like transpilers and interpreters.

These two pieces of motivation can be considered separately:

  • Baseline more accurately documents what is available to web developers than Stage 4. Maybe MDN browser compatibility data can be linked from various parts of the JavaScript specification, to make it easier to see what's supported where.
  • We should definitely continue to require implementations of sufficiently advanced optimization/complexity to promote a feature to Stage 4. This requires fully end-to-end JavaScript implementations, not just transpilers and polyfills.

Rather than splitting JS0 and JSSugar, we can simply accept the reality that not all JavaScript engines will implement all Stage 4 features.

Next steps

I'm glad that Shu is bringing this important topic to plenary. Let's continue moving forward with new features for JavaScript, even if some implementations do not have plans to follow through with implementation. But let's do so carefully, with attention to detail, so that we have a strong basis for future language evolution and implementation strategies.

Even if JS engines today do not want to implement all language features, we should be building the groundwork for future reunification of JavaScript. In particular we should not standardize features which would only work in transpilers and non-browser JS engines, and not work in browsers. A unified language and mental model has great value for JavaScript developers, as we've seen with ES6 and async/await--moving to the native versions of those constructs has been valuable, even as it's taken a long time and had significant cost as well. I predict that, if we do a good job evolving the language, then eventually, JS engine maintainers will come back and understand this value, and proceed with implementation, even if this takes some years.

@rafageist
Copy link

Hi @littledan

Thank you for sharing your thoughts on the future of JavaScript. I have been working on a proposal called Universal Catch and When, which aims to improve error handling in the language.

After reading your gist, I have some concerns about the future of new proposals in JavaScript. Do you think proposals like mine still have space in the future development of the language? Or, considering the landscape you’ve described, would it be more viable to focus on TypeScript, given its strong adoption?

I would really appreciate your thoughts on the feasibility of future proposals in JS, and any advice you might have on how to proceed with this initiative.

Thank you for your time and for all the work you do for the community!

Best regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment