Understanding Transducers in JavaScript

I’ve found a very good article explaining Transducers. If you are familiar with Clojure, go and read it: “Understanding Transducers”. But if you are JavaScript developer and not used to read lisp code, I’ve translated code examples from that articles into JavaScript. So you can still read the article and check code examples here.

What are Transducers?

A quick noob intro: transducers are composable and efficient data transformation functions which doesn’t create intermediate collections.

Here’s a visualisation to show the difference between array built-ins and transducers.

chained built-in transformations create intermediate arrays
transduced transformations process items one by one into output array

Why use them?

The above visualisation means that given transformations like map, filter and reduce we want to compose them together to pipe every piece of data through them step by step. But the following is not this kind of composition:

array
.map(fn1)
.filter(fn2)
.reduce(fn3);

Instead we want something like this:

const transformation = compose(map(fn1), filter(fn2), reduce(fn3));
transformation(array);

This way we can reuse the transformation and compose it with others. But the problem is that map, filter and reduce functions have different signatures. This is why all of them need to be generalised and it can be done in terms of reduce.

Code examples from the article

map and filter, and how they can be combined together:

map and filter can be implemented using reduce. Here’s map implementation:

Let’s extract incrementing function to allow it to be passed into reducer:

More usage examples of map reducer:

filter implementation using reduce:

Again, extract predicate function, so it can be passed from the outside:

Combine both reducers together:

Similar to what you usually do with built-in array methods:

Here are both reducers again and both of them are using array push as a reducing function:

push and + are both reducing functions, they take initial value and input, and reduce them to a single output value:

Let’s extract reducing function, so it can be also passed from the outside:

Here’s how reducers can be used now:

The type of reducers is result, input -> result:

Composition of reducers has the exact same type:

So it also can be used as a reducer:

Let’s use R.compose from Ramda library for better readability:

More complex example:

Finally let’s wrap it into transduce helper function:

Usage example:

Check out transducers-js library for a complete and performant transducers implementation in JavaScript. Read about Transducer protocol which allows different libraries to connect together (like Lodash, Underscore and Immutable.js).