In the release of babel@7.0.0-beta52, a new mandatory config flag for the @babel/plugin-proposal-pipeline-operator
, which breaks backward compatibility for previous versions of the plugin. From this article you will learn what a pipeline
operator is and why it needs a configuration.
UPD: smart pipelines appeared in babel@7.2.0: https://babeljs.io/blog/2018/12/03/7.2.0#smart-pipeline-operator-parsing-8289-https-githubcom-babel-babel- pull-8289
Gilbert Garza , who originally proposed the pipeline
operator, 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, two controversial points arise:
async / await
in pipeline?The first question was raised by Kevin Smith in this ticket , where he suggested using the Hack style of the 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 the fact 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 intersect 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 .
The first variant of the pipeline
operator contained 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 async
to the pipeline
, the members of the committee spoke out against the pipeline
operator, which 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 additional functions.
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.
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 babel-plug-in of previous versions, before the appearance of the configuration, and corresponds to the current specification for the pipeline
operator in the repository. It is used more as a draft probe to identify the advantages and disadvantages of other proposals, and is unlikely to be adopted without the drastic changes that are in the alternative proposals.
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.
At the moment (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.
Regarding asynchrony, in F # pipelines, await
works like a unary function:
promise |> await
What unfolds in:
await promise
and therefore await
can be used in the middle of a long chain of asynchronous calls:
promise |> await |> (x => doubleSay(x, ', ')) |> capitalize |> (x => x + '!') |> (x => new User.Message(x)) |> (x => stream.write(x)) |> await |> console.log;
Such special handling of await
could potentially open up the possibility of using other unary operators in a similar way (for example, typeof
), but the original specification for F # pipelines does not contain them.
Smar pipelines bring the idea of ​​placeholders to a logical conclusion, allowing both partial application and arbitrary expressions in pipelines. The previous long string can be written like this:
promise |> 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 is passed to the pipeline step, then no additional token (placeholder) is required, this is called a “minimal style”:
x |> a; x |> fb;
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 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 pipelines solve the problem of asynchronous support in a more general form, which allows all possible expressions to be used in the pipelines, not only await
, but also typeof
, yield
and any other operators.
Once all three proposals were concretized, we concluded that such discussions would not lead to the resolution of deep contradictions between the proposals. We decided that the best way is to collect feedback from developers using sentences in real code. Given the role of Babel in the developer community, we decided to add all three options to the pipeline
operator plugin.
Since the parsing for all three sentences is insignificant, but different, their support must first be added to @babel/parser
(which is babylon
), and the parser must know which sentence needs to be supported now. Thus, the pipeline
operator plugin requires the "proposal"
option, 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 pipeline options for the plugin, in order to eliminate the need for a configuration option.
Given these limitations, we decided to add an option to the plug-in 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 "proposal"
option 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 offers of the pipeline operator . There is also a presentation from the TC39 meeting . After all, you can contact James DiGioia , JS Choi, or Daniel Ehrenberg on Twitter.
But more importantly, as soon as the work on the pipeline
is 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. Tweet @babeljs
Source: https://habr.com/ru/post/418125/
All Articles