Babel 中文文档
  • 印记中文
  • 文档
  • 配置
  • 试用
  • 视频
  • 博客
  • 赞助
  • 团队
  • GitHub

›All Blog Posts

All Blog Posts

  • 7.19.0 Released: Stage 3 decorators and more RegExp features!
  • 7.18.0 Released: Destructuring private elements and TypeScript 4.7
  • 7.17.0 Released: RegExp 'v' mode and ... 🥁 decorators!
  • 7.16.0 发布: ESLint 8 和 TypeScript 4.5
  • 7.15.0 发布:支持 Hack-style 管道, TypeScript 枚举常量和 Rhino 目标
  • Babel is used by millions, so why are we running out of money?
  • 7.14.0 Released: New class features enabled by default, TypeScript 4.3, and better CommonJS interop
  • 7.13.0 Released: Records and Tuples, granular compiler assumptions, and top-level targets
  • 7.12.0 Released: TypeScript 4.1, strings as import/export names, and class static blocks
  • 7.11.0 Released: ECMAScript 2021 support in preset-env, TypeScript 4.0 support, printing config and the future of `babel-eslint`
  • The State of babel-eslint
  • 7.10.0 Released: Class Fields in preset-env, '#private in' checks and better React tree-shaking
  • 7.9.0 Released: Smaller preset-env output, Typescript 3.8 support and a new JSX transform
  • 7.8.0 Released: ECMAScript 2020, .mjs configuration files and @babel/cli improvements
  • Babel's Funding Plans
  • 7.7.0 Released: Error recovery and TypeScript 3.7
  • 7.6.0 Released: Private static accessors and V8 intrinsic syntax
  • 7.5.0 Released: dynamic import and F# pipelines
  • The Babel Podcast
  • 7.4.0 Released: core-js 3, static private methods and partial application
  • 7.3.0 Released: Named capturing groups, private instance accessors and smart pipelines
  • 7.2.0 发布:私有实例方法(Private Instance Methods)
  • 在 Babel 中支持 TC39 标准的装饰器
  • 7.1.0 Released: Decorators, Private Static Fields
  • Babel 7 发布
  • Removing Babel's Stage Presets
  • What's Happening With the Pipeline (|>) Proposal?
  • Announcing Babel's New Partnership with trivago!
  • On Consuming (and Publishing) ES2015+ Packages
  • Nearing the 7.0 Release
  • Babel Turns Three
  • Planning for 7.0
  • Zero-config code transformation with babel-plugin-macros
  • Contributing to Babel: Three Lessons to Remember
  • Personal Experiences at Babel #1 — A PR with Unusually High Number of Reviews
  • Babel and Summer of Code 2017
  • Upgrade to Babel 7 (moved)
  • Upgrade to Babel 7 for Tool Authors (WIP)
  • 6.23.0 Released
  • The State of Babel
  • 6.19.0 Released
  • 6.18.0 Released
  • 6.16.0 Released
  • Babili (babel-minify)
  • 6.14.0 Released
  • Babel Doctor
  • Setting up Babel 6
  • 6.0.0 Released
  • React on ES6+
  • Function Bind Syntax
  • 5.0.0 Released
  • Babel 喜爱 React
  • 并非出生而逐渐走向灭亡
  • 2to3
  • 6to5 + esnext

What's Happening With the Pipeline (|>) Proposal?

July 19, 2018

James DiGioia

With the release of babel@7.0.0-beta52, we introduced a new required configuration flag to @babel/plugin-proposal-pipeline-operator, a breaking change for the pipeline operator. To clear up any confusion, let's take a look at the pipeline proposal and why we needed to introduce this configuration option.

Current Status

The pipeline operator was originally introduced by Gilbert Garza, providing a clean syntax for "streamlining chained function calls in a readable, functional manner." The pipeline operator has roots in a number of languages, including F#, Hack, Elm, Elixir, and others, but there were two major points of contention in introducing the new syntax to JavaScript:

  • Whether and how to introduce placeholders
  • How to handle async / await in the pipeline

Placeholders

The first issue was the question of placeholders. This was first raised by Kevin Smith in this issue, where he suggested Hack-style pipelining. In Hack, a placeholder is required for every right-hand side of the pipeline, as in this 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 built on this concept, as a placeholder can easily be used in arbitrary expressions, with the placeholder representing the value returned from the previous step. This affords additional flexibility and power within a pipeline step.

The downside is the complexity involved in introducing a new token. The hash (#) is the current choice, and although this is still open to bikeshedding, any token would potentially have multiple meanings. The hash is also used by the private fields proposal, and all other options are in use in one form or another.

Async / Await

The initial introduction of the pipeline included this syntax for await:

x |> await f

which would desugar to

await f(x)

Unfortunately, users may expect this alternative desugaring:

(await f)(x)

While there was pushback on the idea of including async handling in the pipeline at all, committee members expressed concern about a pipeline operator that didn't handle async/await. While there are ways to handle Promise-returning functions without explicit syntax, they are too cumbersome to be useful or require a helper function.

Proposed Solutions

As a result of these discussions, two proposals, along with a base minimal proposal, emerged to resolve them: F# Pipelines and Smart Pipelines. Let's go through how they resolve the problems posed above.

Minimal Pipelines

This proposal covers the basic functionality of the pipeline operator. The minimal proposal bans await, so there's no async handling involved at all, and includes no placeholders. It matches the behavior of the babel plugin before we introduced the configuration and is the current specification in the pipeline operator proposal repository. It functions more as a straw man, to compare the benefits and tradeoffs of other the proposals, and is unlikely to be accepted as-is without lethal defects in both of the alternatives.

F# Pipelines

On the question of placeholders, F# Pipelines argue they're not needed. In the base proposal, arrow functions fill the area placeholders fill, requiring less new syntax and building on a syntax developers are already familiar with and have been using since ES2015.

As currently specced, arrow functions are required to be wrapped in parentheses:

let person = { score: 25 };

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

Exploration is underway to determine whether it would be feasible to enable arrow functions to be used without parentheses, as they are a significant syntactical burden.

On the question of async, F# Pipelines treat await similar to a unary function:

promise |> await

This would desugar to:

await promise

and can thus be used in the middle of larger function chains with async:

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

The special casing of await could potentially enable other unary operators to be used similarly (e.g. typeof), but the F# pipelines don't support them initially.

Smart Pipelines

Smart Pipelines takes the idea of the placeholder to its logical conclusion, enabling it to manage partial application as well as arbitrary expressions in a pipeline. The above long chain would be written thus:

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

Smart Pipelines have a few rules for the placeholder. If a bare identifier is provided to a step in the pipeline, no token is necessary, called "bare style":

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

Unlike Hack, unary functions don't require a placeholder token.

For other expressions, a placeholder (called a "lexical topic token") is required, and the code will throw an early SyntaxError if it is not included in "topic style":

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

If there are any operators, parentheses (including for method calls), brackets, or anything other than identifiers and dot punctuators, then a topic token is necessary. This avoids footguns and eliminate ambiguity when not using a topic token.

Smart pipelines thus resolve the issue of async in an integrative way, allowing all possible expressions to be embedded in a pipeline; not only await, but also typeof, yield, and another other operator desired.

Where Babel Comes In

Once all three proposals were fleshed out, we realized discussion and debate were unlikely to resolve the inherent tension between them. We decided the best way to make a decision would be with the feedback of developers, using the proposals in real code. Given Babel's role in the community, we decided to introduce all three of the above proposals into the pipeline operator plugin.

Because these proposals parse slightly differently, support needs to be added to @babel/parser (formerly babylon), and the parser needs to be configured based on which proposal is its current parse target. The pipeline operator plugin itself thus needs the "proposal" option, both to configure babylon as well as its own transformation.

We were working under a compressed timeline for this, as we needed to introduce any breaking changes to babel, @babel/parser, as well as the pipeline proposal plugin before babel@7 left beta. We also wanted the plugin to be able to eventually default to whichever of the proposals get accepted, so the configuration option becomes obsolete.

Given these two constraints, we opted to introduce this new configuration option and make it required, forcing users to decide which proposal they want to use in their project. Once a particular proposal gets accepted as the canonical behavior of the pipeline operator, we'll deprecate the "proposal" option and make the default whichever gets accepted, and the rejected proposals will be removed in the following major version.

Get Involved

If you're interested in getting involved in the pipeline proposal, all the conversations are public and you find them in the pipeline operator repository. You can also check out the presentation from the last TC39 meeting. Lastly, you can find James DiGioia, J. S. Choi, or Daniel Ehrenberg on Twitter.

But most importantly, once the work is complete, try out the pipeline operator in your own projects! We're also working on adding options to the repl, so you'll be able to try out the code there as well. We need feedback and real code in order for this to be useful, so we'd love to hear from you. Tweet us at @babeljs to let us know.

Recent Posts
  • Current Status
    • Placeholders
    • Async / Await
  • Proposed Solutions
    • Minimal Pipelines
    • F# Pipelines
    • Smart Pipelines
  • Where Babel Comes In
  • Get Involved
Babel 中文文档
文档
学习 ES2015
社区
视频用户Stack OverflowSlack 频道Twitter
更多
博客GitHub 组织GitHub 仓库Website 仓库旧版网址 6.x旧版网址 5.x