proposal-do-expressions
proposal-do-expressions copied to clipboard
Focus on `const` bindings
Seems the do
concept has invited a wide range of interpretations. But, with modern JavaScript, the actual practical need (evidenced in part by the majority of code samples in other Issues in this repository) is to simplify code/patterns related to constant initializations. When applying Best Practices™, it is very common to have some logic that
- is idiomatically expressed with flow control or exception handling statements,
- not needed elsewhere and therefore suitable to be placed inline without a named function,
- can rely on closures for parameterization, and
- produces a value that is best treated as a constant. (An inline IIFE would reduce readability and should be refactored to a constant.)
The way to address this currently is to write, e.g.
// Sample 1
const calculation = (() => {
try {
switch(determinant) {
case 0: { return input * cofactor; }
case 1: { return input * input; }
default: { return 0; }
}
}
catch (e) {
log(e);
return -1;
}
})();
Most of this follows from the common desire to use const
bindings to improve code clarity and safety. This should be promoted by removing the syntactic noise of the IIFE.
Informal Proposal
By scoping this proposal to the constraints identified above, we can focus on a variation of const
declaration. I propose one of the following two options: <=
(left arrow) operator, or from
keyword.
<=
operator (location of operator alludes to assignment, angle-bracket alludes to a function body):
// Sample 2
const calculation <= {
try {
switch(determinant) {
case 0: { return input * cofactor; }
case 1: { return input * input; }
default: { return 0; }
}
}
catch (e) {
log(e);
return -1;
}
};
from
keyword (alludes to module system):
// Sample 3
const calculation from {
try {
switch(determinant) {
case 0: { return input * cofactor; }
case 1: { return input * input; }
default: { return 0; }
}
}
catch (e) {
log(e);
return -1;
}
};
These are essentially syntactic sugar for the original sample. There would be no form to sugarcoat expression-bodied IIFEs.
async
/await
:
The syntactic noise is exacerbated with async
functions. Currently, e.g.
// Sample 4
const result = await (async () => {
try { return await db.getAll(); }
catch { return await db.getActive(); }
})();
<=
operator:
// Sample 5
const result <= await {
try { return await db.getAll(); }
catch { return await db.getActive(); }
};
from
keyword:
// Sample 5
const result from await {
try { return await db.getAll(); }
catch { return await db.getActive(); }
};
In either case, the presence of await
would imply the the use of the async
before the opening {
. await
will cause the result of the Promise to be bound to the identifier. It is acceptable, but optional, to include async
explicitly between await
and {
.
If await
appears in the const
initialization body ("initialization block"?) but the desire is to bind the Promise itself to the identifier, then the await
keyword must be not be present and the async
keyword becomes non-optional. This makes clear the const
will refer to a Promise.
const
initialization bodies would also support destructuring, just as if an IIFE were being used.
By focusing on a narrow best practice that, due to its frequency, is currently painful, const
initialization bodies can be extremely beneficial to the community.