Babel pipeline operator proposal
The community has been working on the pipeline operator proposal for quite sometime now. To be precise, they managed to release the minimal variant of this proposal in Babel 7.0.0-beta.
The minimal proposal covers only the basics around the pipeline operator, so features like partial function application and async/await don’t work.
They decided to play around with this proposal and work with few more variants on the same operator, which actually extend the minimal variant. Some proposed variants didn’t make it, like Hack or Split style variants, but we have two at the moment, which both are available in Babel 7.5.0 version. The people who are working on this proposal want to compare the trade-offs between the two competing proposals, which as I mentioned earlier, are based on the minimal variant.
One of the competing proposals is the Smart style variant, which was introduced in Babel 7.3.0, while the other is the F# style variant, which has been introduced in Babel 7.5.0. To use any of these three variants (minimal, smart, F#), you must use a Babel plugin. More on this below.
The pipeline operator (
|>) takes two arguments. The second one, on the right side, is a function, while the first one, on the left side, is a value. The pipeline operator provides that value as an argument to the function on the right and returns the result.
If you think about it, it’s very similar to chaining functions, like the old-fashioned jQuery for example, however it’s not limited to intrinsic methods of an object and also it’s way more declarative.
This technique is commonly used to pipe multiple operations in one go. As discussed above, the operator passes a value from the left side to a function on the right side. For the next operation, it passes the result of the previously executed operation, which is a value, to the function on the right and so on and so forth. This continues, until there’s no operation left, i.e. no more pipelining.
Such coding style is very common in FP languages like F#, OCaml, Elixir, etc. and also it can be combined with other powerful functional programming techniques like partial function application and function composition.
Why use it?
Generally speaking, in the functional world, this is a very useful operator. FP languages are notorious of their declarative programming style, which results in more readable code, well at least for people familiar with the language, as for developers who come from an OOP background the code might look cryptic. 😥
However, it’s proven when using these languages, the clutter is minimal and the code has a tendency to read naturally from left to right, like a proper sentence, which makes it easier for one’s brain to follow the code flow and finally make sense from the jargon.
The F# and Smart variants are quite similar in how they operate, however the smart variant uses a
# topic reference. For this variant, each step of the pipeline creates its own lexical scope, with the
# topic reference being the result of the previous step. You don’t need to use arrow functions, you just provide the result via the topic reference.
.babelrc file if you wish to use this technique. You will need the @babel/plugin-proposal-pipeline-operator plugin for the pipeline operator to transpile.
npm install @babel/plugin-proposal-pipeline-operator --save-dev
And in the
.babelrc set the plugin as follows for fsharp.
Note: For other variants, set the
proposal property either to
The partial application proposal supports the
? reference, which is similar to the
# topic reference in the smart variant, but you can pass it only in function calls.
npm install @babel/plugin-proposal-partial-application --save-dev
Then add it to your babel plugins in
.babelrc as follows.
Let’s see an example with the F# variant. First, you have to download and install the babel plugin as shown above. I am going to use partial function application, so make sure to install and add as plugin the partial application proposal plugin as well. See the gist above about the plugins.
In this example I have a list of order items, which makes an order. I want to calculate the final price for the items in stock and apply a discount of 20%, as the client has a coupon. The operations I mentioned earlier are all performed in sequence thanks to the pipeline operator. Notice the
applyCoupon function returns a function which receives a total, but I don’t provide the total explicitly in the parameters. This is called partial function application, the value is passed implicitly by the pipeline operator.
Finally, let’s see how this works with async/await.
In this example I want to fetch a country from a remote API and transform the response to an object that I want to show in console. Because I am running these examples in NodeJS and not in browser, I’ve installed a package called
node-fetch, in order to use the fetch API.
The pipeline and the
await operator work very nice together as we can see. Notice the
await operator must go after the promise/asynchronous code, which in this example are the
json methods, which both return a promise. So, always remember to put your await after the promise when pipelining. Think of
await as a function, which takes one
Promise parameter, unwraps it and returns the underlying value.
You can find additional motivating examples at the TC39 Pipeline Operator proposal page and explore other interesting use cases. You can use the pipeline operator in several cases in your code, like in a validation scenario, where you want to validate specific attributes of an object, or you can use it to create mixins in a much more concise way, or to create object decorators as well as many other use cases that are presented in the official proposal.
Why nesting function calls is bad?
This is the traditional way, it’s the “pipelining before it was cool”. With this technique we nest multiple function calls, making something that resembles a Christmas tree.
Please admit it. It’s ugly. That’s the reason the pipe function was invented, which does the same as above, a bit more elegantly.
Why choose this over the pipe() method?
Libraries like Ramda and RxJS provide a
With this example, I have to first define my functions.
Now, here’s the pipe function in action.
I have to nest the functions in the pipe function, list them in order, so they execute in sequence and finally subscribe to the observable, which is returned by the pipe function, in order to read the raw value.
The same example now in Ramda. I must admit I like Ramda more when working with Functional stuff in JS, mostly for its simplicity. I’ll setup my functions first in a similar manner.
Now, I call the pipe method of Ramda and I provide the phrase as a parameter. In the same fashion, all functions in my pipe will run sequentially.
Finally, it’s time for the pipeline operator. As with previous examples, I will set my functions upfront. I’ve made them a bit more functional compared to previous examples, plus they use the pipeline operator for internal operations.
Now that my functions are ready to use, let’s get the result.
I like the latter most, I don’t have to learn a new library and its intricacies nor I need to install it and import it, I am just using native stuff and Babel will transpile it accordingly for me.
Notice also how different this looks from the example which nests multiple function calls. The order is completely opposite. The pipelining example reads naturally, while the nesting example is awkward and hard to understand.
Another advantage is that you don’t have to use third party libraries like RxJS or at least some of their functions like
pipe(), which only adds to your bundle size. You’ll have to worry for less bandwidth to download for your consumers and less libraries to support in-project, for your developers.
It’s awesome how the language has progressed, from being so fragmented and badly implemented, to a better version of itself through tools like Babel and Typescript. Less and less utility libraries are needed currently to develop a web application, with developers finally putting more trust in native code.
If you liked this blog, please like and share! For more, follow me on Twitter.