The Pipeline Operator In JavaScript
There is a very early proposal for the next version of JavaScript (aka ESNext proposal) which I’m really eager to see making it through.
I’m talking about the pipeline operator |>
.
Immediately, all the guys coming from the functional world know what I’m talking about. Pipeline operator has been for some time in Elm, Elixir, ReasonML and many other functional languages. Unix users can also feel some familiar vibes.
So what is a pipeline?
Well, the basic idea is very straightforward. But let’s consider a problem first.
THE problem
Very often (or even always?) the code we write is about a series of transformation. We take some piece of data and consequentially transform it to get in the end the desired result.
Let's look at the example you will probably never see in real life ;) We’ll pretend we have the infamous lodash library at our disposition.
function maxSalary(departments) {
// finds the maximal salary across the whole company
const employees = _.flatten(_.map(departements, (d) => d.employees));
const salaries = _.map(employees, (e) => e.salary);
return _.max(salaries);
}
Here we take the departments array and then transform it several times to get the max salary amongh all the workers.
And it apparently works. But it introduces several variables along the way which are not really needed here. The alternative is to write the whole expression in one very long row.
function maxSalary(departments) {
return _.max(
_.map(_.flatten(_.map(departements, (d) => d.employees)), (e) => e.salary)
);
}
I don't even have to tell why it is ugly, right? It's simply incomprehensible. We have to read it from right to left (or rather from the middle and sideways), which for most of us is hard.
Until now, there was only one way to streamline that expression but only if having a luxury of using the lodash. Which by chance we do.
function maxSalary(departments) {
return _(departements)
.map((d) => d.employees)
.flatten.map((e) => e.salary)
.max();
}
Much better, isn’t it? We don’t introduce any new variables and still, our code is pretty clean. The flow is going from top to the bottom and from left to the right.
The only downside to this approach is that you a have to pack and unpack your data. And you gotta use a 3rd party library, of cause.
Pipeline to the rescue
So finally, let’s see how the proposed pipeline operator helps to fix this.
function maxSalary(departments) {
return (
departments
|> _.map((d) => d.employees)
|> _.flatten
|> _.map((e) => e.salary)
|> _.max
);
}
The |>
symbol in this code is the pipeline operator. It takes a piece of data on the left side and passes it to a function on its right side.
And this is not a new concept but rather just a syntactic sugar.
Expressions Math.sqrt(64)
and 64 |> Math.sqrt
are equivalent. The difference is in how we read it. With pipeline operator data flows from left to the right, and thus making it much more comprehensible without the need to introduce extra variables.
Conclusion
I gotta say that this proposal is at a very early stage. There are actually two proposals now competing with each other, each has its own opinion on how async/await should work with pipelines and how to use the partial application, which is a different proposal (stage 1 as I’m writing this).
Still, I’m pretty sure it will go through pretty soon. The benefits are quite obvious especially for those who tried it in other functional languages.
I mean c'mon guys, we have dragged freaking symbols into JavaScript.
Where to go from here
- As usual, I will be glad to connect on Twitter
- The official TC38 proposal