November 28, 2017

Sweet chunk of functional paradigm for you today. I don’t know why did I write “versus” while they compliment each other. Anyway, let’s get to the good stuff…

Simply speaking a `Reducer`

is a function that takes an accumulation and a value, and then returns a new accumulation.

You are already familiar with reducers if you’ve used the `Array.prototype.reduce()`

method. The `.reduce()`

function itself is not a reducer!
It iterates over a collection and passes values to it’s “callback” that is a **reducer** here.

Let’s imagine that we have an array with five numbers: `[1, 2, 3, 14, 21]`

and we want to find the biggest of them.

```
const numbers = [1, 2, 3, 14, 21];
const biggestNumber = numbers.reduce(
(accumulator, value) => Math.max(accumulator, value)
);
// 21
```

The arrow function here is a reducer. The `.reduce()`

method only takes the result of previous reduction and calls the reducer with it and next element of an array.

Reducers can work with any kinds of values. The only rule is that the accumulation you return should have the same type that the accumulation you pass in.

For example you can easily create a reducer that will work with strings:

```
const folders = ['usr', 'var', 'bin'];
const path = folders.reduce(
(accumulator, value) => `${accumulator}/${value}`
, ''); // Here I passed empty string as an initial value
// /usr/var/bin
```

Actually it’s better to illustrate without `Array.reduce()`

method. Look:

```
const stringReducer = (accumulator, value) => `${accumulator} ${value}`
const helloWorld = stringReducer("Hello", "world!")
// Hello world!
```

The other cool thing about reducers is that you can chain them to perform a series of operations on some data. This opens up huge possibilities for composition and reuse of small reducer functions.

Let’s say you have an ordered array of numbers. You want to get even numbers from it and then multiply by 2.

The ordinary way to do it would be to use `.map`

and `.filter`

functions:

```
[1, 2, 3, 4, 5, 6]
.filter((x) => x % 2 === 0)
.map((x) => x * 2)
```

But what if your array had 1000,000 elements? You have to loop through the whole array for every operation, that’s extremely ineffective.

We need some way to combine the functions we passed to `map`

and `filter`

. But we can’t do this as they have different interface. The function
that we passed to `filter`

is called **predicate** and it takes a value and returns **True** or **False** depending on inner logic. And the function
we passed to `map`

is **transformer** function. It takes a value and returns **transformed value**.

We can achieve this with reducers, let’s create our own **reducer** version of `.map`

and `.filter`

functions.

```
const filter = (predicate) => {
return (accumulator, value) => {
if(predicate(value)){
accumulator.push(value);
}
return accumulator;
}
}
const map = (transformer) => {
return (accumulator, value) => {
accumulator.push(transformer(value));
return accumulator;
}
}
```

Great, we used **decorator** functions to wrap our reducers. Now we have `map`

and `filter`

functions that return **reducers** that can be passed to `Array.reduce()`

method!

```
[1, 2, 3, 4, 5, 6]
.reduce(filter((x) => x % 2 === 0), [])
.reduce(map((x) => x * 2), [])
```

Great, now we have a chain of `.reduce`

function calls, but we still can’t compose our reducers! Good news is there is only one step left. To be able to compose reducers we need to be able to pass them to each other.

Let’s update our `filter`

function so it would also accept **reducer** as an argument. We are going to decompose it and instead of pushing value to **accumulator** we’ll allow
the passed in **reducer** to perform it’s logic.

```
const filter = (predicate) => (reducer) => {
return (accumulator, value) => {
if(predicate(value)){
return reducer(accumulator, value);
}
return accumulator;
}
}
```

This pattern where we take a **reducer** as an argument and return another **reducer** is called **transducer**. As it’s a combination of **transformer** and **reducer** (we take a reducer and transform it).

```
const transducer => (reducer) => {
return (accumulator, value) => {
// Some logic involving passed in reducer
}
}
```

So basically transducer looks like this `(oneReducer) => anotherReducer`

.

Let’s rewrite our **mapping** reducer as well:

```
const map = transformer => reducer => {
return (accumulator, value) => {
return reducer(accumulator, transformer(value));
};
};
```

Also we need to add a final reducer that will push values to array for us:

```
const finalReducer = (acc, val) => {
acc.push(val);
return acc;
};
```

Now we can combine our **mapping** reducer and **filtering** transducer and do our calculations in one run.

```
const evenPredicate = (x) => x % 2 === 0;
const doubleTransformer = (x) => x * 2;
const filterEven = filter(evenPredicate);
const mapDouble = map(doubleTransformer);
[1, 2, 3, 4, 5, 6]
.reduce(filterEven(mapDouble(finalReducer)), []);
```

Actually we could make our map method a transducer as well and continue this composition indefinitely.

But just imagine having to compose more than 2 transducers. We have to find more convenient way to compose them.

Basically we need something that would take a number of functions and compose them in that order.

`compose(fn1, fn2, fn3)(x) => fn1(fn2(fn3(x)))`

Luckily a lot of libraries provide this kind of function. For instance RamdaJS. But for educational purposes let’s create our own version.

```
const compose = (...functions) =>
functions.reduce((accumulation, fn) =>
(...args) => accumulation(fn(args)), x => x)
```

The function is very compact, let’s break it down.

Imagine that we called that function like this `compose(fn1, fn2, fn3)(x)`

.

First look at the `x => x`

part. In lambda calculus it’s called **identity function**. It just returns whatever it takes as an argument without changing. We need it here to start our unfolding.

So after fist iteration we’ll have that **identity function** (for convenience let’s call it **I**) called with the **fn1** function as an argument:

```
(...args) => accumulation(fn(args))
// STEP 1
// We pass our fn1 to accumulation
(...args) => accumulation(fn1(args))
// STEP 2
// Here we basically substitute accumulation with I
// and fn and fn1
(...args) => I(fn1(args))
```

Yay, we calculated the `accumulation`

value after the first iteration. Let’s do the second one:

```
(...args) => I(fn1(args)) // Our new accumulation
// STEP 3
// Now we pass fn2 to our accumulation
(...args) => accumulation(fn2(args))
// Step 4
// Lets substitute "accumulation" with it's current value
(...args) => I(fn1(fn2(args)))
```

I think you got the idea. Now just repeat steps 3 and 4 for `fn3`

and voila, you’ve converted your `compose(fn1, fn2, fn3)(x)`

to `fn1(fn2(fn3(x)))`

.

Now we can compose our `map`

and `filter`

like this:

```
[1, 2, 3, 4, 5, 6]
.reduce(compose(filterEven,
mapDouble));
```

I suppose you already knew about **reducers**, and if not – you’ve learned a nice abstraction to work with collections. Reducers are great to fold different data structures.

Also you’ve learned how to do your computations effectively using **transducers**.

Share this post: