Functional pipeline

Tiny library functional-pipeline composes functions in left to right order.

JavaScript treats functions as first class objects: you can easily pass them around and combine into new functions. Functions are often used as callbacks, for example in array iterations

1
2
3
[1, 2, 3].forEach(function (value) {
console.log(value);
});

When composing several functions into a callback function, we either write a new function explicitly (like the example above), or use helpers, like lodash.compose.

1
2
3
4
5
6
[1, 2, 3].forEach(function (value) {
console.log(value * 2);
});
// or
function double(value) { return value * 2; }
[1, 2, 3].forEach(_.compose(console.log, double));

Notice that the second approach has certain advantages: it removes actual code and forces the function double to be defined separately. This has a side benefit: you can easily unit test double.

I like compose function, except it is hard to read. If we look at the order of operations in the example above, the steps first read left to right, but the callback reads right to left (inside out):

1
2
3
[1, 2, 3].forEach(_.compose(console.log, double));
________ ___________ ______
1 3 2

It reads: for each value (1), double it (2), then log to console (3).

The operation order is even harder to read without composition helper. Here is a typical callback to sort values by a property

1
2
3
4
5
items = _.sortBy(items, function(item) {
return new Date(item.latest_event.datetime);
------ -------- ------------ --------
4 3 1 2
});

It reads: take property latest_event (1), then its property datetime, then construct a new Date object (3) and then return it (4).

The switching of reading order plus writing code makes the code:

  1. Harder to understand
  2. More prone to errors (which correlates with #1)

Remember, you can only make money programming, if you write code once and reuse it for a long time. Thus you should strive to make the code as readable as possible.

functional-pipeline

I noticed a common pattern in callbacks: usually there is a single argument passed between functions, and the callback alternates between property access, method call or a function call. I wrote functional-pipeline - it is a single function that makes left to right functional composition super easy.

You load the function from node or from the browser:

var fp = require('functional-pipeline');
// or
<script src="bower_components/functional-pipeline/fp.js"></script>

However you load fp function, it can construct new functions that operate on first argument

1
2
var newFunction = fp('property name', 'or method name', orFunction, ...);
newFunction(foo);

Instead of writing the code to sort values as above, you can define single pipeline in a single shot:

1
2
function newDate(a) { return new Date(a); }
items = _.sortBy(items, fp('latest_event', 'datetime', newDate));

Notice the clear operation order: grab property latest_event, then datetime, then apply newDate function. The return operation is implicit at each step.

I liked using the verb define when talking about functional pipelines, since we remove actual code. If there are errors in the data, we can easily debug the pipeline by using its debug configuration (see README.md). The debug fp function will check each argument during the definition step, or during actuall callback execution to give you a very detailed error message.

Bonus

I wrote d3-helpers that has a few little utility functions, like creating a new Date object as a function which you can use with the functional pipeline.