Ideas for a JavaScript Stricter Mode
rtpg.co
rtpg.co
Reshuffling concepts names is just a bad idea.
from … import …
Would help autocompletion, a good idea. The pythonic cumbersomeness of changing “import foo” to “from foo import …” is not relevant here, since bare imports do not create a symbol, so there is low chance you’d ever need to do it.
get rid of await
Not gonna happen. You can’t know in advance what a function returns, so it’s either a performance hit of awaiting every call, or true stackful coroutines (aka green/light threading, cooperative multitasking, etc), which javascript will never get by “design of browsers who don’t want to rewrite their C++ code to support this type of continuations”. That’s the official position of those in charge.
Postfix foo.await is not compatible with object key semantics. A key can be any(?) identifier-like token: foo.class, foo.function, foo.await, foo.delete, etc. You may want to prohibit that before implemeting .await operator and explain yet another inconsistency with +5728 upvotes on SO.
Also, some people simply like explicit await. Both camps are right in different situations.
arrow func return object
=> ({…})
The proposed solution is a heuristic. You don’t want heuristics like that in code usually, because you end up with {; …} hacks which turn into a habit.Without ASI, the following would work as you'd assume, instead of returning `undefined`:
return
{
answer: 42
};
Just one of many ridiculous edge cases that would go away if ASI didn't insert a secret "helpful" semicolon at the end of the return line.Rather than making me remember all the edge cases and rules [2] that keep growing with each new syntactical feature added to the language, we should just be making your code more explicit. The rules of reading the code are simply much more complicated than they should be with ASI, and many proponents don't even understand these issues, or suggest preposterous solutions to some of these edge cases such as inserting leading semicolons[3]!
Maybe put simply: Semicolons are sometimes required. Let's just make them always required.
[1]: https://www.theregister.com/2018/01/12/javascript_technical_...
[2]: https://tc39.es/ecma262/multipage/ecmascript-language-lexica...
I have the same problem with JS devs removing file extensions on imports. You're just removing useful information from the code, that's sitting in the most out of the way place in the file (right at the top before anything is even happening), for absolutely no good reason. And replacing it with build time magic in some 3rd party tool. That's such a terrible tradeoff it's almost unfathomable to me that an experienced programmer would willingly make it, let alone double down and argue for it if the topic is brought up.
You even get the same ridiculous post-hoc justifications for it too. "Oh but what if I want to change a JS file to a JSON file? I'd have to change the extension!".
This gets solved by using some sort of packager, but it's another sort of bit of ugliness that doesn't feel like it's going to get resolved anytime soon.
Okay, I'll bite. I don't think "aesthetics" and "clarity" are disconnected. Clear code looks nice, and nice looking code is clear.
I still adhere to a "no-semicolons-needed" approach. The edge cases are few, and I can't recall the last time I ran in to trouble because there was no semi-colon.
People not including that trailing comma out of IE-era habits is something that bites me far more often (although you do get an error, but Firefox at least will also issue an "unreachable code" error in your example).
For me, personally, I find it easier to remember putting a semicolon in special cases than to remember putting one after each statement.
https://pksunkara.com/semicolon/
The comments on HN:
https://news.ycombinator.com/item?id=3854130
Further context:
https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...
The result will automatically be await-ed.
Please don't do this, it is more confusing to omit the await keyword.For example, take this code:
```
let eventOccurred = false;
// Some async logic is invoked and an event listener is launched which may change the eventOccurred variable.
if (eventOccurred) { ... }
await foo();
if (eventOccurred) { ... }
```
This logic makes sense as it's possible that the eventOccurred variable has changed from one line to the next. On the other hand, it makes no sense if foo is a regular synchronous function and you remove the await keyword - Any developer reading this will think it is a bug and will delete the second if block. The await gives you a hint that the state of variables in the current scope may have changed by the time the next line is reached.
If we await by default, we won't be able to easily distinguish functions which allow concurrent scope state mutations from those which don't.
Really what I’d want from a stricter JS is control over mutability, prototypes, and eval. Basically restrict dynamicism so you can do more effective static analysis over code. I’d also love some reduced syntax like no comma operator, no for…in, no ==. It’d be interesting if tuples/records could be introduced in this context. Basically make a sub language that’s more immutable and static.
let generatePerson = name => ({ name, id: generateId() })
Just use a lint rule (eshint or whatever people use these days). Even if var serves no purpose in "stricter" mode, removing it also serves no purpose. It's got to remain supported by the underlying implementation for eternity, in all likelihood.
> Fix import keyword ordering
As it is, import binding looks like an assignment,
import { x, y } from "z";
Kind of like: const { x, y } = require("z");
The consistency is nice.> Get rid of await soup for most code
Non-starter, because you need to be able to pass around promises as values, without having them explode on you.
> The less wild alternative would be to just make await a postfix keyword.
This is the only proposal here that makes some sort of sense, but the problem here is that in JavaScript, keywords can be used as property names... without quoting. I mean, that's already how promises work:
somePromise.then(x => { ... })
Even though "then" is a keyword, it's a property here, which is fine. Why would the "await" property be special? If it's special, it should have special syntax. In other words, somePromise.await
This SHOULD be a property access, because that's the syntax for property accesses, even when the property happens to be the name of a keyword. Don't make major breaks to language consistency and introduce weird corner cases just because postfix syntax has some advantages.> Make object-returning arrow functions cleaner
I don't see how this one can be done in a way that isn't completely horrible. We do need functions that return void. The proposal doesn't show how you can write out a function that returns void here.
Preface: I wrote this as a whim, but understand that almost none of this is acceptable.
However! People claim these are not merely bad ideas, but not doable at a technical level. This is, to my understanding, false. These are merely bad ideas, but can be implemented!
> This does not make things stricter
True! The name was a bad idea, playing off "strict mode". The main thing was about opting into new behavior.
> let/const/var reshuffling can't work with existing code
This could be done at the tokenizer level. If you have "stricter mode" on the top of the file, you swap out the tokens.
> auto-await cannot work because some non-async functions return promises
A non-async function would simply return the promise value! I am not suggesting removing promises. I am suggesting that when a call to an async function happens from within another async function (async function being defined via syntax, not by return value!), at runtime, we insert the await (with an escape hatch via asPromise). This does not require solving the halting problem, and is merely extremely confusing and hard to explain and probably gets in the way of a lot of JITing efforts.
> name => {name} is not possible at a parsing level
It's possible! But it involves breaking the parse results of existing code. Python recently introduced changes that required more complicated parsers than before, and this would also be of a similar quality. Some recursive descent backtracking parser that simply tries to parse as an object literal, and if that fails backtrack would solve the requirement. Perhaps a well-thought out grammar could make this work without backtracking, but I would be surprised.
> foo.await is not possible
This is such a weird one to me. Rust has this! I am not saying "if an object lookup is for the key of await then do an await", but "if foo.await is in your actual code then that is the same as (await foo)". There's a difference between modifying syntax and modifying semantics, and I was proposing modifying of syntax, not object lookup semantics.
Most of the ideas here are not good enough that they deserve to be the future of Javascript. The one part that is probably a good idea is the import reordering but, before you have a strict mode for this, you first need JS to accept either order during some transitional period.
The const declaration creates block-scoped constants, much like variables declared using the let keyword. The value of a constant can't be changed through reassignment (i.e. by using the assignment operator), and it can't be redeclared (i.e. through a variable declaration). However, if a constant is an object or array its properties or items can be updated or removed
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
How can this be complicated?
With the complexities of being backwards compatible with all the ECMA in the wild, TypeScript could be the candidate language for this.
async foo() {
let y = awaited(); // as in article
let p = async notAwaited();
// do stuff
let z = await p;
}Am I doing something wrong? How do you or why would you avoid waiting?
1. I pass it into Promise.all(). This one is a little obvious but I think it counts as you pass in non-awaited promises.
2. I store the promise I receive in a variable, that I can re-use to de-duplicate multiple in-progress calls.
3. I anticipate a future call, and fetch data in the background.
Those are some real examples I've used, but I can also image my HTTP server kicks off some process that's not consequential for the current request, which would also happened asynchronously (and maybe immediately return a 202. My HTTP framework doesn't use callbacks unlike express so functions need to return to send a response).
If you use React, calls in useEffect are also typically not awaited because the callback to useEffect does not support async functions.
I'd generally agree though that most of my code is also very linear, and the few places I need to weirder stuff tends be in the libraries I maintain.
- Promise.all lets you do work in parallel. Very useful if you have a set of network requests to make, or things like that
- Sometimes I want multiple parts of my program to respond to some event happening. You can use a promise as a simple one shot event dispatcher. Eg:
this.p = fetch(…)
… then elsewhere, from multiple call sites:
foo.p.then(…)
You can use this for http requests, user actions (make a promise which resolves when the user closes the form) or for dynamically importing a module. I’ve used this a lot with for webassembly modules in the browser. Multiple bits of code can all wait for the module to load and then start using it as soon as it’s ready. And once it’s ready, the promise will resolve immediately.I’m sure there are other uses, but these are things I’ve used raw promises for. They’re handy!
const p1 = heavy_foo()
const p2 = heavy_bar()
await some_baz()
const r2 = await p2
…
return [await p1, r2, …]
It is useful in “threaded” code which does many things in parallel runs. Or in this code: const [p, done] = block()
obj.on("event", done)
const res = await p
…
The implementation of block() is left as an exercise for the reader ;)It can help, and was addressed in https://github.com/microsoft/TypeScript/issues/31658 quite a while ago.
Typescript already maps all the exports that are available and what modules they come from (including externals) so is able to provide an autocomplete.