Pipe Function in JavaScript

Hello, in this blog post I am going to explore and implement a pipe function in JavaScript(JS). Here is a mini table of content for this post:

  1. What is a pipe function?
  2. Implementing a simple pipe function.
  3. Refactoring the pipe function.
  4. Allowing our pipe function to accept more than two functions.
  5. Application of pipe function in daily life.
  6. Why is pipe function necessary?

1. What is a pipe function?

Technical Definition:

I wasn’t able to find the exact definition for pipe function, until I searched for the term piping. The idea of piping was originally brought to life by Douglas McIlroy, while working at Bell’s Lab. Although the terminologies are different the given definition for piping applies to pipe functions. Here is how Unix defines piping,

A pipe is a form of redirection that is used to send the output of one program to another program for further processing.

Hmm…? In order to get a slightly better understanding, lets look at another definition but from a programming perspective,

pipeline consists of a chain of processing elements, arranged so that the output of each element is the input of the next.

My Definition:

I’ll be honest, the first time I read the two definitions on piping I was lost. However, after reading it multiple times I was able to conjure up my own definition,

A pipe function takes an n sequence of operations; in which each operation takes an argument; process it; and gives the processed output as an input for the next operation in the sequence. The result of a pipe function is a function that is a bundled up version of the sequence of operations.

Let’s solidify our idea of pipe function by implementing and using it.

2. Implementing a Pipe Function:

So we know that a pipe function takes an n sequence of operations and returns a function. Before tackling the whole problem, let’s first solve a small sub problem. By finding a solution to a sub problem, we can then generalize the solution to solve the main problem. So, our sub problem is to implement a pipe function that takes any 2 operations and returns a function.

Let’s say my friend and I bought a large pizza and I want to know how much the pizza costed each of us, after tax.

Lets walk through the code to get a better understanding. Lets start with our operations:

  • The first operation calcTotalWithTax takes a cost and returns the cost plus tax.
  • The second operation costForTwo takes total cost and returns the total cost divided by two.

We pass the operations in their respective order to our pipe function. The pipe function returns a new function splitTotalCost that accepts the cost of pizza and return the split cost.

3. Refactoring the Pipe Function:

Here is the current pipe function, which requires a variable to hold op1 output and takes up three lines of code.

We can refactor the current pipe function into the following statement,

Now the pipe function is declared in a single line; as well as, reducing the need for an extra variable to hold op1 output.

4. Accepting More Than Two Functions:

The current status of the pipe function accepts only two functions; however according to the definition, a pipe function should be able to accept n number of functions. According to the definition, the pipe function should be something like the following pseudocode:

const pipe = (op1, op2, op3, ... , opN) => {
return (arg) => opN(op3(op2(op1(arg))));

Luckily with ES6, the pseudocode is very close to the actual solution. The pipe function needs to accept an arbitrary number of arguments(operations). This is made possible with rest parameter. According to MDN rest parameter,

represents an indefinite number of arguments as an array.

Utilizing our current pipe function and rest parameter syntax, a new pipe function can be produced that accepts n number of functions.

Here is the refactored version of the function:

Pizza example continued:

Let’s expand on the previous pizza example, by converting the split cost into US dollar for my American friend.

Explanation of the code:

  1. Before examining the code, we must have a good understanding of how reduce function works. If we don’t provide an accumulator to the reduce function initially, then the reduce function will pass the first and second element of the array into the callback, only for the first iteration. Which is what we desire.
  2. The operations are then passed to _pipe function which returns a bundled function, that becomes the accumulator for the next iterations.

5. Application of pipe function in daily life.

Recently I was working on a project that involved retrieving a JSON containing an array of objects. The objects represented school names and their addresses. And my objective was to display the received schools as an un-ordered list containing the name and address.

Let see how the problem can be tackled using pipe function.

6. Why is pipe function necessary?

Sure, pipe functions are pretty neat, however that is besides the point. Pipe functions supports the idea of negative coding. Negative coding is the process of reducing the lines of code from your code base while keeping the existing functionality of the program. This makes sense since the Douglas Mcllory (who brought the idea of pipeline to Unix) said the following,

The real hero of programming is the one who writes negative code.

By reducing the number of variables used and lines of code, the code base becomes cleaner; and easier to read, maintain and test.