Babel F# pipeline operator proposal
Introduction
Writing readable and declarative code in JavaScript, how nice it would be! š
I know, readable and declarative donāt go in the same sentence when talking about JavaScript, but should it be always like this? The community has been looking for ways to improve these problems with the language for years.
Since Babel 7.0 there have been a few proposals implemented that lean towards the readable and declarative JavaScript goal. Iām sure you have heard about the pipeline operator, but if you havenāt then itās fine, this post will guide you through. This operator is syntactic sugar with the intention to make our code really sweet (pun intended). In this post Iām going to discuss one of the competing proposals, the F# variant.
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 operator
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# pipeline operator in JavaScript
This operator borrows its style from FP languages like F#, OCaml, Elm, etc. and makes it easier for developers to develop functional solutions in JavaScript. I must inform you that this proposal is still at Stage 1 and itās been evaluated, so expect things to change in the future, as the proposal matures.
The variants
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.
In the F# pipeline, you must use arrow functions, a style which is more close to the JavaScript we all know and love. The partial function application is not supported out of the box if you choose the F# variant, so make sure to install and add it as a plugin in your .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.
1
npm install [@babel/plugin-proposal-pipeline-](http://twitter.com/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 minimal or smart.
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.
1
npm install [@babel/plugin-proposal-partial-a](http://twitter.com/babel/plugin-proposal-partial-a)pplication - save-dev
Then add it to your babel plugins in .babelrc as follows.
Examples
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 fetch and 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 pipe() function which you can use to pipe operations, very much like the pipeline operator. Techniques such these have been around for few years, as no pipeline operator existed before in JavaScript. But now, with its arrival, it manages to make nested function calls look clumsy, wordy and obsolete. I have prepared an example, one with RxJS and one with Ramda and finally, the native approach with the pipeline operator. Letās make it functional all the way.
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.
Comments powered by Disqus.