Programming as Transformations of Data

Published Jul 05, 2016Last updated Jan 17, 2017
Programming as Transformations of Data

Functional programming is about transformations. Transformations of immutable data. Let's look at this example:

const execute = () => {

This looks good but each method assumes a lot of things.
readInput puts data in a variable shared by all the methods.
processData assumes that the input has already been read.

If you forget to call readInput before processData, things will break at runtime. Same goes for generateOutput, which assumes that data was processed before it was called. Bring concurrency into the mix and things will fall apart pretty quickly.

Let's try to improve it:

const execute = () => {
  const input = readInput();
  const result = processData(input);
  const output = generateOutput(result);

The functions here don't make any assumptions. They just get data as parameters and they work only on those parameters without assuming anything else. Each of those are small programs that work independently and execute is just another program that plugs them together.

But there are a lot of unnecessary intermediate variables. Let's see what happens when we remove them:

const execute = () => generateOutput(processData(readInput()));

This is like Arabic now, we have to read it from right to left. It's easy to fix this issue by defining a pipe function that takes a value, and passes it to another function. Unix pipes are like water pipes. They take values (or water) and pass them to another programs (or to a sink, a bucket, or to any other location).

const pipe = (v,f) => f(v);
const execute = () => pipe(pipe(readInput(), processData), generateOutput);

This time you read it from left to right, but the core of the program is still hidden in the multitude of pipe calls. If we can change the pipe to the symbol | and if we could use infix notation while calling it (e.g. pipe(a,b) = apipeb = a | b) (Javascript unfortunately doesn't allow us to do that), it will convert to:

const execute = () => readInput() | processData | generateOutput;

This time, it doesn't assume state, doesn't have intermediate variables, reads beautifully, conveys the gist of the program elegantly and more importantly, is an expression. readInput, processData and generateOutput are all small programs that can work independently. The execute function just composes them together. This builds on the Unix philosophy of having small composable programs. Since they share no state, there are no concurrency issues here as well. It's easy to unit test these functions because they are different units – they are not bound to other units via state. You don't need mocks/stubs to test them. You get all of this for free if you just forgo the state πŸ˜ƒ

BTW, we can get close to what we have above if we move the pipe to Object prototype:

Object.prototype.pipe = function(f) { return f(this); };
const execute = () => readInput().pipe(processData).pipe(generateOutput);
Discover and read more posts from Abdulsattar Mohammed
get started