Hacker News

Top stories

Live mirror
30 storiesupdated just nowView source snapshot
  1. Training a 4B model to produce 81% faster query plans than Postgres(rohanbansal.com ↗)
    14comments
  2. Vectorized and performance-portable Quicksort (2022)(googleblog.com ↗)
    21comments
  3. Small programming tricks(will-keleher.com ↗)
    138comments
  4. Accurate Models of AMD Matrix Cores(arxiv.org ↗)
    3comments
  5. Dream-RSI: Recursive Self-Improvement through Evolving Worlds(arxiv.org ↗)
    46comments
  6. How good are frontier models at physics?(arxiv.org ↗)
    3comments
  7. Mistral X Mozilla: Private, Multilingual AI Browsing(mistral.ai ↗)
    169comments
  8. Show HN: An e-ink frame that hears birds and draws them as 1800s illustrations(github.com/arnegiacomo ↗)
    230comments
  9. Tell the speakers that you liked their talks(ohhelloana.blog ↗)
    60comments
  10. The Siberian Ice Maiden and the Scythian World(patrickwyman.substack.com ↗)
    discuss
  11. Reversing Factorio's RNG(gegell.github.io ↗)
    3comments
  12. How big are factorials?(thegreenplace.net ↗)
    27comments
  13. Learning Programming in an Age of LLMs(ploeh.dk ↗)
    165comments
  14. Claude Cowork and chat are now one Claude(claude.com ↗)
    177comments
  15. The DeepMind Institute(deepmind.com ↗)
    26comments
  16. ER visits for gambling disorders doubled after expanded online gambling market(utoronto.ca ↗)
    100comments
  17. The Google Play app review process now regularly takes longer than a week(gultsch.social ↗)
    302comments
  18. Hackers Got Inside a Flock Camera(wired.com ↗)
    185comments
  19. Why a fast-growing German AI startup is moving its parent company from the US(euronews.com ↗)
    7comments
  20. A coffee shop owner used AI to make a menu poster. Then came the angry DMs(businessinsider.com ↗)
    88comments
  21. Kyber (YC W23) Is Hiring a Forward Deployed Engineer(ycombinator.com ↗)
    discuss
  22. Show HN: How Stale Is Your AI? Release age and training cutoff for 20 models(stale.jock.pl ↗)
    41comments
  23. Barndoor acquires Diaphora, creators of open-source workflow runtime Frags(barndoor.ai ↗)
    discuss
  24. Can we stop with the uptime percentages?(jim-nielsen.com ↗)
    91comments
  25. Salesforce Global Outage(salesforce.com ↗)
    155comments
  26. Show HN: I made a flight simulator, except you're just a passenger(inflightsimulator.com ↗)
    199comments
  27. A warning about 'model welfare'(mustafa-suleyman.ai ↗)
    339comments
  28. Scaling Golang CI by Replacing actions/setup-go(cloudx.ai ↗)
    19comments
  29. This Code Is CRAP (2011)(googleblog.com ↗)
    48comments
  30. Introducing System One Models and Jev(typesafe.ai ↗)
    470comments

Anecdotally, Programmers Dislike "Reduce"

38 pointsby 2d agoevanhahn.com
54 comments
2d agoHN ↗

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.

2d agoHN ↗

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)

1d agoHN ↗

For 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

1d agoHN ↗

Has 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… :)

17h agoHN ↗

wow, 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!

2d agoHN ↗

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.

1h agoHN ↗

It's because it reduces data dimensionality. From 2d to 1d and from 1d to 0d (scalar).

1h agoHN ↗

It 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

18m agoHN ↗

No, reduce has exactly the same time complexity as map and filter.

2d agoHN ↗

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

2d agoHN ↗

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.

2d agoHN ↗

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.

1d agoHN ↗

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

1d agoHN ↗

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.

2h agoHN ↗

`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.

2h agoHN ↗

There 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.

1h agoHN ↗

Using briefer abstractions in place of nested loops is almost always preferable.

Indeed, this is why everyone knows the J programming language.

2h agoHN ↗

Nah. 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.

1h agoHN ↗

fold 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

1h agoHN ↗

No, the sum of the first ten natural numbers is always 55. It is not "initialised" to some other number beforehand.

1h agoHN ↗

I think part of the issue is that a lot of programming languages don't make a strong distinction between the two, and only provide the (more powerful) fold, but in a way that makes reduce operations harder to reason about (like OP said, with 0 types).

Associativity also makes fold hard. It's not super trivial to know when you might need e.g. left fold vs right fold

1h agoHN ↗

These are pretty close to each other, to the point where I wouldn't bother strongly distinguishing them.

Suppose we have foldr as in [A] -> B -> ((A, B) -> B) -> B, foldl as in [A] -> B -> ((B, A) -> B) -> B, and reduce as in [A] -> ((A, A) -> A) -> A.

Then we have foldr list value operator = reduce [\b -> operator a b | a <- list] (.), foldl list value operator = foldr (reverse list) value (flip operator), and in the case of a finite non-empty list and associative operator, we have reduce list operator = foldr (tail list) (head list) operator = foldl (init list) (last list) operator.

So these are all basically slight re-parametrizations of each other.

1d agoHN ↗

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

1h agoHN ↗

I’ve never ever heard while described as a smell, or even slightly smelly.

Care to explain?

55m agoHN ↗

If you can smell it, there's something fishy in the neighborhood

38m agoHN ↗

I 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)

15m agoHN ↗

You 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.

1d agoHN ↗

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.

1h agoHN ↗

The 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.

1h agoHN ↗

Isn'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)

52m agoHN ↗

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.

It's always worthwhile to consider what the result will be when you pass in an empty list.

15m agoHN ↗

Right. It's just one more thing you have to think about with Reduce that's not something you have to consider with Map/Filter.

2h agoHN ↗

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...

1h agoHN ↗

This always feels odd to me. It would seem a fairly straight forward optimization of the interpreter to special case the different types that it can reduce.

That is, why couldn't they have done the essentially same trick that you reference for += with reduce?

1h agoHN ↗

That sounds nice but in practice it’s probably not super helpful. Yes you could make a special case for reducing “+” over integers. But in python you can generally not promise that all the inputs are strictly integers, and you can’t even promise that your “+” function has no side effects.

1h agoHN ↗

There is no such trick. Python is only now getting those sort of JIT style optimizations, and that one in particular still hasn't hit. Do not use += on strings in a loop unless you are certain the iteration count will be small.

There is an optimization for lists, and maybe that's what GP is remembering. l += is functionally different from l = l +. The former mutates l, whereas the latter creates a new l. The difference matters when the line above is m = l. The mutation version will mutate m as well (they're the same reference), the creates new version will not. This optimization can just as easily turn into a footgun if the programmer is unaware of it, and in that sense is unpythonic.

1h agoHN ↗

Maybe you are misremembering the story? += deferred concatenation requires lazy strings and that didn't come until 10-15 years later. However, concatenating string lists with sum() was a common Python idiom at the time and it indeed incurred O(n^2) complexity. Gvr's reduce dislike was more about its syntax. It doesn't mesh well with Python's lambda syntax.

1h agoHN ↗

2006...would that have been Mondrian?

2h agoHN ↗

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

1h agoHN ↗

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.

1h agoHN ↗

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.

1h agoHN ↗

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.

1h agoHN ↗

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.

1h agoHN ↗

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

1h agoHN ↗

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.

1h agoHN ↗

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

1h agoHN ↗

accumulator

I had similar trouble, but I know call the "accumulator" just "previous" which makes it more logical in my head:

.reduce( (previous, current) => previous+current, 0 );

57m agoHN ↗

That's an interesting idea. I might get hung up for cases where "previous" is a different type from "current", like if you're reducing a list of objects into a single object. You've got the current item of the array and the current state of the accumulator, so they're kind of both current. Or you've got the last state and current item, but "last" is ambiguous.

In general, I find that if something is hard to describe in plain language, it's hard to code. Reducers are a bit clunky to talk about, which could make them harder to reason about, too.

1h agoHN ↗

It literally may be a syntax thing, but I too can never remember the exact arguments to put where so I never use it.

I think if `reduce` looked more functional or more like Erlang code, it'd be easier to read and digest.

1h agoHN ↗

In TS/JS you’re usually inlining the reducer fn, and there’s something hard to read/especially ugly about the comma after the bracket or arrow fn into the initalValue.

That said, when I’m reducing a list, I still use reduce.

1h agoHN ↗

I've worked with developers that were reduce maximalist. During PR reviews, anything that could be rewritten with reduce was flagged. One of the benefits of AI is not having to care as much about things like that.

55m agoHN ↗

I like it, but don't care for the name. I find it easier to think about in terms of an accumulator.