Chrome 80 stable has a regression with Array.reduce that affects Angular
bugs.chromium.org
bugs.chromium.org
“All code using array.reduce should be rewritten without array.reduce so it's readable by humans.
Ok ok, fair enough, there are cases where array.reduce is the best thing to use.
Specifically, cases where you want to look really smart and don't care about readability.”
https://mobile.twitter.com/jaffathecake/status/1213077702300...
Seriously, even the simplified repro example in the bug is not immediately obvious - I can only imagine how bad the actual code is.
I personally love reduce. Reduce is probably my favorite function an iterable, or enumerable, can have... Is it really that clever to map and then reduce something?
You can totally use reduce when you shouldn't or when it makes your life more difficult, but you can also eat fried chicken and drink hot coffee driving naked down the freeway during rush-hour.... it's up to you to make smart choices.
- Because you have an un-braced arrow function
- Because you want to guarantee* no side-effects
- Because it's in the middle of a React render, or similar
Certain things can only be accomplished in a single expression by using reduce. But I agree that unlike other list-comprehensions it's generally harder to read than the imperative equivalent, outside of simple cases like summing numbers or concatenating arrays.
* Of course you could technically create side-effects from within any of your list-comprehension callbacks; like most things in JavaScript, this is only enforced by convention
A reduce function turns an array into a single value, passing the result of computation of each element to the next element. So, the signature is:
function (accumulator, element) -> accumulator
So, you take in some accumulated value (or the initial value on the first iteration) and an element and return the accumulated value to the next element. If the values were swapped, it would be inconsistent with the rest of the array functions where optional arguments are tacked on to the end (e.g. index, array), so the obvious assumption is that the accumulator goes in front to keep the rest of it as similar to the other array functions as possible.
I think it's unfortunate that array.reduce takes the accumulator after the function, but that also makes sense since it's technically valid to omit the accumulator (defaults to undefined).
The problem, I think, is that reduce is often abused when another function might make more sense (e.g. as a ghetto "find" or something). If it's used properly, I think it's quite elegant. If it's not, it's a confusing mess.
Are you a javascript programmer? If not, then it's ok if the code in an unfamiliar language looks cryptic. But if yes, then the code example (is it the one with the Promise.resolve as an accumulator?) is a very idiomatic way of writing sequential http requests before we had async functions and stuff.
One of the easiest ways to tell that it’s a misuse of reduce is that the result is not being assigned to anything.
The times I use reduce are simple. I want to “reduce” a collection of some sort to a single value, without any side effects. It’s additionally beneficial if the action being performed can be encapsulated in a function, so I can simply pass the function in, instead of code. However, this isn’t necessary.
JS doesn’t enforce it, but the code my team and I write requires that reduce has no side effects (we enforce through code review).
The point is that when I come across reduce in our team’s code, the intention is communicated to me immediately. A collection of some sort was converted to a single value, and there were no side effects.
That won’t be my expectation if it’s a for loop, or a forEach, or a while.
If those conditions are not met for a reduce, then it’s a bug in the code, as far as my team is concerned.
> One of the easiest ways to tell that it’s a misuse of reduce is that the result is not being assigned to anything.
The example with Promises is using the accumulator, it's just referring to it as "sequence" to more accurately describe what it using the accumulator for (to "sequence" in serial a set of Promises).
While the final result from the accumulator isn't assigned to anything in snippet as shown, one would easily imagine it being used as a "finished loading all of the chapters" promise before some other action.
It would definitely be a better example if the side effect (`addHtmlToPage`) wasn't performed inside the promise callbacks but on the results, but it's still an alright example of promise composition.
Though reduce for promise composition isn't a great example today when most cases these days you'd just want to write it async/await and a traditional for loop, with the browser or your transpiler doing the composition work for you, leaving it a clearer to read sequence of events.
Use it when it's clear and elegant, don't when it's not. Unfortunately, relatively junior programmers try to force it because it seems like a cool tool to use.
Sure, it's bad that this made it into a release in the first place, but when it did, they were quick to identify the issue, halt the release, find 2 potential fixes, manually test the fix, get a canary release out to get wider testing, update the test bench to prevent this class of bug happening again, and start rollout to the public stable build.
All that across tens of team members, with regular status updates from humans and bots, and despite the fact no bug reporter could give them a proper repo testcase for a long time.