logoalt Hacker News

Anecdotally, Programmers Dislike "Reduce"

31 pointsby praptaklast Monday at 2:58 PM36 commentsview on HN

Comments

chubottoday at 6:28 PM

Related to the point about worse performance, I'm pretty sure I was there when reduce was "banished" from Python 3 -- demoted to functools.reduce(), instead of the builtin reduce() in Python 2

The story is that sometime in 2006 or 2007, Guido van Rossum was debugging why a web page in Google's internal code review tool (which he wrote) was taking 30+ seconds to render.

This is basically a "production" incident, since thousands of Google engineers relied on the tool. Requests like this were probably tying up threads and exhausting thread pools, perhaps

Eventually it was tracked down to a line wrapping algorithm written with reduce(). I don't think he wrote it -- it may have come in through a dependency. As many know, reduce() is basically:

     s1 + s2
     s1 + s2 + s3
     s1 + s2 + s3 + s4 
     ...
And that's O(n^2) when s_i are strings. And I think it showed up if you viewed a 5000+ line diff, or a 5000+ line file. (Newer programs like Github also suffer here)

I believe, in Python at that time, += was already optimized to avoid this (just like essentially all JS VMs are). Or you can use the idiom of append() to list and join() after.

But reduce() basically forces the inefficient implementation, and I'm sure this is still true in Python 3.

---

So basically Guido spent a long time debugging a performance problem related to reduce(), and made the decision to eject it, to help users avoid "footguns". I was his officemate at the time, so I recall this, but I wasn't involved directly

Also, somebody contributed reduce() to Python way back in the 90's, as well as other functional idioms. He wouldn't have added that himself -- it was never his preferred style.

He preferred a more imperative style. But he allowed those contributions, and then slightly regretted it later.

https://docs.python.org/3/library/functools.html#functools.r...

show 3 replies
franeytoday at 6:51 PM

At least in TypeScript, it's a bit clunky to type, and I usually forget the order of the reduce function's arguments (accumulator, current item). Maybe it's just me, but it's especially easy to forget the order when the position of the accumulator is the 1st argument to the callback but the 2nd argument of the reduce function:

    array.reduce(
      (accumulator, currentItem) => {...},
      initialValue,
    )
In .filter(), The current item is the 1st argument and the intermediate/accumulated value comes later: filter((currentItem, index, intermediateArray)) => ...)

I use .filter() more often, so that argument ordering where currentItem is right next to the array is more intuitive for me

show 3 replies
snackbrokenyesterday at 5:42 AM

Map and Filter are nice because they let you reason locally about a single element in isolation. Reduce(Fold) forces you to reason globally about intermediate results. Reduce also forces you to conjure up a "zero" value of the relevant type, which isn't usually difficult but it does constitute some extra mental overhead.

japgollyyesterday at 12:53 AM

I assume the author is talking about `fold`, as in `[A] -> B -> ((B,A) -> B) -> B`, and not what I often think of as reduce as `[A] -> ((A,A) -> A) -> A`.

`fold` is awesome and super useful. It's the easiest and most convenient way to turn a collection into a single value. Put me anecdotally in the opposite bucket.

show 1 reply
evnixlast Monday at 7:31 PM

The name itself is confusing to begin with.

I come across reduce once in a few months, then I think it's a neat trick and a nice to have function.

then I forget it's even available and don't ever use unless these days LLM brings it up again.

Glyptodontoday at 6:51 PM

It's on my list of things that are awkwardly named because there's not a great name to choose, particularly given how wide the different use cases are.

juancntoday at 6:43 PM

I like it conceptually, but the main issue for me with reduce is that it's hard to know exactly how the reduction will actually be executed.

The FUBAR potential with map and filter is much smaller, with reduce it depends on deep knowledge of the internals of the reduction itself, which makes it not as useful as a safe abstraction.

s-zengyesterday at 4:44 AM

Even in the world of functional programming, there's an argument to be made that `fold` is a bit of a code smell, in a similar vein as `while` being slightly smelly in an imperative code base. There's good reasons for each to be used, but they are such low level iteration primitives that you might be better off with a higher one (e.g. for loops or iterators in imperative programs; in FP you might reach for monoidic reduces (as opposed to folds where the accumulator is a different type from the list element), monadic traverses, or recursion schemes). Even though you can implement iterators or for loops in terms of while loops, you probably shouldn't, and similar for functional traversals.

In languages like python or Java though, you don't really have access to many of the higher power functional traversals however. So that puts you into a similar kind of bind as working in a language with only while loops

slopntlast Monday at 7:55 PM

I like reduce in principle since it generalizes a simple concept pretty nicely. I don't use it that much in practice since its alternatives just require less brainpower. It competes against using local mutable state with a loop or iterator combinator which I would argue are easier to wrap your head around (i.e. loop with variable/map with closure). I would argue its one of those cases where something is just harder to do/understand in functional vs imperative programming.

sumolessonstoday at 6:39 PM

I wanted to add that from personal experience tastes can change! I didn't like reduce when I was first exposed to functional programming, but have come to prefer it.

Might be nonsensical, but one thing I sometimes wonder is why I reach for reducing a list to a value more often than I need to generate a list from a starting value. I guess the asymmetry has something to do with the kinds of applications I work on.

karmakazelast Monday at 7:36 PM

It's part of the functional trio: map, filter, reduce--and half of MapReduce.

norirtoday at 6:42 PM

Anywhere that I could use reduce, I instead write a tail recursive function. This is also why I do not and will not ever choose python or javascript voluntarily.

ChrisMarshallNYtoday at 6:36 PM

I like it, but I don't use it anywhere near as much as other built-in closures.

I find the two ways that you call it to be a bit annoying (not a showstopper). It just seems a bit "kludgy" to me.

theamklast Monday at 5:41 PM

At least in Python, I've found that "reduce" is very rarely needed. Most of the times, "sum" is enough, sometimes with "start" values customized (set it to [] to flatten an array for example). It is both easier to read, faster, and needs no imports. It also works great with list comprehensions - "sum(foo(x) for x in input if x > 5)" is much easier to read than reduce equivalent.

If you are multiplying, you are likely doing heavy math, and you'll be using numpy - which does not need reduce either.

If you are going to return a list of dict, then it's much faster to mutate the results, so using "reduce" will have significant performance implications (unless you want to return input argument, mis-using it as a glorified "for" loop)

And if returning not a list/dict, if you can use "min" or "max" or "any" or "all" or "next" (take the first element), then you should use it - it will be easier to read and faster too.

So what does this leave us for "reduce"? Frankly, not much. I've only seen it in merging immutable status codes, and that was pretty niche usecase to begin with.

(this was all for Python. In other languages without nice list of built-ins reduce might make more sense)

show 2 replies
billyp-rvalast Monday at 7:37 PM

Well yeah, it's the lowest-level array function. All of the others can be written with reduce, but not vice-versa. Of course it's going to be less friendly.

g8ozlast Monday at 9:09 PM

I've always like reduce myself, didn't realize others had a negative attitude towards it.

WesolyKubeczektoday at 6:46 PM

I like neither of the three and prefer for loops and if statements instead. Yay for shallower stacks!

eimrinelast Monday at 3:01 PM

Reduce requires knowing that the sum of zero entities is zero but the multiply of zero entities is one. They forget to throw the correct number and think that reduce() just do not work for them.

semiinfinitelytoday at 6:30 PM

you guys still reading and review code with ur eyes and brain?