logoalt Hacker News

Skeimelast Monday at 8:09 AM2 repliesview on HN

I think this is because in an imperative language, `reduce` does not actually give you much over a `for item in collection` loop. With `map` and `filter`, you immediately learn something about the result (it's a list of the same length as the original, with each item only depending on the corresponding original item; it's a list containing some of the original elements unchanged and nothing else). This is useful, so `map` and `filter` are good.

With `reduce`, the result could be anything, and in an imperative language, side effects are also possible. So it's just a loop with worse syntax.

(Admittedly, in an imperative language, `map` and `filter` could also have side effects, though I think most people would consider this bad style.)


Replies

Someonelast Monday at 11:16 AM

I think what makes reduce less popular is that it takes two lambdas:

- a slightly awkward one that takes a partial result and the next value to produce a new partial result

- one that maps the final partial result to the result

Also, in many languages, when reading the code, you have to skip initialization of the partial result, read the lambda, and then jump back to make sense of the initial values

I think something like awk’s syntax, with BEGIN and END blocks would improve on that. Example of a first go at such syntax (needs work):

  Items.BEGIN
    min = ∞
    max = -∞
    sum = 0
    n = 0
  ITER
    min = Min(min,_)
    max = Max(max,_)
    n += 1
    sum += _
  RETURN
    average = sum / n
    (min, max, average)
Advantages:

- items in the partial results have names, making them easier to understand

- result also is easier to understand

Price paid is wordiness, and you cannot simply write a function name for either of the lambdas.

However, I think the latter only is useful in case the partial result is the final result. There, you can keep

  sum = items.reduce(0,+)
if you want to.
Hackbratenlast Monday at 9:10 AM

I think that in every imperative language that offers `map`, `filter`, `reduce`, or similar, the written contract of this API should state that any higher-order function handed to it as an argument must be free from side effects.

I think I’ve seen several language core APIs have this in their contract, e.g. `Stream#reduce` in Java [0] (emphasis mine):

> accumulator - an *associative, non-interfering, stateless* function for combining two values

[0]: https://docs.oracle.com/javase/8/docs/api/java/util/stream/S...

show 3 replies