4 ms·
Anecdotally, Programmers Dislike "Reduce"
- eimrine 17d agoReduce 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.
- theamk 17d agoAt 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)
- rsfern 17d agoFor numerical code I like einops.reduce more than numpy/pytorch sum reductions because you can reduce over named dimensions. It’s much more readable than having to reason through axis indexing again every time you come back to the code
- Pinus 16d agoHas the performance of sum on lists of lists in Python been fixed? It used to be pretty abysmal. But I suppose some would say that if you need to consider performance at all, you’re in the wrong language… :)
- theamk 16d agowow, TIL! Python 3.13.5 (main, Jul 15 2026, 20:25:40) [GCC 14.2.0] on linux >>> x = [[n]*1000 for n in range(1000)]; import timeit, itertools, functools, operator >>> timeit.timeit("len(list(sum(x, [])))", number=10, globals=globals()) 13.009033881127834 >>> timeit.timeit("len(list(list(functools.reduce(operator.add, x, []))))", number=10, globals=globals()) 12.941937348805368 >>> timeit.timeit("len(list(itertools.chain.from_iterable(x)))", number=10, globals=globals()) 0.0706032607704401 >>> timeit.timeit("out=[]; [out.extend(i) for i in x]; len(out)", number=10, globals=globals()) 0.06334403157234192 >>> timeit.timeit("len([i for a in x for i in a])", number=10, globals=globals()) 0.1232151910662651 mutable is fastest, itertools is just a bit slower, list comprehension is 2x slower, both "sum(..., [])" and "reduce" are 200 times slower!
- mahboi 15d agoYeah this is the kind of reason people dislike reduce
- evnix 17d agoThe 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.
- bjourne 15d agoIt's because it reduces data dimensionality. From 2d to 1d and from 1d to 0d (scalar).
- sigbottle 15d agoIt always messes with me: reducing across a specific axis always takes O(whole tensor) time, because there's no difference between "iterate over all dims, then collapse the final one" versus "iterate versus the first dim and do some cursed tensor accum" (and likewise for between) Maybe there's just a better way to think about it and I'm still thinking about it way too much like a programmer
- KingMob 14d agoBut that's not actually guaranteed at all. You "accumulate" an answer one item at a time, but there's no guarantee any dimensions are getting reduced. You can easily duplicate the effects of map with reduce, for example, so the dims would stay the same. You could even expand dimensions, if you like, turning a 1-d array with n elements into an s X t 2-d array. If the reducing function tracks the total number of elements seen, it can easily know when to start a new row. This is part of why people keep pointing out the name, "reduce", is a bit misleading.
- karmakaze 17d agoIt's part of the functional trio: map, filter, reduce--and half of MapReduce.
- billyp-rva 17d agoWell 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.
- slopnt 17d agoI 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.
- g8oz 17d agoI've always like reduce myself, didn't realize others had a negative attitude towards it.
- japgolly 17d agoI 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.
- wannabe44 15d ago> `fold` is awesome and super useful. It's the easiest and most convenient way to turn a collection into a single value. You will eventually learn about something called "for loop", and it will be nice.
- t-3 15d agoThere are way more places where a simple typo will ruin you in a for loop than a reduce or fold or map. Using briefer abstractions in place of nested loops is almost always preferable.
- robrenaud 15d ago> Using briefer abstractions in place of nested loops is almost always preferable. Indeed, this is why everyone knows the J programming language.
- mrkeen 15d agoNah. It involves multiple passes and setting the answer to the wrong value before (hopefully) setting it to the right value. Plus it forces you out of whatever lazy/streaming paradigm you had going on. If your foldr produces a list, downstream can start consuming it in constant memory as long as you let it do its thing.
- 8note 15d agofold kinda does too, for setting the first combined value that you are assembling, and thus on an empty list you end up with that wrong value, same as the for loop
- s-zeng 17d agoEven 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
- grebc 15d agoI’ve never ever heard while described as a smell, or even slightly smelly. Care to explain?
- polonbike 15d agoIf you can smell it, there's something fishy in the neighborhood
- theamk 15d agoI assume OP refers to the cases where "while" is used to re-implement existing operations... imagine finding code like this: i = 0 while i != len(todo): process(todo[i]) i = i + 1 sure, there may be a good reason to implement things this way (maybe "todo" grows during iteration?), but maybe not, and then the loop should be instead simplified to: for value in todo: process(value) (as an aside, this is exactly the case where the comments are required: "# not using for loop because todo might grow" will make it clear it's an intentional decision and not hallucination or something written from ignorance)
- grebc 15d agoYou can use iterators in a while loop like your for example, making it look as clean as the for. I feel like this is a case of personal preference over actual issue.
- snackbroken 17d agoMap 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.
- mrkeen 15d agoIt's pairwise, not global reasoning.
- snackbroken 15d agoThe accumulator is global state. If you're folding from list<int> to int you're right that it's (usually) effectively a pairwise operation on ints. If the fold is something like list<foo> -> tree<bar> then you have to reason about each intermediate (tree<bar>, foo) -> tree<bar>, i.e. how global state should evolve over time with each update.
- deleted 15d ago[deleted]
- sigbottle 15d agoIsn't reduce usually used for monoidal operations? Or do people implicitly absue ordering? If the algortihm doesn't work the same forward, backwards, and with a tree scan, it ain't reduce (as a first approximation not IFF)
- snackbroken 15d agoThat's what I'm used to as well, but in my experience a lot of programmers take fold and reduce to be synonyms. A monoidal reduce is much less "scary" than a general fold. I suspect most programmers have never[1] heard the word monoid, let alone know what it means, and having to remember the meaning of a weird new word is enough to make most people dislike something compared to the simpler more familiar operations. [1]Or if they have, their only encounter with it is the "a monad is just a monoid in the category of endofunctors" meme.
- dang 15d agoComments moved to https://news.ycombinator.com/item?id=49692844 https://news.ycombinator.com/item?id=49692844, which was posted a bit earlier