Explaining the "reduce" function to imperative programmers

In “typical” programming languages, you often have a structure called a for-loop. The idea is that you take some values and for each of the values you do something.

For example to sum up the values in an array, we can do

total_sum = 0
for value in [5, 10, 2] {
  total_sum = total_sum + value
}

Now the sum is stored in the total_sum variable

In functional programming, for-loops aren’t a really thing. Functional programming isn’t about saying “now do this in this specific way”. In functional programming you express what needs to happen through functions who map a value to another value. All of this is a bit theoretical, though. The truth is that you still need to do things like listening for input, writing to output, storing things, etc. And it still uses the same underlying technology where a cpu will actually do the same kind of things like with another language. So in the end, it’s really just the same, we just express things slightly differently.

A for-loop is a way to handle an ordered list. In functional programming we will often use the reduce function for that. And when you look at it closely enough, you may find that this reduce function is really a for-loop in disguise.

reduce(enum, acc, fun)

Is equivalent to

for value in enum {
  acc = fun(acc, value)
}

Here enum stands for “enumerable”, acc stands for “accumulator”, and fun stands for “function” and also for how fun functional programming can be.

Let’s take a closer look using our previous example. We start with this

total_sum = 0
for value in [5, 10, 2] {
  total_sum = total_sum + value
}

Everything between the brackets of the for-loop can be considered a function who takes the accumulator, in this case total_sum, and the current value, in this case value, and maps this to a new value for the accumulator. Our accumulator is the total_sum variable, and the enumerable is obviously our array [5, 10, 2].

Written a bit differently, we get

enum = [5, 10, 2]
acc = 0
fun = (acc, value) => { return acc + value }

for value in enum {
  acc = fun(acc, value)
}

And thus we can replace the for-loop with the reduce function

total_sum = reduce([5, 10, 2], 0, (acc, value) => {
  return acc + value
})

Neat, huh.

Here we “reduce” the numbers in the list to one number, but that’s not the only thing we can do. We can also create a new list based on the values of our original list. You can map each value to a possibly different value in the new list. Or create a list with certain values filtered out. In functional programming, languages have often implemented filter and map functions for that, but they are really just reduce functions underneath.

Let’s filter odd numbers

filtered_list = []
for value in [5, 10, 2] {
  if is_odd(value) {
    filtered_list = filtered_list + [value]
  }
}

This can be written with reduce

filtered_list = reduce([5, 10, 2], [], (acc, value) => {
  if is_odd(value) {
    return acc + [value]
  }
  return acc
})

Or shorter with a filter function

filtered_list = filter([5, 10, 2], is_odd)

Or maybe we want to map the values to a list but with the values multiplied by two

multiplied_values = []
for value in [5, 10, 2] {
  multiplied_values = multiplied_values + [value * 2]
}

which eventually can be written with a map function as

multiplied_values = map([5, 10, 2], (value) => { return value * 2 })

And if you first want to filter out the odd numbers, and then map the result to multiples of two, you can easily do that in two steps

enum = [5, 10, 2]
filtered_list = filter(enum, is_odd)
filtered_multiplied_values = map(filtered_list, (value) => { return value * 2 })

Making it nicely short and easy.

Most languages will even allow you to “chain” things. For example, Elixir has the |> operator allowing you to chain functions with the output of the previous function becoming the first input of the second function. Using such an operator we could get

filtered_multiplied_values =
  [5, 10, 2]
  |> filter(is_odd)
  |> map((value) => { return value * 2 })

These days, non-functional languages, will often implement these functions as a method of Arrays or other enumerables.

filtered_multiplied_values = [5, 10, 2].filter(is_odd).map((value) => { return value * 2 })

Allowing you to write very compact, but also very expressive and readable, code.