Updated: Feb 8
For those not familiar with the pipeline operator proposal, it’s currently a “stage 1” proposal to TC39 (the committee in charge of the ECMAScript stardandard). This means it’s being actively promoted, but isn’t yet at a stage where it’s expected to be eventually included in the standard. In other words, it’s definitely not there yet.
As you can see, the functions must be written in the reverse sequence from the order in which they are used. In addition, it’s difficult to tell which arrow function is associated with which iteration function.
The proposed pipeline operator was specifically designed to overcome these issues:
This code is much easier to follow and reason about because the functions appear in the order in which they are used. In addition, all their arguments are placed together (through the use of # as an argument placeholder). No need to maintain a mental stack of functions in your head, or try to match parentheses across multiple function expressions.
Why, then, was there such a pushback against my use of this proposed feature? One reason is that some people dislike the use of language features that are not yet part of the ECMAScript standard, or at least at stage 3. I can appreciate that - there have been cases where developers have jumped the gun and used such features only to have them rolled back and modified by TC39. This has happened with the decorators proposal, for example. The situation with the pipeline operator proposal is especially delicate in this context because there are, in fact, two competing proposals for the implementation of this language feature.
I can definitely appreciate the sentiment. I too am not a fan of extensions to the language that appear to deviate from its existing syntax and semantics. That said, the alternative to the pipeline operator which Costin used - method chaining with the dot operator - does, in fact, highlight the benefits of piping over chaining. Indeed, this is something I alluded to in the very title of my original post: breaking chains with pipelines.
Kyle Simpson to the rescue!
The pipe() function proposed by Kyle is a higher-order function which accepts any number of function references as arguments, and returns a function that invokes these functions in sequence on whatever argument it receives.
After a bit of back-and-forth we settled on the following simple implementation of the pipe() function for this use-case:
Rather than accepting the function arguments and returning another function which takes the input argument, this implementation takes both together, and places the input argument first. This enables putting the input argument at the front rather than at the end of the expression, as in Kyle’s original code snippet, and eliminates the extra parenthesis. This results in code that is very similar to the implementation which uses the pipeline operator:
This implementation of pipe() does require modification of the filter(), map(), and slice() functions that I presented in my original article. For example, the original filter() function was implemented as:
The new implementation is:
This modification is required because the # argument placeholder, introduced as part of one pipeline implementation proposal, is no longer available. Instead the functions receive just the operator and return a function which is then invoked on the sequence. In my opinion, this is a very small price to pay for eliminating the need to extend the language. I leave it to you to figure out the required modifications to other functions I presented.
In my second blog post I showed how the iteration library can be extended to also support asynchronous sequences, such as a sequence of events generated by a browser DOM element. And the proposed pipeline operator was able to work with those types of sequences as well, for example:
Similarly, the pipe() function presented above can also work with asynchronous sequences, but the iteration functions need to be slightly modified. For example, the original forEach() function was:
Now it needs to be:
Now, we can rewrite the example that pipes DOM events as:
As usual I will leave it up to you to modify the implementation of reduce() as well as the other iteration functions. Or you can check out this CodePen in which I provide the relevant implementations and examples of usage.
It’s generally preferable to avoid extending a programming language when existing language specification is already sufficiently expressive in the relevant context. This is especially true when the proposed extension deviates from the existing syntax and semantics of the programming language. As this post shows, I now definitely believe that this is the case with the proposed pipeline operator. Consequently, I hope that TC39 decides to reject this proposal.
In any event, removing the use of the pipeline operator makes the resulting code compatible with modern browsers. If you check the CodePen associated with this post, you will see that I disabled the use of Babel. This makes the library much more usable and useful.
This post was written by Dan Shappir
You can follow him on Twitter
For more engineering updates and insights: