Reduce everything
Photo by Mark TegethoffI believe reduce is the single most important functional programming concept and function.
It's called "reduce" because it reduces a collection of values into a single one.
Of course it's available in all modern languages, and here's how to reduce an array of numbers into it's sum using javascript:
[1, 2, 3, 4].reduce((sum, value) => sum + value, 0); // => 10
And here the beautiful ruby expressiveness:
[1, 2, 3, 4].reduce(0, :+) # => 10
The reducer #
Notice that arguments are the same in both examples, but changed in order. reduce always requires two parameters: an initial value and a reducer [1]
A reducer is a function that, given a previous value (or the initial value) and an item from a collection, returns the next value.
This is the reducer from the previous example in js:
// the reducer:
const add = (a, b) => a + b;
// reduce:
[1, 2, 3, 4, 5].reduce(add, 0); // => 10
Of course, in ruby the reducer is just +
👏
Reduce everything! #
One important detail of reduce is that the values of the collection and the result does NOT have to be of the same type. Even the result doesn't have to be a single object.
You can reduce a list of numbers into an object, a list into a another list (with elements of different types) or whatever you imagine (stream of events into http calls...)
This versatility makes it the most important function because it allows to implement any other enumerable function using it as a base [2]
Versioning #
One popular and easy way to implement versioning in a data stores is by saving, not all the values, but the difference between the current and the next. Storing the change, the update, the action, the event... git, for example, uses this technique.
With this kind of data stores you end with an enormous list of changes.
The point is that you can get the actual value at any moment of time just by following the changes from the very beginning to the time you want. How? By reducing the changes.
With an appropriate reducer you can reduce a collection of "updates" into a single value at any point of time. It's not performant but it's easy.
State + Reduce = redux #
Now imagine that the initial value is a state. It could be the state of a UI component or the state of a whole app. Doesn't matter.
You have an initial state and a collection of (user) events. With the appropriate reducer you can obtain the current state at any point.
That's what redux is all about. You just have to write the appropriate reducer: a function that, given a current state and a (user) event (called "action" in redux), returns the next state.
redux just reduces a collection of actions into the current (app) state. And makes easier for us to write that reducer (using composition 🤪).
And by the way, you get time traveling debugging for free
Notes:
[1] Well, not strictly true: the initial value can be the first value of the collection.
[2] You can write any enum function with reduce, but the opposite is not possible. That's why reduce is the mother of all enum functions. For example, this is "map" implemented with "reduce".
Javascript:
const map = (array, fn) => array.reduce((result, item) => {
result.push(fn(item));
return result;
}, []);
// usage:
map([1, 2, 3, 4, 5], (n) => n \* n) // => [1, 4, 9, 16, 25]
And ruby:
def map(enumerable)
enumerable.reduce([]) {|total, current| total << yield(current)}
end
# usage:
map([1, 2, 3, 4, 5]) {|n| n \* n} # => [1, 4, 9, 16, 25]