Adventure operator pipeline in babel @ 7

Original author: James DiGioia
  • Transfer

In the release of babel@7.0.0-beta52, a new mandatory config flag for the plugin has appeared @babel/plugin-proposal-pipeline-operator, which breaks backward compatibility for previous versions of the plugin. In this article you will learn what an operator is pipelineand why it needs a configuration.

UPD: smart pipelines appeared in babel@7.2.0: pull-8289

Current status

Gilbert Garza , who originally proposed the operator pipeline, aimed to get a simple syntax for "ordered chains of function calls in a readable functional style." The pipeline operator has its origin in languages ​​such as F #, Hack, Elm, Elixir, and others, and when adding it to JavaScript, there are two controversial points:

  • Where and how to use placeholders?
  • How should work async / awaitin the pipeline?


The first question was raised by Kevin Smith in this ticket , where he suggested using the Hack style pipelines . In Hack, placeholders are required for any right side of the pipeline, for example:

namespace Hack\UserDocumentation\Operators\Pipe\Examples\MapFilterCountPiped;
function piped_example(array<int> $arr): int {
  return $arr
    |> \array_map($x ==> $x * $x, $$)
    |> \array_filter($$, $x ==> $x % 2 == 0)
    |> \count($$);
var_dump(piped_example(range(1, 10)));

We took as a basis that the placeholder can be used in any expression and contains the value from the last step of the pipeline. This approach gives us the flexibility and opportunities for the formulation of expressions.

The reverse side of the coin is the complication of the language due to the addition of a new token. So far, we have chosen the hash ( #), and although the discussion is still open, any token will potentially have intersections with other uses. For example, the hash is also used by the private fields of the class , like any other token variants are used anyway .

Async / await

The first version of the operator pipelinecontained the following syntax for await:

x |> await f

what can be expanded to:

await f(x)

Unfortunately, users may well expect this deployment:

(await f)(x)

While the idea of ​​adding asyncto the pipeline was skidding, members of the committee spoke out against the operator pipelinewho does not support async / await. Yes, there are options for how to work with functions that return Promise without explicit syntax, but all of these options are too cumbersome or require auxiliary functions.

Proposed Solutions

As a result of the discussions, two proposals were formed (in addition to the minimum option): use F # pipelines and Smart pipelines. Let's look at these suggestions.

Minimal version of the operator

This offer only applies to basic functionality. In the minimal version, asynchronous support is removed and there are no placeholders. This option corresponds to the behavior of the previous version of the babel-plugin, before the appearance of the configuration, and corresponds to the current specification for the operator pipelinein the repository. It is used more as a draft probe to reveal the advantages and disadvantages of other proposals, and is unlikely to be adopted without the drastic changes that are in the alternative proposals.

F # pipelines

Placeholders for F # pipelines are not needed at all. In the basic version, the switch functions close the need for placeholders, requiring less scribbling, and they are based on the syntax ES2015, which is familiar to everyone.

Currently (according to the specification of F # pipelines), the arrow functions must be wrapped in brackets:

let person = { score: 25 };
let newScore = person.score
  |> double
  |> (_ => add(7, _))
  |> (_ => boundScore(0, 100, _));

Research is well underway to determine whether it is feasible to use pointer functions without parentheses, which here seem to be syntactically redundant.

As for asynchrony, in the F # pipelines it awaitworks as a unary function:

promise |> await

What unfolds in:

await promise

and therefore awaitcan be used in the middle of a long chain of asynchronous calls:

  |> await
  |> (x => doubleSay(x, ', '))
  |> capitalize
  |> (x => x + '!')
  |> (x => new User.Message(x))
  |> (x => stream.write(x))
  |> await
  |> console.log;

Such special processing awaitcan potentially open up the possibility to use other unary operators in a similar way (for example, typeof), but the original specification of F # pipelines does not contain them.

Smart pipelines

Smar pipelines bring the idea of ​​placeholders to a logical conclusion, allowing partial applications and arbitrary expressions to be used in pipelines. The previous long string can be written like this:

  |> await #
  |> doubleSay(#, ', ')
  |> # || throw new TypeError()
  |> capitalize
  |> # + '!'
  |> new User.Message(#)
  |> await stream.write(#)
  |> console.log;

The rules for using placeholders in the smart pipelines are quite simple. If a single identifier transmitted in step Pipeline, no additional token (placeholder) is not required, it is called a "minimalist style» ( "a bare style" ):

x |> a;
x |> f.b;

Unlike Hack, unary functions do not require a placeholder token.

For other expressions, the placeholder (called the "lexical topic token" - "thematic style token") is required, and the pipeline is considered to work within the "thematic style" - "topic style" . The absence of a placeholder token in this case causes an early SyntaxError error:

10 |> # + 1;
promise |> await #;

If there are any operators, brackets (including for the method call), quotes, or anything else besides the identifier and a point, then the placeholder token is required. This will help avoid shooting yourself in the leg and eliminate uncertainty.

Smart-Pipeline decide to support asynchronous problem in a more general way that allows the use of Pipeline in all possible expressions, not only await, but also typeof, yieldand any other statements.

Babel comes on the scene

Once all three sentences were concretized, we concluded that such discussions would not lead to the resolution of deep contradictions between the sentences. We decided that the best way is to collect feedback from developers using sentences in real code. Taking into account the role of Babel in the developer community, we decided to add all three options to the operator's plugin pipeline.

Since the parsing for all three sentences is insignificant, but different, their support must first be added to @babel/parser(which babylon), and the parser should know which sentence needs to be supported now. Thus, the operator plugin pipelinerequires an option "proposal", both for configuring babylon for parsing, and for subsequent transformation.

We worked on this online, because we need to make all the changes that break backward compatibility before babel @ 7 ceases to be a beta. In the end, we would like to make one of the default pipelines for the plugin, in order to eliminate the need for a configuration option.

Given these limitations, we decided to add an option to the plugin configuration and make it mandatory, forcing users to decide which of the proposals they want to use in their project. As soon as a concrete proposal is selected as the canonical behavior of the operator, we mark the option "proposal"as obsolete, and the canonical version will work by default. Support for canceled offers will work until the next major version.


If you want to participate in the discussion of the proposal, then all discussions are public and you can find them in the repository of the proposals of the pipeline operator . There is also a presentation from the meeting TC39 . After all, you can contact James DiGioia , JS Choi, or Daniel Ehrenberg on Twitter.

But more importantly, as soon as the work on the project pipelineis completed, try it in your projects! We are also working on adding new features to the repl , so that you can check your code online. We need feedback, and using it in real code will help to gather it. Send tweets on @babeljs .

Only registered users can participate in the survey. Sign in , please.

And which of the options for the pipeline operator at first glance seems to you more convenient to use?

Also popular now: