> ustifying it on the grounds of "productivity" and "readability". It seriously gets on my nerves. Do not do this.
On the contrary, please do this. "productivity" and "readability" are important aspects to consider when writing code, especially if someone else is going to be reading it.
When you've identified a bottleneck, feel free to write the code in the bottleneck more performantly, if necessary. But please do not sacrifice readability across the entire codebase for a couple of hot loops.
I can agree with your point in general, but [...Array(N).keys()].forEach() is not the most readable way to write "do this N times".
It creates an array of length N, but for obscure-to-most-people reasons, Array(N).forEach() doesn't work, so they Rube Goldberged their way to an array that they could call forEach() on. Their solution was to use Array#keys to get an iterable from the array. But an iterable doesn't have a .forEach() method, so they iterate the iterable into another array just to iterate it again with Array#forEach. Frankly the only thing this seems optimized for is to solve the problem without the for-loop for some reason.
The for-loop, on the other hand, is an instantly obvious solution. It's how programmers have been expressing "do this N times" for decades across languages.
Especially when Array(N).fill(0).forEach((_, key) => ...) gives you the same exact functionality without the second eager array if you're seriously trying to avoid for loops.
Unless there's a reason to use Array.forEach such as automatic paralellization or some other SIMD-like optimization that can be done that cannot be done in a forloop.
A lot of cargo-cult programming seem to take functional programming to mean programming using the map/reduce/foreach functions, and you end up with shitty code like [...Array(N).keys()].foreach(), just so you can somehow claim that you're doing functional programming.
I sometimes wish there was a way to demand the same level of proof for claims of readability and productivity that we demand for claims about performance.
Measuring before optimizing is of course a good idea, but it's also time consuming. There's a lot of latitude to make different reasonable choices about how you write code down in the first place before you get to the measuring staging.
Should we criticize someone who reaches for a for loop because they know it doesn't allocate without proving that that matters more than someone who reaches for a map because they think it's more readable and productive without any great way of proving that that's true?
>map because they think it's more readable and productive without any great way of proving that that's true?
I mean, doing
sendUserIds(users.map(u => u.id))
is very obviously more readable than what you're forced to do in languages like Go
ids := make([]int, len(users))
for i, user := range users {
ids[i] = user.Id
}
sendUserIds(ids)
The difference only grows once you start needing to do more operations, grouping, putting stuff into map by a certain key, etc. Many languages also have lazy sequences for such operations (e.g. https://kotlinlang.org/docs/reference/sequences.html, though they aren't always faster, it depends)
The code in the comment above is however not an example of this, and is actually less readable in my opinion. It seems more like an author wishing to be using a different language instead of accepting what they have in front of them.
You really don't need to measure to know that a tight loop inside a render function called at 60 fps is performance critical. The post does call this out explicitly as "in the middle of tight loops", which is a vary, very different situation than code that pretty much only runs once.
This is pretty far from premature optimization even if you never measure the effect, it is entirely predictable that code like this would have unnecessarily bad performance. If you write performance sensitive code you don't allocate stuff inside a tight loop unless you can't avoid it. The consequence of this is typically that the code is more straightforward imperative with fewer abstractions, but that is not inherently less readable.
On the contrary, please do this. "productivity" and "readability" are important aspects to consider when writing code, especially if someone else is going to be reading it.
When you've identified a bottleneck, feel free to write the code in the bottleneck more performantly, if necessary. But please do not sacrifice readability across the entire codebase for a couple of hot loops.