Feels like code golf. The two run examples are basically the same, but now I have to reason about what ‘run’ is and I’ve made my stack trace more complicated.<p>I am in love with “everything is an expression” from my time with Rust. I regularly use a ‘let’ and wish I could just have the entire conditional feed into a ‘const’ given it’s never going to change after the block of code responsible for assignment.<p>I wish there were more generations of ‘use strict’ so that we could make bolder, more breaking changes to the language without breaking stuff or dying in a horrible fire of “just roll your own dialect using compiler plugins.”
I appreciate the sentiment, but i do not agree with the solution.<p>The actual solution is to extract a function. What is the legitimate excuse for not extracting a function (other than being lazy, which i will not accept as an argument)<p>Edit: Just to enhance my comment, having a separate function with a distinct name enhances readability and maintainability. Readability is enhanced because a clear name lets the reader understand what is happening in the function (this attribute is lost with self calling nameless functions). Also, less lines need to be read to understand the wider context. Maintainability is enhanced because 1) more readable code is easier to reason about and change and 2) extracting a function means it can be reused
I like it! Another one-liner I'm constantly adding is<p><pre><code> const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
</code></pre>
Whatever runs the main function should probably do more, like handling rejections.
One control flow function I use often in JavaScript is my `runIfUnhandled` function:<p><pre><code> export const UNHANDLED: unique symbol = Symbol("UNHANDLED");
export function runIfUnhandled<TReturn = void>(
ifUnhandled: () => TReturn,
run: (unhandled: Unhandled) => TReturn | Unhandled,
): TReturn {
const runResult = run(UNHANDLED);
if (runResult === UNHANDLED) {
return ifUnhandled();
}
return runResult;
}
</code></pre>
I often use it to use guards to bail early, while keeping the default path dry. In particular, I used it heavily in a custom editor I built for a prior project, example:<p><pre><code> runIfUnhandled(
() => serialize(node, format),
(UNHANDLED) => {
if (
!Element.isElement(node) ||
!queries.isBlockquoteElement(node)
) {
return UNHANDLED;
}
switch (format) {
case EditorDocumentFormat.Markdown: {
const serialized = serialize(node, format) as string;
return `> ${serialized}`;
}
default: {
return UNHANDLED;
}
}
},
);</code></pre>
Another short utility I often add (that I wish were a language feature):<p><pre><code> function given<T, R>(
val: T|null|undefined,
fn: (val: T) => R
): R|null|undefined {
if (val != null) {
return fn(val)
} else {
return val // null|undefined
}
}
function greet(name: string|null) {
return given(name, name => 'Hello ' + name)
}
</code></pre>
This is equivalent to eg. Rust's .map()<p>Can also do a version without the null check for just declaring intermediate values:<p><pre><code> function having<T, R>(
val: T,
fn: (val: T) => R
): R {
return fn(val)
}
const x =
having(2 * 2, prod =>
prod + 1)</code></pre>
If I ever see this shit in code review I’m flagging it. IIFEs exist to avoid polluting global space in JS - its not something you are often dealing with in modern development. Especially the react example - make a new component, this prevents better readability like a new component or using an inline function.<p>Additionally for ever developer that hasn’t seen this, they have to look up the run function and try to grasp WTF it is doing. And god forbid someone modifies the run function.
In the "Use as a `do` expression" section, the example which uses `run` does not need the `else` cases and could be simplified:<p><pre><code> function doWork() {
const x = run(() => {
if (foo()) return f();
if (bar()) return g();
return h();
});
return x * 10;
}</code></pre>