I did, until I used them enough where I saw where they were useful.
The bad examples of arrow functions I saw initially were of:
1. Devs trying to mix them in with OOP code as a bandaid over OOP headahes (e.g. bind/this) instead of just not using OOP in the first place.
2. Devs trying to stick functional programming everywhere because they had seen a trivial example where a `.map()` made more semantic sense than a for/for-in/for-of loop. Despite the fact that for/for-in/for-of loops were easier to read for anything non-trivial and also had better performance because you had access to the `break`, `continue` and `return` keywords.
I feel there are a few ways to invoke .map() in a readable way and many ways that make the code flow needlessly indirect.
Should be a judgment call, and the author needs to be used to doing both looping and mapping constructs, so that they are unafraid of the bit of extra typing needed for the loop.
As an aside: It’s way less ergonomic, but you likely want `Promise.allSettled` rather than `Promise.all` as the first promise that throws aborts the rest.
It doesn’t really abort the rest, it just prioritizes the selection of a first catch-path as a current continuation. The rest is still thenable, and there’s no “abort promise” operation in general. There are abort signals, but it’s up to an async process to accept a signal and decide when/whether to check it.
Admittedly, I was being a bit hand-wavy and describing a bit more of how it feels rather than the way it is (I'm perpetually annoyed that promises can't be cancelled), but I was thinking of the code I've seen many times across many code bases:
While you could pull that promises mapping into a variable and keep it thenable, 99% of the time I see the above instead. Promises have some rough edges because they are stateful, so I think it might be easier to recommend swapping that Promise.all for an Promise.allSettled, and using a shared utility for parsing the promise result.
I consider this issue akin to the relationship between `sort`, `reverse`, `splice`, the mutating operation APIs, and their non mutating counterparts `toSorted`, `toReversed`, `toSpliced`. Promise.all is kind of the mutating version of allSettled.
Thank you, that's something I also never have understood myself. For inline anonymous functions like callbacks they make perfect sense. As long as you don't need `this`.
But everywhere else they reduce readability of the code with no tangible benefit I am aware of.
ESLint and Prettier are the de facto default linter/formatter combo in JS. There are rules you can enable to enforce your preferred style of function [1][2].
They are miles away from `gofmt` or `rust fmt` or `cargo clippy` and so on.
It's not opinionated, but requiring you to form your own opinion or at least choose from a palette of opinions.
It requires effort to opt-in rather than effort to opt-out.
The community doesn't frown on code that's not adhering to the common standard or code that doesn't pass the "out of the box" linter.
So, if I have a typescript project with a tree of some 20 dependencies (which is, unfortunately, a tiny project), I'll have at least five styles of code when it browse through it. Some JS, some TS, some strictly linted with "no-bikeshedding", some linted with configs that are bigger than the codebase itself. Some linted with outdated. Many not linted at all. It's really a mess. Even if each of the 20 dependencies themselves are clean, beauties, the whole is an inconsistent mess.
I personally believe that Prettier strikes a decent balance between being configurable vs being opinionated. I've used formatters that are much less opinionated than Prettier (e.g. the last time I used XCode it only handled indentation by default). ESLint also teeters on the edge between configuration vs convention quite well in my opinion, especially given the fact that browser JS and server side JS have vastly different linting requirements. I also love the extensibility of ESLint. Being able to write your own linting rules is a boon on productivity. Having access to custom framework specific linting rules is quite nice (I couldn't use React without the react-hooks/rules-of-hooks third party ESLint rules).
> Some JS, some TS
I think the JS community has done remarkably well amongst dynamically typed languages in settling on one form of gradual typing and adopting it fervently (Flow no longer has any market share at all). Whereas the last time I checked Python still had the Mypy/Pywright divide, and Ruby had the Sorbet/RBS dichotomy.
Ultimately though, most of your critique boils down to the fact that JS (unlike Rust and Go) isn't maintained by a single monolithic entity, and therefore there's no one to dictate the standards you're looking for. If Deno were the sole caretaker of JS for example, we'd have a standard linter and formatter devoid of complex configuration, but Deno doesn't control JS.
This is a consequence of JS being a collaborative product of the various browser vendors, TC39, and the server side JS runtimes that have adapted JS to run on servers. The advantage of this of course though is that JS can run natively in the browser. I think that's a decent tradeoff to make in exchange for having to wade through dependencies with different ideas about when it's appropriate to use arrow functions.
Moreover the binding and lexical scope aspects supported by classic functions are amongst the worst aspects of the language.
Arrow functions are also far more concise and ergonomic when working with higher order functions or simple expressions
The main thing to be wary of with arrow functions is when they are used anonymously inline without it being clear what the function is doing at a glance. That and Error stack traces but the latter is exacerbated by there being no actual standard regarding Error.prototype.stack
To me arrow functions mostly just decrease readability and makes them blend in too much, when it should be important distinction what is a function and what is not.
I'm not a javascript programmer, but I really like the arrow pattern from a distance exactly because it enforces that idea.
My experience is that newcomers are often thrown off and confused by higher order functions. I think partly because, well let's be honest they just are more confusing than normal functions, but I think it's also because languages often bind functions differently from everything else.
`const cool = () => 5`
Makes it obvious and transparent, that `cool' is just a variable where as:
`function cool() {return 5}`
looks very different from other variable bindings.
Since we're on the topic of higher order functions, arrow functions allow you to express function currying very succinctly (which some people prefer). This is a contrived example to illustrate the syntactical differences:
const arrow = (a) => (b) => `${a}-${b}`
function verbose(a) {
return function (b) {
return `${a}-${b}`
}
}
function uncurried(a, b) {
return `${a}-${b}`
}
const values = ['foo', 'bar', 'baz']
values.map(arrow('qux'))
values.map(verbose('qux'))
values.map(uncurried.bind(null, 'qux'))
values.map((b) => uncurried('qux', b))
> should be important distinction what is a function and what is not
code is to express logic clearly to the reader. We should assess it for that purpose, before assess for any derivative, secondary concern such as whether categories of things in code (function etc) visually pops out when you use some specific tool like vim, or grep. There are syntax highlighters for a reason.
And maybe if grep sucks with code then build the proper tool for code searching, instead of writing code after the tool.
A simple heuristic I use is to use arrow functions for inline function arguments, and named "function" functions for all others.
One reason is exactly what the subject of discussion is here, it's easier to string-search with that keyword in front of the name, but I don't need that for trivial inline functions (whenever I do I make it an actual function that I declare normally and not inline).
Then there's the different handling of "this", depending on how you write your code this may be an important reason to use an arrow function in some places.