Visualizing transducers — Part 1 : compose() and pipe().

Rajesh Naroth
3 min readJun 3, 2019

--

A few months ago I read about transducers. Seemed like a very cool way to work on large amounts of data. Then I tried to visualize its internals and my mind went crazy. The end goal of this series is to ultimately explain what a transducer is. But to do that I must establish a visual representation for patterns such as map, filter, reduce and compose.

One step at a time.

Function basics

A function can have multiple inputs (arguments) but only one output (return value).

A unary function takes one argument, A binary function takes two arguments. Functions with arity (no of arguments) of 3 or more are rare in functional patterns.

A pure function has no side effects and returns the exact output for a given argument(s).

A function that returns a function or takes another function as an argument is called a higher order function.

When visualizing a function, try to imagine its shape based on the number of arguments it takes.

(If you copy the image, please provide attribution)

Composing functions

Composition is fundamental to functional programming. Composition allows you to combine two or more functions in a sequence. Consider the following functions:

const incr = x => x + 1;
const double= x => x + 2;
const triple = x => x * 3;

If you want to perform all these three functions to a number in that order, You could do:

triple(double(incr(x)))

Note how the output of incr() becomes the input argument to double(). Thus in composition, there is one implicit rule. You can only compose unary functions. When you have a function with more than one argument, you can curry them to convert them to a sequence of unary functions.

Back to the piece of code above. It is a maze of parentheses. The internals of the composition looks like this:

(If you copy the image, please provide attribution)

There is a lot of clutter even in pictorial form. To mitigate this, we can create a compose function. First let us write a function to compose two functions.

const compose2 = (f1, f2) => (x) => f2(f1(x))// and this for those who are still coming to grips with arrow functions:function compose2(f1, f2) {
return function(x) {
return f2(f1(x))
}
}

compose lets us express a sequence of operations in a more readable format. compose2 lets you compose two functions together. We could do a three function compose function like this.

const compose3 = (f1, f2, f3) => (x) => f1(f2(f3(x))

Thus instead of saying:

const multiply = x => triple(double(incr(x)))

We could say.

const multiply = compose3(triple, double, incr);

This can be visualized as:

(If you copy the image, please provide attribution)

Composition here follows a Right to Left (RTL) direction. This may seem a bit counter intuitive especially when you need to visualize the composition. A Left to Right flow would be more ‘natural’. This way of expressing composition is called pipe.

Pipe

We can derive pipe as follows:

const pipe3 = (f1, f2, f3) => (x) => f3(f2(f1(x)))
const multiply = compose3(incr, double, triple);
(If you copy the image, please provide attribution)

This the human mind can easily reason. A series of functions where result from one is passed to another. You can create generic versions of pipe and compose that takes in any number of functions. That is a discussion for another time. There are several examples you can find online.

Next up, we’ll look at map, filter and reduce.

--

--

Rajesh Naroth
Rajesh Naroth

Written by Rajesh Naroth

Frontend Architect, San Jose, CA

Responses (1)