4 ms·
A recent bandwagon to jump on is that you should never use reduce() for anything whatsoever, because it's always easier to read if you rewrite it. I'm not sure
by dreen 6y ago
A recent bandwagon to jump on is that you should never use reduce() for anything whatsoever, because it's always easier to read if you rewrite it.
I'm not sure about it, but I have certainly seen some overengineered code using reduce().
- chriswarbo 6y ago'reduce' (AKA 'fold') is rather fundamental to lists/sequences: it's their 'elimination form', which means anything involving lists can be written using 'reduce'. Functions like 'map', 'filter', etc. are essentially common patterns which can be implemented using 'reduce'. I understand the aversion to 'reduce', since it can get quite messy, but I still prefer it to e.g. WHILE loops (note that the 'for' keyword in most languages actually implements a WHILE loop). I think a more general rule is that custom abstractions can sometimes be useful, so we shouldn't try to write everything in terms of language builtins (these days 'map', 'filter' and 'reduce' are often built-in, but we can still make our own abstractions on top if appropriate). As a comparison, the elimination form for booleans is 'if/then/else': we could write all of our branching in terms of if/then/else, but there are common patterns that can be expressed using abstractions like boolean algebra (AND/OR/NOT/etc.).
- dkarl 6y agoReduce and fold capture the structure, but there is a readability problem with them, which is that the semantics of the accumulation value are often complex and not obvious. It's nice to give the accumulator a descriptive name, but the more complicated the value, the more likely a programmer is to give up and call it something unhelpful like "x" or "accum" instead of "mapOfAccountIdAndSectionIdToCountAndSumAndCountOfTransactionsExceedingLimit". To make sure code like that is readable, programmers have to declare types even when they're optional, use descriptive names, and use comments when these methods don't suffice. Or in Scala, even declare case classes that are only used in a single complicated expression, which sounds extravagant, but when I've seen it, it turned code that might have taken ten minutes to decipher into code I could cruise right through. Unfortunately, in my experience, this is rare. Often my first step in figuring out someone else's reduce or fold is to guess how I would have done it and then see if their code implements my guess, which is an assembly language level of readability.
- chriswarbo 6y agoI completely agree, using 'reduce' can make code hard to figure out. I'm very guilty of this myself, e.g. I've written a bunch of hacky mess which roughly follows this template: snd (reduce (FOO, BAR) go BAZ) where go (x, y) elem = (FIZZ x y elem, BUZZ x y elem) This usually starts out as a 'map'; then I find myself needing to append or discard some elements so I change it to a 'reduce'; then I find myself needing to propagate some info across calls, so I pair this on to the accumulator and discard it at the end. The end result is a sequential computation with mutable state; hardly a 'functional pearl'! My point above was that we can't forbid 'reduce'; since it's a fallback when our calculation doesn't follow an established pattern; and that it's often useful to codify that pattern into a nice, generic function (using 'reduce'), and use that new pattern in our application code.
- dkarl 6y agoI haven't heard that before, but I've worked with several programmers who normally write very readable code, and when they write a reduce or fold, it's like they forget everything they know. They go super compressed and cryptic even if it's not their normal style. I can see why someone would want them to give it up.