main 5ac4dfd624e6 cached
14 files
103.9 KB
27.7k tokens
1 requests
Download .txt
Repository: tc39/proposal-pipeline-operator
Branch: main
Commit: 5ac4dfd624e6
Files: 14
Total size: 103.9 KB

Directory structure:
gitextract_wep3lz_j/

├── .editorconfig
├── .github/
│   ├── pull_request_template.md
│   └── workflows/
│       ├── floodgate.yml
│       ├── publish.yml
│       └── stale-lock.yml
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── HISTORY.md
├── LICENSE.md
├── README.md
├── package.json
└── spec.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = space
indent_size = 2


================================================
FILE: .github/pull_request_template.md
================================================
Thank you for taking the time to contribute to this proposal!
We’re so happy you’re helping out.

1. Please take a look at the [contributing guidelines][]
   and the resources to which it links.
2. Please include the purpose of the pull request. For example:
   * “This adds…”
   * “This simplifies…”
   * “This fixes…”
3. Please be explicit about what feedback, if any, you want:
   a quick pair of eyes, discussion or critique of its approach,
   a review of its copywriting, and so on.
4. Please mark the pull request as a Draft if it is still unfinished.

All text in this repository is under the
[same BSD license as Ecma-262][LICENSE.md].
As is the norm in open source, by contributing to this GitHub repository,
you are licensing your contribution under the same license,
as per the
[GitHub terms of service][ToS].

[contributing guidelines]: https://github.com/tc39/proposal-pipeline-operator/blob/main/CONTRIBUTING.md
[LICENSE.md]: https://github.com/tc39/proposal-pipeline-operator/blob/main/LICENSE.md
[ToS]: https://help.github.com/en/github/site-policy/github-terms-of-service


================================================
FILE: .github/workflows/floodgate.yml
================================================
name: Comment Floodgate
on: issue_comment
jobs:
  floodgate:
    runs-on: ubuntu-latest
    steps:
    - uses: js-choi/github-comment-floodgate@v2
      with:
        minutes-in-period: 60
        # This number is how many comments are allowed per period per issue.
        max-comments-per-period: 12


================================================
FILE: .github/workflows/publish.yml
================================================
name: Build and Deploy to GitHub Pages
on:
  push:
    branches:
    - main
  workflow_dispatch:
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout 🛎️
      uses: actions/checkout@v2
      with:
        persist-credentials: false

    - name: Setup Node
      uses: actions/setup-node@v1
      with:
        node-version: '14.x'

    - name: Install and Build 🔧
      run: |
        npm install
        npm run build

    - name: Deploy 🚀
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./dist


================================================
FILE: .github/workflows/stale-lock.yml
================================================
name: Lock Stale Threads

on:
  schedule:
  - cron: '0 0 * * *'

  workflow_dispatch:

permissions:
  issues: write
  pull-requests: write

concurrency:
  group: lock

jobs:
  action:
    runs-on: ubuntu-latest
    steps:
    - uses: dessant/lock-threads@v3
      with:
        issue-inactive-days: 7


================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
out
dist
npm-debug.log
deploy_key


================================================
FILE: .travis.yml
================================================
sudo: off

language: node_js

node_js:
  - "8"

script:
  - bash ./deploy.sh

env:
  global:
    - ENCRYPTION_LABEL: "bc1f69dfbe70"
    - GH_USER_NAME: "littledan"
    - GH_USER_EMAIL: "littledan@igalia.com"
    - PRIVATE_KEY_FILE_NAME: "github_deploy_key.enc"


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of conduct
This repository is a TC39 project, and it therefore subscribes to
its [code of conduct][CoC]. It is available at <https://tc39.es/code-of-conduct/>.

We all should strive here to be respectful, friendly and patient,
inclusive, considerate, and careful in the words we choose.
When we disagree, we should try to understand why.

To ask a question or report an issue, please follow the [CoC]’s directions, e.g., emailing
[tc39-conduct-reports@googlegroups.com][].

More information about contributing is also available in [CONTRIBUTING.md][].

[CoC]: https://tc39.es/code-of-conduct/
[tc39-conduct-reports@googlegroups.com]: mailto:tc39-conduct-reports@googlegroups.com
[CONTRIBUTING.md]: https://github.com/tc39/proposal-pipeline-operator/blob/main/CONTRIBUTING.md


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to the ES pipe operator’s proposal
First off, thank you for taking the time to contribute! 🎉

Here are some suggestions to contributing to this proposal.
The pipe operator in JavaScript has a [long and twisty history][HISTORY.md].
A lot of issues have already been argued round in round in circles.

Bearing that in mind, please try to read the following
before making new issues or comments:

1. [HISTORY.md][]: This will give a lot of context
   behind what’s been happening to the proposal since its creation in 2015.
2. The general [TC39 Process][], which summarizes
   how TC39’s “consensus” and “Stages” work.
3. The guide on [contributing to TC39 proposals][contributing guide].
4. The [TC39 Code of Conduct][CoC]:
   It has important information about how we’re all expected to act
   and what to do when we feel like someone’s conduct does not meet the Code.
   We all want to maintain a friendly, productive working environment!
5. The [TC39 How to Give Feedback][feedback] article.
6. The [proposal explainer][] to make sure that it is
   not already addressed there.
7. The [TC39 Matrix guide][] (if you want to chat with TC39 members on Matrix,
   which is a real-time chat platform).
8. If the explainer does not already explain your topic adequately,
   then please [search the GitHub repository’s issues][issues]
   to see if any issues match the topic you had in mind.
   This proposal is more than four years old,
   and it is likely that the topic has already been raised and thoroughly discussed.

You can leave a comment on an [existing GitHub issue][issues],
create a new issue (but really do try to [find an existing GitHub issue][issues] first),
or [participate on Matrix][TC39 Matrix guide].

Please try to keep any existing GitHub issues on their original topic.

We’ve also installed a [“floodgate” moderation bot](https://github.com/marketplace/actions/comment-floodgate)
(see [issue #231](https://github.com/tc39/proposal-pipeline-operator/issues/231)).
When a issue gets a huge flood of new comments (i.e., new comments are being created at a very high rate),
then the bot will temporarily lock that issue.
(The threshold rate is currently set to twelve messages in the same thread in the same sixty minutes.)
This doesn’t mean that the automatically locked high-traffic threads will get locked permanently.
It just means that the issue’s thread will cool down until a volunteer moderator is able to look at what’s going on.

If you feel that someone’s conduct is not meeting the [TC39 Code of Conduct][CoC],
whether in this GitHub repository or in a [TC39 Matrix room][TC39 Matrix guide],
then please follow the [Code of Conduct][CoC]’s directions for reporting the violation,
including emailing [tc39-conduct-reports@googlegroups.com][].

Thank you again for taking the time to contribute!

[HISTORY.md]: https://github.com/tc39/proposal-pipeline-operator/blob/main/HISTORY.md
[CoC]: https://tc39.es/code-of-conduct/
[TC39 process]: https://tc39.es/process-document/
[contributing guide]: https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md#new-feature-proposals
[feedback]: https://github.com/tc39/how-we-work/blob/master/feedback.md
[proposal explainer]: https://github.com/tc39/proposal-pipeline-operator/blob/main/README.md
[TC39 Matrix guide]: https://github.com/tc39/how-we-work/blob/master/matrix-guide.md
[issues]: https://github.com/tc39/proposal-pipeline-operator/issues?q=is%3Aissue+
[tc39-conduct-reports@googlegroups.com]: mailto:tc39-conduct-reports@googlegroups.com


================================================
FILE: HISTORY.md
================================================
# Brief history of the JavaScript pipe operator
The pipe operator in JavaScript has a long and twisty history.
Understanding that history can give context behind
what’s been happening to the proposal since its creation in 2015.

For information on what “Stage 1” and “Stage 2” mean,
read about the [TC39 Process][].

More information about contributing is also available in [CONTRIBUTING.md][].

## 2015–2017
People are debating about a [proposed bind operator `::`][bind]
by [@zenparsing][] (Brave).
A binary `::` operator that binds its left-hand side to its right-hand side (a function)
would serve as a “pipe” operator in JavaScript
(e.g., `a::f(b, c)::g(d, e)` would be a “pipeline” equivalent to
`g.call(f.call(a, b, c), d, e)`).
However, parts of TC39 object to using `this`,
saying that using `this` is strange outside of methods.
Debate online in the `::` repository (and also offline) is bogged down in circles.

[@littledan][] (Igalia) and [@gilbert][] create a proposal
for an alternative pipe operator that does not use `this`.
They start out [comparing F# pipes with Elixir pipes][first pipe preso].
They also [explore other syntaxes such as “placeholder” pipes (i.e., Hack pipes)][I4].

Meanwhile, [@rbuckton][] (Microsoft) discusses pipelining with the F# team
and plans to simultaneously propose F# pipes and syntax
for [partial function application][].
Upon discovering [@littledan][] (Igalia)’s proposal for F#-or-Elixir pipes,
[@rbuckton][] (Microsoft) finds that it already
aligns with the F# pipe that he had been planning to present,
and he switches to focusing on syntax for [partial function application][].

## 2017-07
On [2017-09-26][S1], [@littledan][] makes a first
[presentation for F#-or-Elixir pipes to TC39, successfully advancing to Stage 1][S1].

However, parts of TC39 object to redundancy with a bind operator.
A condition of the advancement to Stage 1 is that it would not be redundant
with a bind operator; this will become relevant later.

In addition, parts of TC39 object to aspects of F# pipes,
such as how they handle `await` and `yield`.

[@rbuckton][] (Microsoft) presents syntax for [partial function application][]
[at the same meeting and succeeds in advancing to Stage 1][PFA S1].
However, parts of TC39 push back against its syntax.

## 2017–2018
[@littledan][] and collaborators [attempt to make a stopgap with `|> await`][I66]
but encounter several conceptual and syntactic problems,
including a problem related to unexpected automatic semicolon insertion.
[@littledan][] decides to try to defer handling `await` at all to a later proposal;
he also drops Elixir pipes in favor of F# pipes.

On [2017-11-29][S2 2017], [@littledan][] makes
[another presentation for F# pipes and does not succeed in advancing to Stage 2][S2 2017].
During the presentation, he proposes to TC39 that `|> await` be deferred,
but there is pushback from several other representatives, and presentation time overflows.

Advancement of F# pipes is therefore stymied.

[@gilbert][] suggests resurrecting Hack pipes,
which were [previously explored in 2015][I4],
as a solution to TC39’s blocking concerns.
He also suggests two possible compromises that mix F# pipes and Hack pipes:
[“split-mix pipes” and “smart-mix pipes”][I89].
[@littledan][] (Igalia) agrees to investigate all three styles.
[@mAAdhaTTah][] (pro-F#-pipes) and [@js-choi][] (slightly pro-Hack-pipes)
volunteer to collaborate on competing specs:
[@mAAdhaTTah][] writes a [spec for F# pipes][F# spec]
and [@js-choi][] writes a [spec for smart-mix pipes][smart-mix-pipes spec].

## 2018–2020
On [2018-03-22][S2 2018], [@littledan][] presents F# pipes again—alongside
smart-mix pipes—in an
[update presentation, trying to gain more consensus within TC39 for Stage 2][S2 2018].
However, neither proposal is able to achieve much consensus among TC39 representatives
due to syntactic concerns.
Some TC39 representatives state that no pipe operator
may be worth standardizing at all.

Advancement of F# pipes therefore continues to be stymied;
advancement of smart-mix pipes is also stymied.

[@rbuckton][] (Microsoft) also presents [partial function application][]
again [on 2018-07, attempting to advance it to Stage 2][PFA 2018-07].
However, several TC39 representatives continue to push back against its syntax.
[@syg][] (Google V8) also expresses “strong reservations” about PFA syntax
increasing the “ease with which [developers] can allocate many many closures”,
with regards to memory use.
Partial function application is also unable to advance to Stage 2,
and its advancement is therefore also stymied.

[@codehag][] (Mozilla SpiderMonkey) meanwhile leads a
[Mozilla user study about pipes][Mozilla study].
Its results suggest that developers like and prefer smart-mix pipes slightly more,
but they make slightly less errors with F# pipes.
Her conclusions from the study were that no difference between smart-mix pipes or F# pipes
was significant enough to justify any decision one way or the other.
(There is also another Mozilla user study about partial function application,
and its results suggests that the JavaScript community
is much more interested in partial function application than any pipe operator.)
The Mozilla SpiderMonkey team becomes weakly against any pipe operator.

Work on F# pipes, smart-mix pipes, and PFA syntax stalls.

For the three years, GitHub debate online and debate offline
continues back and forth in circles:
about the best way to increase the odds that smart-mix pipes vs. F# pipes
will reach consensus in TC39.

## 2021-01
The State of JS 2020 is published,
and it reports that one of the [language’s top requested features][SoJS 20]
is some kind of pipe operator.
This galvanizes [@littledan][] (Igalia)’s further work on pipe,
and he prepares a presentation for 2021-03’s TC39 meeting.

## 2021-03
[@littledan][] (Igalia) is managing other projects
and is now unable to devote time to the pipe operator.
He presents about it again to TC39 and asks there for a new lead champion.
[@tabatkins][] (Google), who also does much work in Web Platform standards
such as HTML5 DOM and CSS,
is personally enthusiastic about helping with any pipe operator
and agrees to take on being co-champion.
[@rbuckton][] (Microsoft) also agrees to co-champion the proposal.

[@tabatkins][] (Google) publishes a [Gist comparing F#, Hack, and smart-mix pipes][Gist], concluding that they are all functionally the same,
with only small differences in actual usage,
and that all three would benefit everyone.
Discussion is sparked in the Gist’s comments.

Galvanized by the Gist,
[@js-choi][] switches from writing the [smart-mix-pipes spec][]
to writing a [Hack-pipes spec][].

## 2021-07
[@tabatkins][] (Google) schedules a
[TC39 incubator meeting devoted to pipes][2021-07 incubator]
(see [general information on incubator calls][]).
Attendees of special note in this meeting are [@syg][] (Google V8),
[@littledan][] (Igalia), [@rbuckton][] (Microsoft), and [@ljharb][]. [@codehag][] (Mozilla SpiderMonkey) is unable to attend.

[@tabatkins][] (Google) presents three choices to the attendees again:
F# pipes, Hack pipes, and Elixir pipes.

[@rbuckton][] (Microsoft) is still in favor of F# pipes with his proposed syntax for
[partial function application][] (see [§ 2016–2017](#20162017)).
[@rbuckton][] (Microsoft) debates with [@tabatkins][] (Google)
about whether F# pipes or Hack pipes are more intuitive.
(He also mentions that [@codehag][] (Mozilla SpiderMonkey)
might be interested in co-championing partial function application without pipes,
based on her user studies’ findings; see [§ 2018–2020](#20182020).)

[@syg][] (Google V8) voices concerns again about engine performance
of partial function application and F# pipes (see [§ 2018–2020](#20182020)).

[@tabatkins][] (Google) and [@ljharb][] are supportive
of either Hack pipes or F# pipes:
“90%” F# pipes and “100%” Hack pipes.
However, their 90% support of F# pipes would drop a lot if [@syg][]
(Google V8)’s performance concerns about F# pipes are borne out.

Nobody in the meeting seems to think Elixir pipes are a good fit for JavaScript.

Most everyone in the meeting seems to be in favor of picking some style for pipes
after three years of indecision.

## 2021-08
[@tabatkins][] (Google) plans to present *some* pipe-operator style for Stage 2.
Based on the results of the the preceding meeting, they pick Hack pipes.
This has support from [@ljharb][] and some other TC39 representatives.
[@js-choi][] (Indiana University) joins as co-champion.

Through this month, [@tabatkins][] (Google) continues to debate offline
with [@mAAdhaTTah][] regarding Hack pipes vs. F# pipes.
As a result, [@mAAdhaTTah][] changes his mind from being in favor of F# pipes
to being in favor of Hack pipes,
deciding that [Hack pipes would be better for bridging functional programming][JDG essay]
with the rest of the JavaScript ecosystem.

[@rbuckton][] (Microsoft) joins in debating with [@tabatkins][] (Google) in late August.
[@rbuckton][] (Microsoft) notes the groundswell of support within TC39 about Hack pipes
due to “some of the limitations of F# pipes”.
Therefore feeling that F# pipes would continue to be indefinitely stuck at an impasse,
[@rbuckton][] (Microsoft) thus decides to give “tentative agreement” to Hack pipes.
(See [@rbuckton’s narrative][].)

***

On [2021-08-31][S2 2021], a formal Committee plenary occurs,
and [@tabatkins][] (Google) therefore
[presents Hack pipes as the tentative consensus among the champions,
proposing that TC39 advance them to Stage 2][S2 2021].
There are several responses from other representatives:

[@ljharb][] voices concern that advancing pipe
would kill any future bind operator (see [§ 2017-09](#2017-09)).
Other representatives respond that Hack pipes are now orthogonal to any bind operator
and would not kill it. [@ljharb][] decides not to block Stage 2.

[@codehag][] (Mozilla SpiderMonkey) voices some concerns:
the Mozilla SpiderMonkey team is still somewhat against any pipe operator,
whatever the style.
However, she decides that these concerns are not strong enough for her to block Stage 2.

[@syg][] (Google V8), having previously expressed concerns about memory allocation
encouraged by F# pipes and partial function application
(see [§ 2021-07](#2021-07)), does not give any objection.

[@rbuckton][] (Microsoft) continues to give tentative agreement to Hack pipes.

No other representatives give objections to Stage 2.
Hack pipes therefore succeed in advancing to Stage 2.
[@tabatkins][] (Google) resolves to continue discussing concerns
with [@rbuckton][] (Microsoft), [@codehag][] (Mozilla SpiderMonkey), [@mAAdhaTTah][],
and others offline.
They also discuss concerns with the community on GitHub,
both in the [2021-03 comparison Gist][Gist]’s comments (see [§ 2021-03](#2021-03))
and in the [pipe proposal’s issues][issues].

## 2021-09
In order to explain this proposal’s history and process to other community members,
[@js-choi][] (Indiana University) creates this document.

Inspired by a [defense of unary functions][I233], [@js-choi][] (Indiana University)
also creates a new proposal, [proposal-function-helpers][], that would add several Function
helper methods. These include Function.pipe, pipeAsync, flow, and flowAsync.

## 2021-10
Starting on 2021-10-25, another formal Commitee plenary occurs.
The pipe operator is not presented at this meeting, although
an [incubator meeting on 2021-11 is chartered][incubator charter 2021-11]
for bikeshedding the pipe operator’s topic token.

[On 2021-10-25, PFA syntax is presented again to the Committee
plenary][2021-10 PFA] by [@rbuckton][] (Microsoft) for Stage 2.
The Committee rejects this proposal; several representatives,
including those from Mozilla SpiderMonkey and Google V8, state
that there were insufficiently specific and compelling use cases presented,
with high syntax cost and novelty in comparison to arrow functions.

[On 2021-10-28, proposal-function-helpers is also presented to the
Committee plenary][2021-10 Function] for Stage 1 by [@js-choi][].
The Committee also rejects this proposal due to its being overly broad,
and it requests that it be split up into multiple proposals.
These split proposals would include a proposal specifically
about [Function.pipe and flow][].

## 2021-12: Holistic-dataflow articles
Since December, TC39 has continued to discuss the pipe operator in the greater
context of “dataflow”.

The “dataflow” proposals include the following:

* The [pipe operator][Hack pipes] `… |> …` (aka “Hack pipes”)
* [Function.pipe][] (a function version of the F# pipe operator)
* The [bind-this][] operator `…::…` (and its variant [call-this][] `…@(…)`)
* The [Extensions][] syntaxes `…::…`, `…::…:…`, and `const ::{ … } = …;`
* [Partial function application][PFA syntax] `…~(…)` (aka “PFA syntax”)

These dataflow proposals overlap in various complicated ways. Multiple TC39
representatives expressed concerns about redundancies between these
proposals—that the space as a whole needs to be holistically considered, that
goals need to be more specifically articulated, and that there is not enough
“syntax budget” in the language to approve all of these proposals. This applies
to the pipe operator, as well as all the others in that list.

![][diagram]

* In late December, [@js-choi][] wrote [an article detailing how these
  proposals overlap][2022-12 jschoi dataflow article].
* [@tabatkins then wrote a response article](https://www.xanthir.com/b5Gd0) on
  their own blog.
* Later, [@hax would also write another response
  article](https://hackmd.io/yDDJCsS-Sv2AJwo8arAn3w?view). (@hax is a TC39
  champion of the [Extensions][] syntaxes.)

## 2022-01: Plenary meeting
On [January 26, 2022, a plenary meeting was held][2022-01 plenary] to discuss these overlapping proposals holistically. This discussion overflowed into an [ad-hoc meeting on the next day][2022-01 overflow].

In these two meetings, TC39 representatives debated over such topics as:
* Creating a unified language versus accommodating multiple programming paradigms (e.g., object oriented versus functional).
* [TMTOWTDI][] versus [TOOWTDI][].
* Whether generalized  language features (like the Hack-style pipe operator) or specialized features (like Function.pipe, the F#-style pipe operator, and the bind-this operator) were more desirable.
* Whether it is better for language features to be universal or prescriptive in their usage.
* The merits of specific dataflow proposals, including the pipe operator.

Support among TC39 representatives for the pipe operator as it is now (with a Hack-style topic reference) appears to range from strongly in favor to weakly against. Several representatives reaffirmed that they are moderately or strongly against F#-style syntax. Support for Function.pipe appears to be tepid: neither strongly positive or negative. For more details, see the [conclusions of the ad-hoc overflow meeting][2022-01 overflow conclusions].

## 2022-03 and 2022-04
At the [2022-03 plenary, several candidate tokens for the topic reference were presented][2022-03 plenary pipe]. The Committee mildly preferred `@` as the topic reference. (However, a [delegate subsequently raised serious concerns about `@`][2022-04 serious @ concerns], and thus `@` was excluded again as a topic reference.)

Additionally, an [update about call-this was also presented at the plenary][2022-03 plenary call-this]. The call-this proposal continues to polarize the Committee due to ecosystem-schism concerns.

## 2022-07
[In the plenary on July 21, proposal-function-pipe-flow was formally presented to the Committee, and it was rejected for Stage 1][2022-07 plenary]. The Committee generally found its use cases not compelling enough compared to the pipe operator. Its champion subsequently withdrew it from consideration. (Eventually, after the pipe operator gains users, pain points with the pipe operator may be enough motivation to revive proposal-function-pipe-flow, but that would not occur for a long time.)

There is another [incubator call chartered for more pipe-operator bikeshedding](https://github.com/tc39/incubator-agendas/issues/26), which might or might not occur before the [September plenary](https://github.com/tc39/agendas/blob/main/2022/09.md).

[issues]: https://github.com/tc39/proposal-pipeline-operator/issues?q=is%3Aissue+
[CONTRIBUTING.md]: https://github.com/tc39/proposal-pipeline-operator/blob/main/CONTRIBUTING.md
[general information on incubator calls]: https://github.com/tc39/how-we-work/blob/master/incubator-calls.md

[bind]: https://github.com/tc39/proposal-bind-operator
[partial function application]: https://github.com/tc39/proposal-partial-application
[proposal-function-helpers]: https://github.com/js-choi/proposal-function-helpers
[Function.pipe and flow]: https://github.com/js-choi/proposal-function-pipe-flow

[F# spec]: https://github.com/valtech-nyc/proposal-fsharp-pipelines/
[smart-mix-pipes spec]: https://github.com/js-choi/proposal-smart-pipelines
[Hack-pipes spec]: https://github.com/tc39/proposal-hack-pipes
[Function.pipe]: https://github.com/js-choi/proposal-function-pipe-flow
[bind-this]: https://github.com/tc39/proposal-bind-this
[call-this]: https://github.com/tabatkins/proposal-call-this-operator
[Extensions]: https://github.com/tc39/proposal-extensions
[PFA syntax]: https://github.com/tc39/proposal-partial-application
[diagram]: https://jschoi.org/21/es-dataflow/map/
[Hack pipes]: https://github.com/tc39/proposal-pipeline-operator
[TMTOWTDI]: https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it
[TOOWTDI]: https://wiki.python.org/moin/TOOWTDI

[first pipe preso]: https://docs.google.com/presentation/d/1qiWFzi5dkjuUVGcFXwypuQbEbZk-BV7unX0bYurcQsA/edit#slide=id.g1fa08b5c5c_0_93

[I4]: https://github.com/tc39/proposal-pipeline-operator/issues/4
[I75]: https://github.com/tc39/proposal-pipeline-operator/issues/75
[I66]: https://github.com/tc39/proposal-pipeline-operator/pull/66
[I89]: https://github.com/tc39/proposal-pipeline-operator/issues/89
[I233]: https://github.com/tc39/proposal-pipeline-operator/issues/233

[S1]: https://github.com/tc39/notes/blob/master/meetings/2017-09/sept-26.md#11iia-pipeline-operator
[S2 2017]: https://github.com/tc39/notes/blob/master/meetings/2017-11/nov-29.md#9iii-pipeline-operator-for-stage-2
[PFA S1]: https://github.com/tc39/notes/blob/master/meetings/2017-09/sept-28.md#13i-partial-application
[S2 2018]: https://github.com/tc39/notes/blob/master/meetings/2018-03/mar-22.md#10ive-pipeline-operator
[PFA 2018-07]: https://github.com/tc39/notes/blob/master/meetings/2018-07/july-25.md#partial-application
[2021-07 incubator]: https://github.com/tc39/incubator-agendas/blob/master/notes/2021/06-17.md#pipeline
[Mozilla study]: https://github.com/tc39/notes/blob/master/meetings/2019-06/june-6.md#javascript-and-syntax-research-methods
[S2 2021]: https://github.com/tc39/notes/blob/master/meetings/2021-08/aug-31.md#pipeline-operator-for-stage-2
[incubator charter 2021-11]: https://github.com/tc39/incubator-agendas/issues/21
[2021-10 PFA]: https://github.com/tc39/notes/blob/master/meetings/2021-10/oct-25.md#partial-function-application-for-stage-2
[2021-10 Function]: https://github.com/tc39/notes/blob/master/meetings/2021-10/oct-28.md#function-helpers
[2022-01 plenary]: https://github.com/tc39/notes/blob/main/meetings/2022-01/jan-26.md#holistic-discussion-of-tc39-dataflow-proposals
[2022-01 overflow]: https://github.com/tc39/incubator-agendas/blob/main/notes/2022/01-27.md
[2022-01 overflow conclusions]: https://github.com/tc39/incubator-agendas/blob/main/notes/2022/01-27.md#conclusions
[2022-12 jschoi dataflow article]: https://jschoi.org/21/es-dataflow/
[2022-03 plenary pipe]: https://github.com/tc39/notes/blob/main/meetings/2022-03/mar-29.md#bikeshedding-the-pipe-operator-topic-token
[2022-03 plenary call-this]: https://github.com/tc39/notes/blob/main/meetings/2022-03/mar-29.md#bikeshedding-call-this-syntax
[2022-04 serious @ concerns]: https://github.com/tc39/proposal-pipeline-operator/issues/91#issuecomment-1084946624
[2022-07 plenary]: https://github.com/tc39/notes/blob/main/meetings/2022-07/jul-21.md#functionpipe--flow-for-stage-1

[TC39 process]: https://tc39.es/process-document/
[Gist]: https://gist.github.com/tabatkins/1261b108b9e6cdab5ad5df4b8021bcb5
[@rbuckton’s narrative]: https://github.com/tc39/proposal-pipeline-operator/issues/91#issuecomment-917645179
[SoJS 20]: https://2020.stateofjs.com/en-US/opinions/?missing_from_js
[JDG essay]: https://jamesdigioia.com/hack-pipe-for-functional-programmers-how-i-learned-to-stop-worrying-and-love-the-placeholder/

[@littledan]: https://github.com/littledan/
[@gilbert]: https://github.com/gilbert/
[@tabatkins]: https://github.com/tabatkins/
[@codehag]: https://github.com/codehag/
[@mAAdhaTTah]: https://github.com/mAAdhaTTah/
[@js-choi]: https://github.com/js-choi/
[@syg]: https://github.com/syg/
[@ljharb]: https://github.com/ljharb/
[@rbuckton]: https://github.com/rbuckton/
[@zenparsing]: https://github.com/zenparsing


================================================
FILE: LICENSE.md
================================================
Copyright 2021 J. S. Choi, James DiGioia, Ron Buckton, Tab Atkins-Bittner

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

**This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright holder or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.**


================================================
FILE: README.md
================================================
# Pipe Operator (`|>`) for JavaScript

* **Stage**: 2
* **Champions**: J. S. Choi, James DiGioia, Ron Buckton, Tab Atkins-Bittner, \[list incomplete] <!-- Alpha order by first name, plz -->
* **Former champions**: Daniel Ehrenberg
* **[Specification][]**
* **[Contributing guidelines][]**
* **[Proposal history][]**
* **Babel plugin**: [Implemented in v7.15][Babel 7.15]. See [Babel documentation][].

(This document uses `%`
as the placeholder token for the topic reference.
This will ***almost certainly not be the final choice***;
see [the token bikeshedding discussion][token bikeshedding] for details.)

[specification]: http://tc39.github.io/proposal-pipeline-operator/
[Babel 7.15]: https://babeljs.io/blog/2021/07/26/7.15.0#hack-style-pipeline-operator-support-13191httpsgithubcombabelbabelpull13191-13416httpsgithubcombabelbabelpull13416
[Babel documentation]: https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator
[token bikeshedding]: https://github.com/tc39/proposal-pipeline-operator/issues/91
[contributing guidelines]: https://github.com/tc39/proposal-pipeline-operator/blob/main/CONTRIBUTING.md
[proposal history]: https://github.com/tc39/proposal-pipeline-operator/blob/main/HISTORY.md

## Why a pipe operator
In the State of JS 2020 survey, the **fourth top answer** to
[“What do you feel is currently missing from
JavaScript?”](https://2020.stateofjs.com/en-US/opinions/#missing_from_js)
was a **pipe operator**. Why?

When we perform **consecutive operations** (e.g., function calls)
on a **value** in JavaScript,
there are currently two fundamental styles:
* passing the value as an argument to the operation
  (**nesting** the operations if there are multiple operations),
* or calling the function as a method on the value
  (**chaining** more method calls if there are multiple methods).

That is, `three(two(one(value)))` versus `value.one().two().three()`.
However, these styles differ much in readability, fluency, and applicability.

### Deep nesting is hard to read
The first style, **nesting**, is generally applicable –
it works for any sequence of operations:
function calls, arithmetic, array/object literals, `await` and `yield`, etc.

However, nesting is **difficult to read** when it becomes deep:
the flow of execution moves **right to left**,
rather than the left-to-right reading of normal code.
If there are **multiple arguments** at some levels,
reading even bounces **back and forth**:
our eyes must **jump left** to find a function name,
and then they must **jump right** to find additional arguments.
Additionally, **editing** the code afterwards can be fraught:
we must find the correct **place to insert** new arguments
among **many nested parentheses**.

<details>
<summary><strong>Real-world example</strong></summary>

Consider this [real-world code from React](https://github.com/facebook/react/blob/17.0.2/scripts/jest/jest-cli.js#L295).

```js
console.log(
  chalk.dim(
    `$ ${Object.keys(envars)
      .map(envar =>
        `${envar}=${envars[envar]}`)
      .join(' ')
    }`,
    'node',
    args.join(' ')));
```

This real-world code is made of **deeply nested expressions**.
In order to read its flow of data, a human’s eyes must first:

1. Find the **initial data** (the innermost expression, `envars`).
2. And then scan **back and forth** repeatedly from **inside out**
   for each data transformation,
   each one either an easily missed prefix operator on the left
   or a suffix operators on the right:

   1. `Object.keys()` (left side),
   2. `.map()` (right side),
   3. `.join()` (right side),
   4. A template literal (both sides),
   5. `chalk.dim()` (left side), then
   6. `console.log()` (left side).

As a result of deeply nesting many expressions
(some of which use **prefix** operators,
some of which use **postfix** operators,
and some of which use **circumfix** operators),
we must check **both left and right sides**
to find the **head** of **each expression**.

</details>

### Method chaining is limited
The second style, **method chaining**, is **only** usable
if the value has the functions designated as **methods** for its class.
This **limits** its applicability.
But **when** it applies, thanks to its postfix structure,
it is generally more usable and **easier** to read and write.
Code execution flows **left to right**.
Deeply nested expressions are **untangled**.
All arguments for a function call are **grouped** with the function’s name.
And editing the code later to **insert or delete** more method calls is trivial,
since we would just have to put our cursor in one spot,
then start typing or deleting one **contiguous** run of characters.

Indeed, the benefits of method chaining are **so attractive**
that some **popular libraries contort** their code structure
specifically to allow **more method chaining**.
The most prominent example is **[jQuery][]**, which
still remains the **most popular JS library** in the world.
jQuery’s core design is a single über-object with dozens of methods on it,
all of which return the same object type so that we can **continue chaining**.
There is even a name for this style of programming:
**[fluent interfaces][]**.

[jQuery]: https://jquery.com/
[fluent interfaces]: https://en.wikipedia.org/wiki/Fluent_interface

Unfortunately, for all of its fluency,
**method chaining** alone cannot accommodate JavaScript’s **other syntaxes**:
function calls, arithmetic, array/object literals, `await` and `yield`, etc.
In this way, method chaining remains **limited** in its **applicability**.

### Pipe operators combine both worlds
The pipe operator attempts to marry the **convenience** and ease of **method chaining**
with the wide **applicability** of **expression nesting**.

The general structure of all the pipe operators is
`value |>` <var>e1</var> `|>` <var>e2</var> `|>` <var>e3</var>,
where <var>e1</var>, <var>e2</var>, <var>e3</var>
are all expressions that take consecutive values as their parameters.
The `|>` operator then does some degree of magic to “pipe” `value`
from the lefthand side into the righthand side.

<details>
<summary><strong>Real-world example</strong>, continued</summary>

Continuing this deeply nested [real-world code from React][react/scripts/jest/jest-cli.js]:

```js
console.log(
  chalk.dim(
    `$ ${Object.keys(envars)
      .map(envar =>
        `${envar}=${envars[envar]}`)
      .join(' ')
    }`,
    'node',
    args.join(' ')));
```

…we can **untangle** it as such using a pipe operator
and a placeholder token (`%`) standing in for the previous operation’s value:

```js
Object.keys(envars)
  .map(envar => `${envar}=${envars[envar]}`)
  .join(' ')
  |> `$ ${%}`
  |> chalk.dim(%, 'node', args.join(' '))
  |> console.log(%);
```

Now, the human reader can **rapidly find** the **initial data**
(what had been the most innermost expression, `envars`),
then **linearly** read, from **left to right**,
each transformation on the data.

</details>

### Temporary variables are often tedious
One could argue that using **temporary variables**
should be the only way to untangle deeply nested code.
Explicitly naming every step’s variable
causes something similar to method chaining to happen,
with similar benefits to reading and writing code.

<details>
<summary><strong>Real-world example</strong>, continued</summary>

For example, using our previous modified
[real-world example from React][react/scripts/jest/jest-cli.js]:

```js
Object.keys(envars)
  .map(envar => `${envar}=${envars[envar]}`)
  .join(' ')
  |> `$ ${%}`
  |> chalk.dim(%, 'node', args.join(' '))
  |> console.log(%);
```

…a version using temporary variables would look like this:

```js
const envarString = Object.keys(envars)
  .map(envar => `${envar}=${envars[envar]}`)
  .join(' ');
const consoleText = `$ ${envarString}`;
const coloredConsoleText = chalk.dim(consoleText, 'node', args.join(' '));
console.log(coloredConsoleText);
```

</details>

But there are reasons why we encounter deeply nested expressions
in each other’s code **all the time in the real world**,
**rather than** lines of temporary variables.
And there are reasons why the **method-chain-based [fluent interfaces][]**
of jQuery, Mocha, and so on are still **popular**.

It is often simply too **tedious and wordy** to **write**
code with a long sequence of temporary, single-use variables.
It is arguably even tedious and visually noisy for a human to **read**, too.

If [**naming** is one of the **most difficult tasks** in programming][naming hard],
then programmers will **inevitably avoid naming** variables
when they perceive their benefit to be relatively small.

[naming hard]: https://martinfowler.com/bliki/TwoHardThings.html

### Reusing temporary variables is prone to unexpected mutation
One could argue that using a single **mutable variable** with a short name
would reduce the wordiness of temporary variables, achieving
similar results as with the pipe operator.

<details>
<summary><strong>Real-world example</strong>, continued</summary>

For example, our previous modified
[real-world example from React][react/scripts/jest/jest-cli.js]
could be re-written like this:
```js
let _;
_ = Object.keys(envars)
  .map(envar => `${envar}=${envars[envar]}`)
  .join(' ');
_ = `$ ${_}`;
_ = chalk.dim(_, 'node', args.join(' '));
_ = console.log(_);
```

</details>

But code like this is **not common** in real-world code.
One reason for this is that mutable variables can **change unexpectedly**,
causing silent bugs that are hard to find.
For example, the variable might be accidentally referenced in a closure.
Or it might be mistakenly reassigned within an expression.

<details>
<summary>Example code</summary>

```js
// setup
function one () { return 1; }
function double (x) { return x * 2; }

let _;
_ = one(); // _ is now 1.
_ = double(_); // _ is now 2.
_ = Promise.resolve().then(() =>
  // This does *not* print 2!
  // It prints 1, because `_` is reassigned downstream.
  console.log(_));

// _ becomes 1 before the promise callback.
_ = one(_);
```

This issue would not happen with the pipe operator.
The topic token cannot be reassigned, and
code outside of each step cannot change its binding.

```js
let _;
_ = one()
  |> double(%)
  |> Promise.resolve().then(() =>
    // This prints 2, as intended.
    console.log(%));

_ = one();
```

</details>

For this reason, code with mutable variables is also harder to read.
To determine what the variable represents at any given point,
you must to **search the entire preceding scope** for places where it is **reassigned**.

The topic reference of a pipeline, on the other hand, has a limited lexical scope,
and its binding is immutable within its scope.
It cannot be accidentally reassigned, and it can be safely used in closures.

Although the topic value also changes with each pipeline step,
we only scan the previous step of the pipeline to make sense of it,
leading to code that is easier to read.

### Temporary variables must be declared in statements
Another benefit of the pipe operator over sequences of assignment statements
(whether with mutable or with immutable temporary variables)
is that they are **expressions**.

Pipe expressions are expressions that can be directly returned,
assigned to a variable, or used in contexts such as JSX expressions.

Using temporary variables, on the other hand, requires sequences of statements.

<details>
<summary>Examples</summary>

<table>
<thead>
<th>Pipelines</th>
<th>Temporary Variables</th>
</thead>

<tbody>
<tr>
<td>

```js
const envVarFormat = vars =>
  Object.keys(vars)
    .map(var => `${var}=${vars[var]}`)
    .join(' ')
    |> chalk.dim(%, 'node', args.join(' '));
```

</td>
<td>

```js
const envVarFormat = (vars) => {
  let _ = Object.keys(vars);
  _ = _.map(var => `${var}=${vars[var]}`);
  _ = _.join(' ');
  return chalk.dim(_, 'node', args.join(' '));
}
```

</td>
</tr>
<tr>
<td>

```jsx
// This example uses JSX.
return (
  <ul>
    {
      values
        |> Object.keys(%)
        |> [...Array.from(new Set(%))]
        |> %.map(envar => (
          <li onClick={
            () => doStuff(values)
          }>{envar}</li>
        ))
    }
  </ul>
);
```

</td>
<td>

```js
// This example uses JSX.
let _ = values;
_= Object.keys(_);
_= [...Array.from(new Set(_))];
_= _.map(envar => (
  <li onClick={
    () => doStuff(values)
  }>{envar}</li>
));
return (
  <ul>{_}</ul>
);
```

</td>
</tr>
</tbody>
</table>

</details>

## Why the Hack pipe operator
There were **two competing proposals** for the pipe operator: Hack pipes and F# pipes.
(Before that, there **was** a [third proposal for a “smart mix” of the first two proposals][smart mix],
but it has been withdrawn,
since its syntax is strictly a superset of one of the proposals’.)

[smart mix]: https://github.com/js-choi/proposal-smart-pipelines/

The two pipe proposals just differ **slightly** on what the “magic” is,
when we spell our code when using `|>`.

**Both** proposals **reuse** existing language concepts:
Hack pipes are based on the concept of the **expression**,
while F# pipes are based on the concept of the **unary function**.

Piping **expressions** and piping **unary functions**
correspondingly have **small** and nearly **symmetrical trade-offs**.

### This proposal: Hack pipes
In the **Hack language**’s pipe syntax,
the righthand side of the pipe is an **expression** containing a special **placeholder**,
which is evaluated with the placeholder bound to the result of evaluating the lefthand side's expression.
That is, we write `value |> one(%) |> two(%) |> three(%)`
to pipe `value` through the three functions.

**Pro:** The righthand side can be **any expression**,
and the placeholder can go anywhere any normal variable identifier could go,
so we can pipe to any code we want **without any special rules**:

* `value |> foo(%)` for unary function calls,
* `value |> foo(1, %)` for n-ary function calls,
* `value |> %.foo()` for method calls,
* `value |> % + 1` for arithmetic,
* `value |> [%, 0]` for array literals,
* `value |> {foo: %}` for object literals,
* `` value |> `${%}` `` for template literals,
* `value |> new Foo(%)` for constructing objects,
* `value |> await %` for awaiting promises,
* `value |> (yield %)` for yielding generator values,
* `value |> import(%)` for calling function-like keywords,
* etc.

**Con:** Piping through **unary functions**
is **slightly more verbose** with Hack pipes than with F# pipes.
This includes unary functions
that were created by **[function-currying][] libraries** like [Ramda][],
as well as [unary arrow functions
that perform **complex destructuring** on their arguments][destruct]:
Hack pipes would be slightly more verbose
with an **explicit** function call suffix `(%)`.

(Complex destructuring of the topic value
will be easier when [do expressions][] progress,
as you will then be able to do variable assignment/destructuring
inside of a pipe body.)

[function-currying]: https://en.wikipedia.org/wiki/Currying
[Ramda]: https://ramdajs.com/
[destruct]: https://github.com/js-choi/proposal-hack-pipes/issues/4#issuecomment-817208635

### Alternative proposal: F# pipes
In the [**F# language**’s pipe syntax][F# pipes],
the righthand side of the pipe is an expression
that must **evaluate into a unary function**,
which is then **tacitly called**
with the lefthand side’s value as its **sole argument**.
That is, we write `value |> one |> two |> three` to pipe `value`
through the three functions.
`left |> right` becomes `right(left)`.
This is called [tacit programming or point-free style][tacit].

[F# pipes]: https://github.com/valtech-nyc/proposal-fsharp-pipelines
[tacit]: https://en.wikipedia.org/wiki/Tacit_programming

<details>
<summary><strong>Real-world example</strong>, continued</summary>

For example, using our previous modified
[real-world example from React][react/scripts/jest/jest-cli.js]:

```js
Object.keys(envars)
  .map(envar => `${envar}=${envars[envar]}`)
  .join(' ')
  |> `$ ${%}`
  |> chalk.dim(%, 'node', args.join(' '))
  |> console.log(%);
```

…a version using F# pipes instead of Hack pipes would look like this:

```js
Object.keys(envars)
  .map(envar => `${envar}=${envars[envar]}`)
  .join(' ')
  |> x=> `$ ${x}`
  |> x=> chalk.dim(x, 'node', args.join(' '))
  |> console.log;
```

</details>

**Pro:** The restriction that the righthand side
**must** resolve to a unary function
lets us write very terse pipes
**when** the operation we want to perform
is a **unary function call**:

* `value |> foo` for unary function calls.

This includes unary functions
that were created by **[function-currying][] libraries** like [Ramda][],
as well as [unary arrow functions
that perform **complex destructuring** on their arguments][destruct]:
F# pipes would be **slightly less verbose**
with an **implicit** function call (no `(%)`).

**Con:** The restriction means that **any operations**
that are performed by **other syntax**
must be made **slightly more verbose** by **wrapping** the operation
in a unary **arrow function**:

* `value |> x=> x.foo()` for method calls,
* `value |> x=> x + 1` for arithmetic,
* `value |> x=> [x, 0]` for array literals,
* `value |> x=> ({foo: x})` for object literals,
* `` value |> x=> `${x}` `` for template literals,
* `value |> x=> new Foo(x)` for constructing objects,
* `value |> x=> import(x)` for calling function-like keywords,
* etc.

Even calling **named functions** requires **wrapping**
when we need to pass **more than one argument**:

* `value |> x=> foo(1, x)` for n-ary function calls.

**Con:** The **`await` and `yield`** operations are **scoped**
to their **containing function**,
and thus **cannot be handled by unary functions** alone.
If we want to integrate them into a pipe expression,
[`await` and `yield` must be handled as **special syntax cases**][enhanced F# pipes]:

* `value |> await` for awaiting promises, and
* `value |> yield` for yielding generator values.

[enhanced F# pipes]: https://github.com/valtech-nyc/proposal-fsharp-pipelines/

### Hack pipes favor more common expressions
**Both** Hack pipes and F# pipes respectively impose
a small **syntax tax** on different expressions:\
**Hack pipes** slightly tax only **unary function calls**, and\
**F# pipes** slightly tax **all expressions except** unary function calls.

In **both** proposals, the syntax tax per taxed expression is **small**
(**both** `(%)` and `x=>` are **only three characters**).
However, the tax is **multiplied** by the **prevalence**
of its respectively taxed expressions.
It therefore might make sense
to impose a tax on whichever expressions are **less common**
and to **optimize** in favor of whichever expressions are **more common**.

Unary function calls are in general **less common**
than **all** expressions **except** unary functions.
In particular, **method** calling and **n-ary function** calling
will **always** be **popular**;
in general frequency,
**unary** function calling is equal to or exceeded by
those two cases **alone** –
let alone by other ubiquitous syntaxes
such as **array literals**, **object literals**,
and **arithmetic operations**.
This explainer contains several [real-world examples][]
of this difference in prevalence.

[real-world examples]: #real-world-examples

Furthermore, several other proposed **new syntaxes**,
such as **[extension calling][]**,
**[do expressions][]**,
and **[record/tuple literals][]**,
will also likely become **pervasive** in the **future**.
Likewise, **arithmetic** operations would also become **even more common**
if TC39 standardizes **[operator overloading][]**.
Untangling these future syntaxes’ expressions would be more fluent
with Hack pipes compared to F# pipes.

[extension calling]: https://github.com/tc39/proposal-extensions/
[do expressions]: https://github.com/tc39/proposal-do-expressions/
[record/tuple literals]: https://github.com/tc39/proposal-record-tuple/
[operator overloading]: https://github.com/tc39/proposal-operator-overloading/

### Hack pipes might be simpler to use
The syntax tax of Hack pipes on unary function calls
(i.e., the `(%)` to invoke the righthand side’s unary function)
is **not a special case**:
it simply is **explicitly writing ordinary code**,
in **the way we normally would** without a pipe.

On the other hand, **F# pipes require** us to **distinguish**
between “code that resolves to an unary function”
versus **“any other expression”** –
and to remember to add the arrow-function wrapper around the latter case.

For example, with Hack pipes, `value |> someFunction + 1`
is **invalid syntax** and will **fail early**.
There is no need to recognize that `someFunction + 1`
will not evaluate into a unary function.
But with F# pipes, `value |> someFunction + 1` is **still valid syntax** –
it’ll just **fail late** at **runtime**,
because `someFunction + 1` isn’t callable.

### TC39 has rejected F# pipes multiple times
The pipe champion group has presented F# pipes for Stage 2 to TC39 **twice**.
It was **unsuccessful** in advancing to Stage 2 both times.
Both F# pipes (and [partial function application (PFA)][PFA syntax])
have run into strong pushback from multiple other TC39 representatives
due to various concerns. These have included:

* Memory performance concerns (e.g., [especially from browser-engine implementors][V8 pushback]),
* Syntax concerns about `await`.
* Concerns about encouraging ecosystem bifurcation/forking, etc.

[V8 pushback]: https://github.com/tc39/proposal-pipeline-operator/blob/main/HISTORY.md#2021-07

This pushback has occurred from **outside** the pipe champion group.
See [HISTORY.md][] for more information.

It is the pipe champion group’s belief that any pipe operator is better than none,
in order to [easily linearize deeply nested expressions](#why-a-pipe-operator)
without resorting to named variables.
Many members of the champion group believe that Hack pipes are slightly better than F# pipes,
and some members of the champion group believe that F# pipes are slightly better than Hack pipes.
But everyone in the champion group agrees that F# pipes have met with far too much resistance
to be able to pass TC39 in the foreseeable future.

To emphasize, it is likely that an attempt to switch from Hack pipes back to F# pipes
will result in TC39 never agreeing to any pipes at all.
[PFA syntax][] is similarly facing an uphill battle in TC39 (see [HISTORY.md][]).
Many members of the pipe champion group think this is unfortunate,
and they are willing to fight again **later** for an [F#-pipe split mix][split mix] and [PFA syntax][].
But there are quite a few representatives (including [browser-engine implementers][V8 pushback])
outside of the Pipe Champion Group
who are generally against encouraging [tacit programming][] (and [PFA syntax][]),
regardless of Hack pipes.

[HISTORY.md]: https://github.com/tc39/proposal-pipeline-operator/blob/main/HISTORY.md
[tacit programming]: https://en.wikipedia.org/wiki/Tacit_programming
[PFA syntax]: https://github.com/tc39/proposal-partial-application
[split mix]: #tacit-unary-function-application-syntax

## Description
(A [formal draft specification][specification] is available.)

The **topic reference** `%` is a **nullary operator**.
It acts as a placeholder for a **topic value**,
and it is **lexically scoped** and **immutable**.

<details>
<summary><code>%</code> is not a final choice</summary>

(The precise [**token** for the topic reference is **not final**][token bikeshedding].
`%` could instead be `^`, or many other tokens.
We plan to [**bikeshed** what actual token to use][token bikeshedding]
before advancing to Stage 3.
However, `%` seems to be the [least syntactically problematic][],
and it also resembles the placeholders of **[printf format strings][]**
and [**Clojure**’s `#(%)` **function literals**][Clojure function literals].)

[least syntactically problematic]: https://github.com/js-choi/proposal-hack-pipes/issues/2
[Clojure function literals]: https://clojure.org/reference/reader#_dispatch
[printf format strings]: https://en.wikipedia.org/wiki/Printf_format_string

</details>

The **pipe operator** `|>` is an **infix operator**
that forms a **pipe expression** (also called a **pipeline**).
It evaluates its lefthand side (the **pipe head** or **pipe input**),
immutably **binds** the resulting value (the **topic value**) to the **topic reference**,
then evaluates its righthand side (the **pipe body**) with that binding.
The resulting value of the righthand side
becomes the whole pipe expression’s final value (the **pipe output**).

The pipe operator’s precedence is the **same** as:
* the function arrow `=>`;
* the assignment operators `=`, `+=`, etc.;
* the generator operators `yield` and `yield *`;

It is **tighter** than only the comma operator `,`.\
It is **looser** than **all other** operators.

For example, `v => v |> % == null |> foo(%, 0)`\
would group into `v => (v |> (% == null) |> foo(%, 0))`,\
which in turn is equivalent to `v => foo(v == null, 0)`.

A pipe body **must** use its topic value **at least once**.
For example, `value |> foo + 1` is **invalid syntax**,
because its body does not contain a topic reference.
This design is because **omission** of the topic reference
from a pipe expression’s body
is almost certainly an **accidental** programmer error.

Likewise, a topic reference **must** be contained in a pipe body.
Using a topic reference outside of a pipe body
is also **invalid syntax**.

To prevent confusing grouping,
it is **invalid** syntax to use **other** operators that have **similar precedence**
(i.e., the arrow `=>`, the ternary conditional operator `?` `:`,
the assignment operators, and the `yield` operator)
as a **pipe head or body**.
When using `|>` with these operators, we must use **parentheses**
to explicitly indicate what grouping is correct.
For example, `a |> b ? % : c |> %.d` is invalid syntax;
it should be corrected to either `a |> (b ? % : c) |> %.d`
or `a |> (b ? % : c |> %.d)`.

Lastly, topic bindings **inside dynamically compiled** code
(e.g., with `eval` or `new Function`)
**cannot** be used **outside** of that code.
For example, `v |> eval('% + 1')` will throw a syntax error
when the `eval` expression is evaluated at runtime.

There are **no other special rules**.

A natural result of these rules is that,
if we need to interpose a **side effect**
in the middle of a chain of pipe expressions,
without modifying the data being piped through,
then we could use a **comma expression**,
such as with `value |> (sideEffect(), %)`.
As usual, the comma expression will evaluate to its righthand side `%`,
essentially passing through the topic value without modifying it.
This is especially useful for quick debugging: `value |> (console.log(%), %)`.

## Real-world examples
The only changes to the original examples were dedentation and removal of comments.

From [jquery/build/tasks/sourceMap.js][]:
```js
// Status quo
var minLoc = Object.keys( grunt.config( "uglify.all.files" ) )[ 0 ];

// With pipes
var minLoc = grunt.config('uglify.all.files') |> Object.keys(%)[0];
```

From [node/deps/npm/lib/unpublish.js][]:
```js
// Status quo
const json = await npmFetch.json(npa(pkgs[0]).escapedName, opts);

// With pipes
const json = pkgs[0] |> npa(%).escapedName |> await npmFetch.json(%, opts);
```

From [underscore.js][]:
```js
// Status quo
return filter(obj, negate(cb(predicate)), context);

// With pipes
return cb(predicate) |> _.negate(%) |> _.filter(obj, %, context);
```

From [ramda.js][].
```js
// Status quo
return xf['@@transducer/result'](obj[methodName](bind(xf['@@transducer/step'], xf), acc));

// With pipes
return xf
  |> bind(%['@@transducer/step'], %)
  |> obj[methodName](%, acc)
  |> xf['@@transducer/result'](%);
```

From [ramda.js][].
```js
// Status quo
try {
  return tryer.apply(this, arguments);
} catch (e) {
  return catcher.apply(this, _concat([e], arguments));
}

// With pipes: Note the visual parallelism between the two clauses.
try {
  return arguments
    |> tryer.apply(this, %);
} catch (e) {
  return arguments
    |> _concat([e], %)
    |> catcher.apply(this, %);
}
```

From [express/lib/response.js][].
```js
// Status quo
return this.set('Link', link + Object.keys(links).map(function(rel){
  return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));

// With pipes
return links
  |> Object.keys(%).map(function (rel) {
    return '<' + links[rel] + '>; rel="' + rel + '"';
  })
  |> link + %.join(', ')
  |> this.set('Link', %);
```

From [react/scripts/jest/jest-cli.js][].
```js
// Status quo
console.log(
  chalk.dim(
    `$ ${Object.keys(envars)
      .map(envar => `${envar}=${envars[envar]}`)
      .join(' ')}`,
    'node',
    args.join(' ')
  )
);

// With pipes
Object.keys(envars)
  .map(envar => `${envar}=${envars[envar]}`)
  .join(' ')
  |> `$ ${%}`
  |> chalk.dim(%, 'node', args.join(' '))
  |> console.log(%);
```

From [ramda.js][].
```js
// Status quo
return _reduce(xf(typeof fn === 'function' ? _xwrap(fn) : fn), acc, list);

// With pipes
return fn
  |> (typeof % === 'function' ? _xwrap(%) : %)
  |> xf(%)
  |> _reduce(%, acc, list);
```

From [jquery/src/core/init.js][].
```js
// Status quo
jQuery.merge( this, jQuery.parseHTML(
  match[ 1 ],
  context && context.nodeType ? context.ownerDocument || context : document,
  true
) );

// With pipes
context
  |> (% && %.nodeType ? %.ownerDocument || % : document)
  |> jQuery.parseHTML(match[1], %, true)
  |> jQuery.merge(%);
```

[ramda.js]: https://github.com/ramda/ramda/blob/v0.27.1/dist/ramda.js
[node/deps/npm/lib/unpublish.js]: https://github.com/nodejs/node/blob/v16.x/deps/npm/lib/unpublish.js
[node/deps/v8/test/mjsunit/regress/regress-crbug-158185.js]: https://github.com/nodejs/node/blob/v16.x/deps/v8/test/mjsunit/regress/regress-crbug-158185.js
[express/lib/response.js]: https://github.com/expressjs/express/blob/5.0/lib/response.js
[react/scripts/jest/jest-cli.js]: https://github.com/facebook/react/blob/17.0.2/scripts/jest/jest-cli.js
[jquery/build/tasks/sourceMap.js]: https://github.com/jquery/jquery/blob/2.2-stable/build/tasks/sourcemap.js
[jquery/src/core/init.js]: https://github.com/jquery/jquery/blob/2.2-stable/src/core/init.js
[underscore.js]: https://underscorejs.org/docs/underscore-esm.html

## Relationships with other proposals

### `Function` helpers
Hack pipes can and would coexist with the [`Function` helpers proposal][helpers],
including its `pipe` and `flow` functions.
These simple (and commonly downloaded) convenience functions
manipulate unary functions without extra syntax.

[helpers]: https://github.com/js-choi/proposal-function-helpers

[TC39 has rejected the F# pipe operator twice][rejected].
Given this reality, TC39 is considerably more likely to pass
`pipe` and `flow` helper functions than a similar syntactic operator.

[rejected]: #tc39-has-rejected-f-pipes-multiple-times

Standardized `pipe` and `flow` convenience functions
may also obviate some of the need for a F#-pipe infix operator.
(They would not preclude standardizing an equivalent operator later.
For example, TC39 standardized binary `**` even when `Math.pow` existed.)

### Partial-function-application syntax
Hack pipes can coexist with a syntax for **partial function application** (PFA).
There are two approaches with which they may coexist.

The **first approach** is with an **eagerly** evaluated PFA syntax,
which has [already been proposed in proposal-partial-application][PFA syntax].
This eager PFA syntax would add an `…~(…)` operator.
The operator’s right-hand side would be a list of arguments,
each of which is an ordinary expression or a `?` placeholder.
Each consecutive `?` placeholder would represent another parameter.

Ordinary expressions would be evaluated **before** the function is created.
For example, `f~(g(), ?, h(), ?)` would evaluate `f`, then `g()`, then `h()`,
and *then* it would create a partially applied version of `f` with two arguments.

An optional number after `?` placeholder
would override the parameter’s position.
For example, `f~(?1, ?0)` would have two parameters but would switch them when calling `f`.

The **second approach** is with a **lazily** evaluated syntax.
This could be handled with an **extension to Hack pipes**,
with a syntax further inspired by
[Clojure’s `#(%1 %2)` function literals][Clojure function literals].
It would do so by **combining** the Hack pipe `|>`
with the **arrow function** `=>`
into a **pipe-function** operator `+>`,
which would use the same general rules as `|>`.

`+>` would be a **prefix operator** that **creates a new function**,
which in turn **binds its argument(s)** to topic references.
**Non-unary functions** would be created
by including topic references with **numbers** (`%0`, `%1`, `%2`, etc.) or `...`.
`%0` (equivalent to plain `%`) would be bound to the **zeroth argument**,
`%1` would be bound to the next argument, and so on.
`%...` would be bound to an array of **rest arguments**.
And just as with `|>`, `+>` would require its body
to contain at least one topic reference
in order to be syntactically valid.

| Eager PFA                  | Pipe functions             |
| ---------------------------| -------------------------- |
|`a.map(f~(?, 0))`           |`a.map(+> f(%, 0))`         |
|`a.map(f~(?, ?, 0))`        |`a.map(+> f(%0, %1, 0))`    |
|`a.map(x=> x + 1)`          |`a.map(+> % + 1)`           |
|`a.map(x=> x + x)`          |`a.map(+> % + %)`           |
|`a.map(x=> f(x, x))`        |`a.map(+> f(%, %))`         |

In contrast to the [eagerly evaluated PFA syntax][PFA syntax],
topic functions would **lazily** evaluate its arguments,
just like how an arrow function would.

For example, `+> f(g(), %0, h(), %1)` would evaluate `f`,
and then it would create an arrow function that closes over `g` and `h`.
The created function would **not** evaluate `g()` or `h()`
until the every time the created function is called.

No matter the approach taken, Hack pipes could coexist with PFA.

### Eventual sending / pipelining
Despite sharing the word “pipe” in their name,
the pipe operator and the [eventual-send proposal][]’s remote-object pipelines
are orthogonal and independent.
They can coexist and even work together.

```js
const fileP = E(
  E(target).openDirectory(dirName)
).openFile(fileName);

const fileP = target
|> E(%).openDirectory(dirName)
|> E(%).openFile(fileName);
```

[eventual-send proposal]: https://github.com/tc39/proposal-eventual-send/

## Possible future extensions

### Hack-pipe syntax for `if`, `catch`, and `for`–`of`
Many **`if`, `catch`, and `for` statements** could become pithier
if they gained **“pipe syntax”** that bound the topic reference.

`if () |>` would bind its condition value to `%`,\
`catch |>` would bind its caught error to `%`,\
and `for (of) |>` would consecutively bind each of its iterator’s values to `%`.

| Status quo                  | Hack-pipe statement syntax |
| --------------------------- | -------------------------- |
|`const c = f(); if (c) g(c);`|`if (f()) \|> g(%);`        |
|`catch (e) f(e);`            |`catch \|> f(%);`           |
|`for (const v of f()) g(v);` |`for (f()) \|> g(%);`       |

### Optional Hack pipes
A **short-circuiting** optional-pipe operator `|?>` could also be useful,
much in the way `?.` is useful for optional method calls.

For example, `value |> (% == null ? % : await foo(%) |> (% == null ? % : % + 1))`\
would be equivalent to `value |?> await foo(%) |?> % + 1`.

### Tacit unary function application syntax
**Syntax** for **tacit unary function application** – that is, the F# pipe operator –
has been [rejected twice by TC39][rejected].
However, they could still eventually be added to the language in two ways.

First, it can be added as a convenience function `Function.pipe`.
This is what the [function-helpers proposal][helpers] proposes.
`Function.pipe` may obviate much of the need for an F#-pipe operator,
while still not closing off the possibility of an F#-pipe operator.

Secondly, it can be added as **another pipe operator** `|>>` –
similarly to how [Clojure has multiple pipe macros][Clojure pipes]
`->`, `->>`, and `as->`.\
For example, `value |> % + 1 |>> f |> g(%, 0)`\
would mean `value |> % + 1 |> f(%) |> g(%, 0)`.

[Clojure pipes]: https://clojure.org/guides/threading_macros

There was an [informal proposal for such a **split mix** of two pipe operators][split mix],
which was set aside in favor of single-operator proposals.
This split mix might return as a proposal after Hack pipes.

[split mix]: https://github.com/tc39/proposal-pipeline-operator/wiki#proposal-3-split-mix


================================================
FILE: package.json
================================================
{
  "private": true,
  "name": "proposal-pipeline-operator",
  "version": "1.0.0",
  "description": "ECMAScript pipe-operator proposal",
  "repository": "tc39/proposal-pipeline-operator",
  "author": "Ecma TC39",
  "license": "BSD-3-Clause",
  "homepage": "https://tc39.github.io/proposal-pipeline-operator/",
  "dependencies": {
    "ecmarkup": "^12.0.0"
  },
  "scripts": {
    "prebuild": "mkdir -p dist",
    "build": "ecmarkup --verbose spec.html dist/index.html",
    "watch": "npm run build -- --watch"
  }
}


================================================
FILE: spec.html
================================================
<pre class=metadata>
title: ES pipe operator (2021)
status: proposal
stage: 2
location: https://github.com/tc39/proposal-pipeline-operator
copyright: false
contributors: J. S. Choi, James DiGioia, Ron Buckton, Tab Atkins-Bittner
</pre>
<script src=ecmarkup.js defer></script>
<link rel=stylesheet href=ecmarkup.css>

<emu-intro id=introduction>
  <h1>Introduction</h1>
  <p>This is the formal specification for a proposed Hack-style pipe
  operator `|&gt;` in JavaScript. It modifies the original <a
  href=https://tc39.github.io/ecma262/>ECMAScript specification</a> with
  several new or revised clauses. See <a
  href=https://github.com/js-choi/proposal-hack-pipes/blob/main/README.md>the proposal's
  explainer</a> for the proposal's background, motivation, and usage examples.</p>

  <p>This document presumptively uses `%` as the token
  for the topic reference. This choice of token is not a final decision;
  `%` could instead be `^` or some other token.</p>
</emu-intro>

<emu-clause id="sec-syntax-directed-operations">
  <h1>Syntax-Directed Operations</h1>

  <emu-clause id="sec-syntax-directed-operations-function-name-inference">
    <h1>Function Name Inference</h1>

    <emu-clause id="sec-static-semantics-isfunctiondefinition" type="sdo"
    aoid="IsFunctionDefinition">
      <h1>Static Semantics: IsFunctionDefinition</h1>

      <emu-note type=editor>
        <p>This section augments the <a
        href=https://tc39.es/ecma262/#sec-static-semantics-isfunctiondefinition>original
        IsFunctionDefinition clause</a>.</p>

        <p>It presumptively uses `%` as the placeholder token for the
        topic reference. This choice of token is not a final decision; `%`
        could instead be `^` or some other token.</p>
      </emu-note>

      <emu-grammar>
        PrimaryExpression :
          `this`
          <ins>`%`</ins>
          IdentifierReference
          Literal
          ArrayLiteral
          ObjectLiteral
          RegularExpressionLiteral
          TemplateLiteral

        <ins>PipeExpression :
          ShortCircuitExpression `|&gt;` PipeBody</ins>
      </emu-grammar>
      <emu-alg>
        1. Return *false*.
      </emu-alg>
    </emu-clause>

    <emu-clause id="sec-static-semantics-isidentifierref" type="sdo"
    aoid="IsIdentifierRef">
      <h1>Static Semantics: IsIdentifierRef</h1>

      <emu-note type=editor>
        <p>This section augments the <a
        href=https://tc39.es/ecma262/#sec-static-semantics-isidentifierref>original IsIdentifierRef
        clause</a>.</p>

        <p>It presumptively uses `%` as the placeholder token for the
        topic reference. This choice of token is not a final decision; `%`
        could instead be `^` or some other token.</p>
      </emu-note>

      <emu-grammar>PrimaryExpression : IdentifierReference</emu-grammar>
      <emu-alg>
        1. Return *true*.
      </emu-alg>
      <emu-grammar>
        PrimaryExpression :
          `this`
          <ins>`%`</ins>
          Literal
          ArrayLiteral
          ObjectLiteral
          FunctionExpression
          ClassExpression
          GeneratorExpression
          AsyncFunctionExpression
          AsyncGeneratorExpression
          RegularExpressionLiteral
          TemplateLiteral
          CoverParenthesizedExpressionAndArrowParameterList
      </emu-grammar>
      <emu-alg>
        1. Return *false*.
      </emu-alg>
    </emu-clause>
  </emu-clause>

  <emu-clause id="sec-syntax-directed-operations-contains">
    <h1>Contains</h1>

    <emu-clause id="sec-static-semantics-ContainsOuterTopic" type="sdo" aoid="ContainsOuterTopic">
      <h1>Static Semantics: ContainsOuterTopic</h1>
      <emu-note type=editor>
        <p>This section is a wholly new sub-clause of the <a
        href=https://tc39.github.io/ecma262/#sec-syntax-directed-operations-contains
        >original Contains clause</a>.</p>
      </emu-note>

      <emu-note>
        <p>Several early error rules for |ScriptBody| and for
        |ModuleItemList|, as well as a step in CreateDynamicFunction,
        use the ContainsOuterTopic operation to check for any unbound topic reference `%`.
        Any inner topic reference within a |PipeBody| is hidden from these rules,
        preventing them from triggering the rules during program
        compilation.</p>

        <p>This guarantees that any topic reference in a program must be
        present within a topic-binding environment
        created by a |PipeBody| within that program.</p>
      </emu-note>

      <p>Every grammar production alternative in this specification which is not listed below implicitly has the following default definition of ContainsOuterTopic:</p>

      <emu-alg>
        1. For each child node _child_ of this Parse Node, do
          1. If _child_ is an instance of `%`, return *true*.
          1. If _child_ is an instance of a nonterminal, and if ContainsOuterTopic of _child_ is *true*, return *true*.
        1. Return *false*.
      </emu-alg>

      <emu-grammar>MultiplicativeExpression : MultiplicativeExpression MultiplicativeOperator ExponentiationExpression</emu-grammar>

      <emu-alg>
        1. If ContainsOuterTopic of |MultiplicativeExpression| is *true*, or if ContainsOuterTopic of |ExponentiationExpression| is *true*, return *true*.
        1. Return *false*.
      </emu-alg>

      <emu-grammar>PipeBody : AssignmentExpression</emu-grammar>

      <emu-alg>
        1. Return *false*.
      </emu-alg>
    </emu-clause>
  </emu-clause>

  <emu-clause id="sec-syntax-directed-operations-miscellaneous">
    <h1>Miscellaneous</h1>

    <emu-clause id="sec-static-semantics-assignmenttargettype" type="sdo"
    aoid="AssignmentTargetType">
      <h1>Static Semantics: AssignmentTargetType</h1>

      <emu-note type=editor>
        <p>This section augments the <a
        href=https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype>original
        AssignmentTargetType clause</a>.</p>

        <p>It presumptively uses `%` as the placeholder token for the
        topic reference. This choice of token is not a final decision; `%`
        could instead be `^` or some other token.</p>
      </emu-note>

      <emu-grammar>
        PrimaryExpression :
          `this`
          <ins>`%`</ins>
          Literal
          ArrayLiteral
          ObjectLiteral
          FunctionExpression
          ClassExpression
          GeneratorExpression
          AsyncFunctionExpression
          AsyncGeneratorExpression
          RegularExpressionLiteral
          TemplateLiteral

        <ins>PipeExpression :
          ShortCircuitExpression `|&gt;` PipeBody</ins>
      </emu-grammar>
      <emu-alg>
        1. Return ~invalid~.
      </emu-alg>
    </emu-clause>
  </emu-clause>
</emu-clause>

<emu-clause id=executable-code-and-execution-contexts>
  <h1>Executable Code and Execution Contexts</h1>

  <emu-clause id=sec-environment-records>
    <h1>Environment Records</h1>

    <emu-clause id=sec-the-environment-record-type-hierarchy>
      <h1>The Environment Record Type Hierarchy</h1>

      <emu-note type=editor>
        <p>This section augments the <a
        href=https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy>original
        Environment Records clause</a>.</p>
      </emu-note>

      <emu-table id=table-15 caption="Abstract Methods of Environment Records">
        <table>
          <thead>
            <tr>
              <th>Method</th>
              <th>Purpose</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td><ins><emu-xref
                href=#sec-declarative-environment-records-hastopicbinding
                >HasTopicBinding()</emu-xref></ins></td>
              <td><ins>Determine whether an Environment Record is a
                topic-binding environment. Return *true* if it establishes a
                topic binding and *false* if it does not.</ins></td>
            </tr>
          </tbody>
        </table>
      </emu-table>

      <emu-clause id=sec-declarative-environment-records>
        <h1>Declarative Environment Records</h1>

        <emu-note type=editor>
          <p>This section augments the <a
          href=https://tc39.github.io/ecma262/#sec-declarative-environment-records
          >original Declarative Environment Records clause</a>.</p>

          <p>It presumptively uses `%` as the placeholder token for the
          topic reference. This choice of token is not a final decision; `%`
          could instead be `^` or some other token.</p>
        </emu-note>

        <ins class=block>
          <p>Declarative Environment Records have the additional state fields
          listed in <emu-xref href="#table-61"></emu-xref>.</p>

          <emu-table id=table-61
            caption="Additional Fields of Declarative Environment Records"
          >
            <table>
              <thead>
                <tr>
                  <th>Method</th>
                  <th>Value</th>
                  <th>Purpose</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>[[TopicValues]]</td>
                  <td>List of any</td>
                  <td>If the declarative Environment Record is a topic-binding
                  environment, then [[TopicValues]] is a List containing the one element
                  which is the environment's topic value (that is, the value of
                  the topic reference within its program scope).
                  Otherwise, the value of [[TopicValues]] is an empty List.</td>
                </tr>
              </tbody>
            </table>
          </emu-table>

          <emu-note type=editor>
            <p>[[TopicValues]] is a List in order to be forward compatible with
            future extensions that would use more than one topic value, e.g., <a
         href=https://github.com/js-choi/proposal-hack-pipes/blob/master/README.md#hack-pipe-functions
            >"pipe functions".</a></p>
          </emu-note>

          <p>Declarative Environment Records support all of the abstract methods
          of Environment Records listed in <emu-xref href="#table-15"></emu-xref>.
          In addition, declarative Environment Records support the methods listed
          in <emu-xref href="#table-62"></emu-xref>.</p>

          <emu-table id=table-62 caption="Additional Methods of Declarative Environment Records">
            <table>
              <thead>
                <tr>
                  <th>Method</th>
                  <th>Purpose</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>BindTopicValues(V)</td>
                  <td>Establish the immutable topic binding of this
                  Environment Record and set the topic binding's value.
                  _V_ is a List containing the one element which is the
                  topic value and is a value of any ECMAScript language
                  type. Afterward, the Environment Record is a
                  topic-binding environment, and the value returned by the
                  Environment Record's HasTopicBinding method is *true*.
                  This method cannot be called more than once on any
                  single Environment Record.</td>
                </tr>
              </tbody>
            </table>
          </emu-table>

          <emu-note type=editor>
          <p>BindTopicValues accepts a List argument rather than a single-value
          argument in order to be forward compatible with future extensions that
          would use more than one topic value, e.g., <a
          href=https://github.com/js-choi/proposal-hack-pipes/blob/master/README.md#hack-pipe-functions
          >"pipe functions"</a>.</p>
          </emu-note>
        </ins>

        <p>The behaviour of the concrete <ins>and additional</ins> specification
        methods for declarative Environment Records is defined by the following
        algorithms.</p>

        <emu-clause id=sec-declarative-environment-records-hastopicbinding>
          <h1><ins>HasTopicBinding ( )</ins></h1>
          <emu-note type=editor>
          <p>This section is a wholly new sub-clause of the <a
          href=https://tc39.github.io/ecma262/#sec-declarative-environment-records
          >original Declarative Environment Records clause</a>.</p>
          </emu-note>

          <p>The concrete Environment Record method HasTopicBinding for
          declarative Environment Records returns whether the Environment Record
          is a topic-binding environment. The value is *true* only if its
          BindTopicValues method has been called.</p>
          <emu-alg>
            1. Let _envRec_ be the declarative Environment Record for which the
               method was invoked.
            1. Assert: _envRec_.[[TopicValues]] is a List.
            1. If _envRec_.[[TopicValues]] is an empty List, then return *false*.
            1. Return *true*.
          </emu-alg>
        </emu-clause>

        <emu-clause id=sec-declarative-environment-records-bindtopicvalues>
          <h1><ins>BindTopicValues ( _V_ )</ins></h1>
          <emu-note type=editor>
            <p>This section is a wholly new sub-clause of the <a
            href=https://tc39.github.io/ecma262/#sec-declarative-environment-records
            >original Declarative Environment Records clause</a>.</p>
          </emu-note>
          <p>The method BindTopicValues for declarative Environment Records may
          only be called on an Environment Record when it is not yet a
          topic-binding environment, after which it does become a topic-binding
          environment.</p>
          <emu-alg>
            1. Assert: _V_ is a List.
            1. Let _envRec_ be the declarative Environment Record for which the
               method was invoked.
            1. Assert: _envRec_.HasTopicBinding() is *false*.
            1. Set _envRec_.[[TopicValues]] to _V_.
            1. Assert: _envRec_.HasTopicBinding() is *true*.
            1. Return NormalCompletion(~empty~).
          </emu-alg>
        </emu-clause>
      </emu-clause>

      <emu-clause id=sec-object-environment-records>
        <h1>Object Environment Records</h1>

        <emu-clause id=sec-object-environment-records-hastopicbinding>
          <h1><ins>HasTopicBinding ( )</ins></h1>

          <emu-note type=editor>
          <p>This section is a wholly new sub-clause of the <a
          href=https://tc39.github.io/ecma262/#sec-object-environment-records>
          original Object Environment Records clause</a>.</p>
          </emu-note>

          <p>An Object Environment Record may never have a topic binding.</p>
          <emu-alg>
            1. Return *false*.
          </emu-alg>
          </emu-clause>
      </emu-clause>

      <emu-clause id=sec-global-environment-records>
          <h1>Global Environment Records</h1>

          <emu-clause id=sec-global-environment-records-hastopicbinding>
            <h1><ins>HasTopicBinding ( )</ins></h1>

            <emu-note type=editor>
              <p>This section is a wholly new sub-clause of the <a
              href=https://tc39.github.io/ecma262/#sec-object-environment-records>
              original Global Environment Records clause</a>.</p>
            </emu-note>

            <p>A Global Environment Record may never have a topic binding.</p>
            <emu-alg>
              1. Return *false*.
            </emu-alg>
          </emu-clause>
        </emu-clause>
    </emu-clause>

    <emu-clause id=sec-topic-bindings>
      <h1><ins>Topic Bindings</ins></h1>

      <emu-note type=editor>
        <p>This section is a wholly new sub-clause of the <a
        href=https://tc39.github.io/ecma262/#sec-environment-records>original
        Environment Records clause</a>.</p>

        <p>It presumptively uses `%` as the placeholder token for the
        topic reference. This choice of token is not a final decision; `%`
        could instead be `^` or some other token.</p>
      </emu-note>

      <p>The <dfn>topic binding</dfn> of a declarative Environment Record
      immutably binds the topic reference `%` to one value of any
      ECMAScript language type (called the <dfn>topic value</dfn> or simply
      the <dfn>topic</dfn>), within that declarative Environment Record, at
      the time of the Environment Record's instantiation. The topic of a
      declarative Environment Record conceptually serves as the value that its
      lexical context is "about".</p>

      <p>A <dfn>topic-binding environment</dfn> is a declarative Environment
      Record that establishes a topic binding.
      The <dfn>topic environment</dfn> of the running execution context is its
      LexicalEnvironment's innermost Environment Record that is also a
      topic-binding environment (or *null* if no such Environment Record
      exists), as defined by the abstract operator GetTopicEnvironment.</p>

      <emu-note>
        <p>An Environment Record is a topic-binding environment
        <strong>only</strong> when it is a declarative Environment Record that
        was created by a |PipeBody|.</p>
      </emu-note>
    </emu-clause>
  </emu-clause>

  <emu-clause id="sec-execution-contexts">
    <h1>Execution Contexts</h1>

    <emu-note type=editor>
      <p>This section augments the <a
      href=https://tc39.es/ecma262/#sec-execution-contexts>original
      Execution Contexts clause</a>.</p>
    </emu-note>

    <emu-clause id="sec-evaluatewithtopics" type="abstract operation">
      <h1>
        EvaluateWithTopics (
          _topicValues_: a List,
          _expression_: a Parse Node,
        )
      </h1>
      <dl class="header"></dl>
      <emu-alg>
        1. Assert: _topicValues_ is a List.
        1. Let _outerEnv_ be the running execution context's LexicalEnvironment.
        1. Let _topicEnv_ be NewDeclarativeEnvironment(_outerEnv_).
        1. Perform ! _topicEnv_.BindTopicValues(_topicValues_).
        1. Set the running execution context's LexicalEnvironment to
           _topicEnv_.
        1. Let _result_ be the result of evaluating _expression_.
        1. Set the running execution context's LexicalEnvironment to _outerEnv_.
        1. Return _result_.
      </emu-alg>
      <emu-note>
        <p>EvaluateWithTopics creates a new topic-binding environment and
        evaluates the given _expression_ with that as its topic environment.
        The previous Lexical Environment is restored afterward.</p>
      </emu-note>
    </emu-clause>

    <emu-clause id=sec-gettopicenvironment type="abstract operation">
      <h1>GetTopicEnvironment ( )</h1>
      <dl class="header"></dl>
      <emu-alg>
      1. Let _envRec_ be the running execution context's LexicalEnvironment.
      1. Repeat,
        1. Let _status_ be _envRec_.HasTopicBinding().
        1. If _status_ is *true*, return _envRec_.
        1. If _envRec_ is a global Environment Record, return *null*.
        1. Let _outer_ be the value of _envRec_.[[OuterEnv]].
        1. Set _envRec_ to _outer_.
      1. Return _envRec_.
      </emu-alg>
      <emu-note>
        <p>GetTopicEnvironment returns the running execution context's topic environment
        (i.e., its LexicalEnvironment's innermost topic-binding environment)
        or *null* if the running execution context has no topic binding.</p>
      </emu-note>
    </emu-clause>

    <emu-clause id=sec-getprimarytopicvalue type="abstract operation">
      <h1>GetPrimaryTopicValue ( )</h1>
      <dl class="header"></dl>
      <emu-alg>
      1. Let _topicEnv_ be GetTopicEnvironment().
      1. Assert: _topicEnv_ is a declarative Environment Record.
      1. Assert: _topicEnv_.HasTopicBinding() is *true*.
      1. Let _topicValues_ be _envRec_.[[TopicValues]].
      1. Assert: _topicValues_ has at least one element.
      1. Return _topicValues_[0].
      </emu-alg>
      <emu-note>
        <p>GetPrimaryTopicValue returns the topic value
        of the running execution context's topic environment.
        It may be called only when the running execution context's topic environment
        is not *null*.</p>
      </emu-note>
    </emu-clause>
  </emu-clause>
</emu-clause>

<emu-clause id=sec-ecmascript-language-lexical-grammar>
  <h1>ECMAScript Language: Lexical Grammar</h1>

  <emu-clause id=sec-punctuators>
    <h1>Punctuators</h1>

    <emu-note type=editor>
      <p>This section augments the <a
      href=https://tc39.github.io/ecma262/#sec-punctuators>original
      Punctuators clause</a>.</p>

      <p>It presumptively uses `%` as the placeholder token for the
      topic reference. This choice of token is not a final decision; `%`
      could instead be `^` or some other token.</p>
    </emu-note>

    <emu-grammar>
      OtherPunctuator :: one of
        `{` `(` `)` `[` `]`
        `.` `...` `;` `,`
        `&lt;` `&gt;` `&lt;=` `&gt;=`
        `==` `!=` `===` `!==`
        `+` `-` `*` `%` `**`
        `++` `--`
        `&lt;&lt;` `&gt;&gt;` `&gt;&gt;&gt;`
        `&amp;` `|` `%`
        `!` `~`
        `&amp;&amp;` `||` `??`
        `?` `:`
        <ins>`|&gt;`</ins>
        `=` `+=` `-=` `*=` <del>`%=`</del> `**=`
        `&lt;&lt;=` `&gt;&gt;=` `&gt;&gt;&gt;=` `&amp;=` `|=` `^=`
        `&amp;&amp;=` `||=` `??=`
        `=&gt;`

      DivPunctuator ::
        `/`
        `/=`
        <ins>`%=`</ins>
    </emu-grammar>
  </emu-clause>
</emu-clause>

<emu-clause id=sec-ecmascript-language-expressions>
  <h1>ECMAScript Language: Expressions</h1>

  <emu-clause id=sec-primary-expression>
    <h1>Primary Expression</h1>

    <emu-note type=editor>
      <p>This section augments the <a
      href=https://tc39.github.io/ecma262/#sec-primary-expression>original
      Primary Expression clause</a>.</p>

      <p>It presumptively uses `%` as the placeholder token for the
      topic reference. This choice of token is not a final decision; `%`
      could instead be `^` or some other token.</p>
    </emu-note>

    <h2>Syntax</h2>
    <emu-grammar type="definition">
      PrimaryExpression[Yield, Await] :
        `this`
        <ins>`%`</ins>
        IdentifierReference[?Yield, ?Await]
        Literal
        ArrayLiteral[?Yield, ?Await]
        ObjectLiteral[?Yield, ?Await]
        FunctionExpression
        ClassExpression[?Yield, ?Await]
        GeneratorExpression
        AsyncFunctionExpression
        AsyncGeneratorExpression
        RegularExpressionLiteral
        TemplateLiteral[?Yield, ?Await, ~Tagged]
        CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]
    </emu-grammar>

    <emu-clause id=sec-topic-references>
      <h1><ins>Topic Reference</ins></h1>

      <emu-note type=editor>
        <p>This section is a wholly new sub-clause to be inserted between the <a
        href=https://tc39.github.io/ecma262/#sec-this-keyword>original `this`
        Keyword clause</a> and the <a
        href=https://tc39.github.io/ecma262/#sec-identifier-reference>original
        Identifier Reference clause</a>.</p>

        <p>It presumptively uses `%` as the placeholder token for the
        topic reference. This choice of token is not a final decision; `%`
        could instead be `^` or some other token.</p>
      </emu-note>

      <emu-note>
        <p>The <dfn>topic reference</dfn>, which is the token `%`, is a
        nullary operator that evaluates to the current Environment Record's
        topic value. The topic reference acts as if it were a special variable,
        implicitly bound to the topic value, yet still lexically scoped. But
        `%` is not actually an |IdentifierName| and the topic reference is
        not a variable, and it cannot be bound by typical assignment.
        Instead, the topic reference is immutably bound to a value
        during the instantiation of any topic-binding environment by a |PipeBody|.</p>

        <p>The concept of the topic binding is further discussed in <emu-xref
        href="#sec-topic-bindings">Topic Bindings</emu-xref> and in <emu-xref
        href="#sec-declarative-environment-records">Declarative Environment
        Records</emu-xref>.</p>
      </emu-note>

      <emu-note>
        <p>An <dfn>unbound topic reference</dfn> is a topic reference that
        is not present within any topic-binding environment created by a |PipeBody|.
        All unbound topic references are invalid syntax.
        Several early error rules for |ScriptBody| and for
        |ModuleItemList|, as well as a step in CreateDynamicFunction,
        use ContainsOuterTopic to check for any unbound topic reference `%`.
        Any inner topic reference within a |PipeBody| is hidden from these rules,
        preventing them from triggering the rules during program
        compilation.</p>

        <p>This guarantees that every topic reference in a program must be
        present within a topic-binding environment
        created by a |PipeBody| within that program.</p>
      </emu-note>

      <emu-clause id=sec-topic-references-runtime-semantics-evaluation>
        <h1>Runtime Semantics: Evaluation</h1>
        <emu-note>
          <p>A topic reference may be evaluated only when
          the running execution context's topic environment is not *null*.
          This is syntactically enforced by early error rules
          for |ScriptBody| and for |ModuleItemList|,
          as well as a step in CreateDynamicFunction.
          These rules use ContainsOuterTopic to check for any unbound topic reference.</p>
        </emu-note>

        <emu-grammar>PrimaryExpression : `%`</emu-grammar>
        <emu-alg>
        1. Return GetPrimaryTopicValue().
        </emu-alg>
      </emu-clause>
    </emu-clause>
  </emu-clause>

  <emu-clause id=sec-pipe-operator>
    <h1><ins>Pipe Operator</ins></h1>

    <emu-note type=editor>
      <p>This section is a wholly new sub-clause to be inserted between the <a
      href=https://tc39.github.io/ecma262/#sec-conditional-operator>original
      Conditional Operator (`?` `:`) clause</a> and the <a
      href=https://tc39.github.io/ecma262/#sec-assignment-operators>original
      Assignment Operators clause</a>.</p>
    </emu-note>

    <h2>Syntax</h2>
    <emu-grammar type=definition>
      PipeExpression[In, Yield, Await] :
        ShortCircuitExpression[?In, ?Yield, ?Await] `|&gt;` PipeBody[?In, ?Yield, ?Await]

      PipeBody[In, Yield, Await] :
        AssignmentExpression[?In, ?Yield, ?Await]
    </emu-grammar>

    <emu-clause id=sec-pipe-operator-static-semantics-early-errors>
      <h1>Static Semantics: Early Errors</h1>
      <emu-note type=editor>
        <p>This section is a wholly new sub-clause.</p>

        <p>It presumptively uses `%` as the placeholder token for the
        topic reference. This choice of token is not a final decision; `%`
        could instead be `^` or some other token.</p>
      </emu-note>

      <emu-grammar>PipeBody : AssignmentExpression</emu-grammar>
      <emu-alg>
        1. It is a Syntax Error if |PipeBody| ContainsOuterTopic is *false*.
      </emu-alg>

      <emu-note>
        <p>A |PipeBody| must use its topic at least once.
        `value |&gt; foo + 1` is an early error,
        because ContainsOuterTopic of its |PipeBody| is *false*.
        This design is such because omission of any topic reference from a |PipeBody|
        is almost certainly an accidental programmer error.</p>
      </emu-note>

      <emu-grammar>PipeBody : AssignmentExpression</emu-grammar>
      <emu-alg>
        1. It is a Syntax Error if one of the following productions is covering |PipeBody|:
            * |ShortCircuitExpression| ? |AssignmentExpression| : |AssignmentExpression|
            * |YieldExpression|
            * |ArrowFunction|
            * |AsyncArrowFunction|
            * |LeftHandSideExpression| `=` |AssignmentExpression|
            * |LeftHandSideExpression| |AssignmentOperator| |AssignmentExpression|
            * |LeftHandSideExpression| `&&=` |AssignmentExpression|
            * |LeftHandSideExpression| `||=` |AssignmentExpression|
            * |LeftHandSideExpression| `??=` |AssignmentExpression|
      </emu-alg>

      <emu-note>
        <p>A |PipeBody| must not be an unparenthesized |AssignmentExpression|, such as |YieldExpression|, |ArrowFunction|, or |ConditionalExpression|—unless it is a |ShortCircuitExpression|.</p>
        <p>This is to prevent confusing expressions from being valid, such as:</p>
        <pre><code class="javascript">
          x |> yield % |> % + 1 // Syntax Error
        </code></pre>
        <p>This expression would otherwise be equivalent to:</p>
        <pre><code class="javascript">
          x |> (yield % |> % + 1)
        </code></pre>
        <p>Likewise, this expression:</p>
        <pre><code class="javascript">
          x |> y ? % : z |> % + 1 // Syntax Error
        </code></pre>
        <p>…would otherwise be equivalent to:</p>
        <pre><code class="javascript">
          x |> (y ? % : z |> % + 1)
        </code></pre>
        <p>These expressions are visually unclear and are therefore made invalid. The developer may make them valid with explicit parentheses:</p>
        <pre><code class="javascript">
          x |> (yield %) |> % + 1
          x |> (yield % |> % + 1)
          x |> (y ? % : z) |> % + 1
          x |> (y ? % : z |> % + 1)
        </code></pre>
      </emu-note>
    </emu-clause>

    <emu-clause id=sec-pipe-operator-runtime-semantics-evaluation>
    <h1>Runtime Semantics: Evaluation</h1>

    <emu-grammar>PipeExpression : ConditionalExpression `|&gt;` PipeBody</emu-grammar>
    <emu-alg>
      1. Let _inputRef_ be the result of evaluating _ConditionalExpression_.
      1. Let _inputValues_ be « ? GetValue(_inputRef_) ».
      1. Let _outputValue_ be ? EvaluateWithTopics(_inputValues_, |PipeBody|).
      1. Return _outputValue_.
    </emu-alg>
    </emu-clause>
  </emu-clause>
</emu-clause>

<emu-clause id="sec-ecmascript-language-scripts-and-modules">
  <h1>ECMAScript Language: Scripts and Modules</h1>

  <emu-clause id="sec-scripts">
    <h1>Scripts</h1>

    <emu-note type=editor>
      <p>This section augments the <a
      href=https://tc39.github.io/ecma262/#sec-scripts>original
      Scripts clause</a>.</p>

      <p>It presumptively uses `%` as the placeholder token for the
      topic reference. This choice of token is not a final decision; `%`
      could instead be `^` or some other token.</p>
    </emu-note>

    <emu-clause id="sec-scripts-static-semantics-early-errors">
      <h1>Static Semantics: Early Errors</h1>

      <emu-grammar>Script : ScriptBody</emu-grammar>
      <ul>
        <li>
          <ins>It is a Syntax Error if ContainsOuterTopic of |ScriptBody| is *true*.</ins>
        </li>
      </ul>
      <ins class="block">
      <emu-note>
        <p>An early error rule uses ContainsOuterTopic
        to check for any unbound topic reference.
        Any inner topic reference within a |PipeBody| is hidden from this rule,
        preventing them from triggering the rule during program
        compilation.</p>

        <p>This guarantees that every topic reference in a |Script| must be
        present within a topic-binding environment created
        by a |PipeBody| within that |Script|.</p>
      </emu-note>
      </ins>
    </emu-clause>
  </emu-clause>

  <emu-clause id="sec-modules">
    <h1>Modules</h1>

    <emu-clause id="sec-module-semantics">
      <h1>Module Semantics</h1>

      <emu-note type=editor>
        <p>This section augments the <a
        href=https://tc39.github.io/ecma262/#sec-module-semantics>original
        Module Semantics clause</a>.</p>

        <p>It presumptively uses `%` as the placeholder token for the
        topic reference. This choice of token is not a final decision; `%`
        could instead be `^` or some other token.</p>
      </emu-note>

      <emu-clause id="sec-module-semantics-static-semantics-early-errors">
        <h1>Static Semantics: Early Errors</h1>
        <emu-grammar>ModuleBody : ModuleItemList</emu-grammar>
        <ul>
          <li>
            <ins>It is a Syntax Error if ContainsOuterTopic of |ModuleItemList|
            is *true*.</ins>
          </li>
        </ul>

        <ins class="block">
        <emu-note>
          <p>An early error rule uses ContainsOuterTopic
          to check for any unbound topic reference.
          Any inner topic reference within a |PipeBody| is hidden from this rule,
          preventing them from triggering the rule during program
          compilation.</p>

          <p>This guarantees that every topic reference in a |ModuleBody| must be
          present within a topic-binding environment created
          by a |PipeBody| within that |ModuleBody|.</p>
        </emu-note>
        </ins>
      </emu-clause>
    </emu-clause>
  </emu-clause>
</emu-clause>

<emu-clause id="sec-fundamental-objects">
  <h1>Fundamental Objects</h1>

  <emu-clause id="sec-function-objects">
    <h1>Function Objects</h1>

    <emu-clause id="sec-function-constructor">
      <h1>The Function Constructor</h1>

      <emu-clause id="sec-function-p1-p2-pn-body">
        <h1>Function ( _p1_, _p2_, &hellip; , _pn_, _body_ )</h1>

        <emu-clause id="sec-createdynamicfunction" aoid="CreateDynamicFunction">
          <h1>CreateDynamicFunction ( _constructor_, _newTarget_, _kind_, _args_ )</h1>
          <emu-note type=editor>
            <p>This section augments the <a
            href=https://tc39.github.io/ecma262/#sec-createdynamicfunction>original
            CreateDynamicFunction clause</a>.</p>

            <p>It presumptively uses `%` as the placeholder token for the
            topic reference. This choice of token is not a final decision; `%`
            could instead be `^` or some other token.</p>
          </emu-note>

          <emu-alg>
            1. Assert: The execution context stack has at least two elements.
            1. Let _callerContext_ be the second to top element of the execution context stack.
            1. Let _callerRealm_ be _callerContext_'s Realm.
            1. Let _calleeRealm_ be the current Realm Record.
            1. Perform ? HostEnsureCanCompileStrings(_callerRealm_, _calleeRealm_).
            1. If _newTarget_ is *undefined*, set _newTarget_ to _constructor_.
            1. If _kind_ is ~normal~, then
              1. Let _exprSym_ be the grammar symbol |FunctionExpression|.
              1. Let _bodySym_ be the grammar symbol |FunctionBody[~Yield, ~Await]|.
              1. Let _parameterSym_ be the grammar symbol |FormalParameters[~Yield, ~Await]|.
              1. Let _fallbackProto_ be *"%Function.prototype%"*.
            1. Else if _kind_ is ~generator~, then
              1. Let _exprSym_ be the grammar symbol |GeneratorExpression|.
              1. Let _bodySym_ be the grammar symbol |GeneratorBody|.
              1. Let _parameterSym_ be the grammar symbol |FormalParameters[+Yield, ~Await]|.
              1. Let _fallbackProto_ be *"%GeneratorFunction.prototype%"*.
            1. Else if _kind_ is ~async~, then
              1. Let _exprSym_ be the grammar symbol |AsyncFunctionExpression|.
              1. Let _bodySym_ be the grammar symbol |AsyncFunctionBody|.
              1. Let _parameterSym_ be the grammar symbol |FormalParameters[~Yield, +Await]|.
              1. Let _fallbackProto_ be *"%AsyncFunction.prototype%"*.
            1. Else,
              1. Assert: _kind_ is ~asyncGenerator~.
              1. Let _exprSym_ be the grammar symbol |AsyncGeneratorExpression|.
              1. Let _bodySym_ be the grammar symbol |AsyncGeneratorBody|.
              1. Let _parameterSym_ be the grammar symbol |FormalParameters[+Yield, +Await]|.
              1. Let _fallbackProto_ be *"%AsyncGeneratorFunction.prototype%"*.
            1. Let _argCount_ be the number of elements in _args_.
            1. Let _P_ be the empty String.
            1. If _argCount_ = 0, let _bodyArg_ be the empty String.
            1. Else if _argCount_ = 1, let _bodyArg_ be _args_[0].
            1. Else,
              1. Assert: _argCount_ &gt; 1.
              1. Let _firstArg_ be _args_[0].
              1. Set _P_ to ? ToString(_firstArg_).
              1. Let _k_ be 1.
              1. Repeat, while _k_ &lt; _argCount_ - 1,
                1. Let _nextArg_ be _args_[_k_].
                1. Let _nextArgString_ be ? ToString(_nextArg_).
                1. Set _P_ to the string-concatenation of _P_, *","* (a comma), and _nextArgString_.
                1. Set _k_ to _k_ + 1.
              1. Let _bodyArg_ be _args_[_k_].
            1. Let _bodyString_ be the string-concatenation of 0x000A (LINE FEED), ? ToString(_bodyArg_), and 0x000A (LINE FEED).
            1. Let _prefix_ be the prefix associated with _kind_ in <emu-xref href="#sec-createdynamicfunction"></emu-xref>.
            1. Let _sourceString_ be the string-concatenation of _prefix_, *" anonymous("*, _P_, 0x000A (LINE FEED), *") {"*, _bodyString_, and *"}"*.
            1. Let _sourceText_ be ! StringToCodePoints(_sourceString_).
            1. Let _parameters_ be ParseText(! StringToCodePoints(_P_), _parameterSym_).
            1. If _parameters_ is a List of errors, throw a *SyntaxError* exception.
            1. Let _body_ be ParseText(! StringToCodePoints(_bodyString_), _bodySym_).
            1. If _body_ is a List of errors, throw a *SyntaxError* exception.
            1. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, `new Function("/*", "*/ ) {")` is not legal.
            1. NOTE: If this step is reached, _sourceText_ must match _exprSym_ (although the reverse implication does not hold). The purpose of the next two steps is to enforce any Early Error rules which apply to _exprSym_ directly.
            1. Let _expr_ be ParseText(_sourceText_, _exprSym_).
            1. If _expr_ is a List of errors, throw a *SyntaxError* exception.
            1. <ins>NOTE: The dynamic function must not contain an unbound topic reference `%`.)</ins>
            1. <ins>If ContainsOuterTopic of _expr_ is *true*, throw a *SyntaxError* exception.</ins>
            1. Let _proto_ be ? GetPrototypeFromConstructor(_newTarget_, _fallbackProto_).
            1. Let _realmF_ be the current Realm Record.
            1. Let _scope_ be _realmF_.[[GlobalEnv]].
            1. Let _privateScope_ be *null*.
            1. Let _F_ be ! OrdinaryFunctionCreate(_proto_, _sourceText_, _parameters_, _body_, ~non-lexical-this~, _scope_, _privateScope_).
            1. Perform SetFunctionName(_F_, *"anonymous"*).
            1. If _kind_ is ~generator~, then
              1. Let _prototype_ be ! OrdinaryObjectCreate(%GeneratorFunction.prototype.prototype%).
              1. Perform DefinePropertyOrThrow(_F_, *"prototype"*, PropertyDescriptor { [[Value]]: _prototype_, [[Writable]]: *true*, [[Enumerable]]: *false*, [[Configurable]]: *false* }).
            1. Else if _kind_ is ~asyncGenerator~, then
              1. Let _prototype_ be ! OrdinaryObjectCreate(%AsyncGeneratorFunction.prototype.prototype%).
              1. Perform DefinePropertyOrThrow(_F_, *"prototype"*, PropertyDescriptor { [[Value]]: _prototype_, [[Writable]]: *true*, [[Enumerable]]: *false*, [[Configurable]]: *false* }).
            1. Else if _kind_ is ~normal~, perform MakeConstructor(_F_).
            1. NOTE: Functions whose _kind_ is ~async~ are not constructible and do not have a [[Construct]] internal method or a *"prototype"* property.
            1. Return _F_.
          </emu-alg>
        </emu-clause>
      </emu-clause>
    </emu-clause>
  </emu-clause>
</emu-clause>
Download .txt
gitextract_wep3lz_j/

├── .editorconfig
├── .github/
│   ├── pull_request_template.md
│   └── workflows/
│       ├── floodgate.yml
│       ├── publish.yml
│       └── stale-lock.yml
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── HISTORY.md
├── LICENSE.md
├── README.md
├── package.json
└── spec.html
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (110K chars).
[
  {
    "path": ".editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\ncharset = utf-8\nindent_sty"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 1090,
    "preview": "Thank you for taking the time to contribute to this proposal!\nWe’re so happy you’re helping out.\n\n1. Please take a look "
  },
  {
    "path": ".github/workflows/floodgate.yml",
    "chars": 302,
    "preview": "name: Comment Floodgate\non: issue_comment\njobs:\n  floodgate:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: js-choi/g"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 605,
    "preview": "name: Build and Deploy to GitHub Pages\non:\n  push:\n    branches:\n    - main\n  workflow_dispatch:\njobs:\n  build-and-deplo"
  },
  {
    "path": ".github/workflows/stale-lock.yml",
    "chars": 301,
    "preview": "name: Lock Stale Threads\n\non:\n  schedule:\n  - cron: '0 0 * * *'\n\n  workflow_dispatch:\n\npermissions:\n  issues: write\n  pu"
  },
  {
    "path": ".gitignore",
    "chars": 57,
    "preview": ".DS_Store\nnode_modules\nout\ndist\nnpm-debug.log\ndeploy_key\n"
  },
  {
    "path": ".travis.yml",
    "chars": 261,
    "preview": "sudo: off\n\nlanguage: node_js\n\nnode_js:\n  - \"8\"\n\nscript:\n  - bash ./deploy.sh\n\nenv:\n  global:\n    - ENCRYPTION_LABEL: \"bc"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 782,
    "preview": "# Code of conduct\nThis repository is a TC39 project, and it therefore subscribes to\nits [code of conduct][CoC]. It is av"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3536,
    "preview": "# Contributing to the ES pipe operator’s proposal\nFirst off, thank you for taking the time to contribute! 🎉\n\nHere are so"
  },
  {
    "path": "HISTORY.md",
    "chars": 21045,
    "preview": "# Brief history of the JavaScript pipe operator\nThe pipe operator in JavaScript has a long and twisty history.\nUnderstan"
  },
  {
    "path": "LICENSE.md",
    "chars": 1507,
    "preview": "Copyright 2021 J. S. Choi, James DiGioia, Ron Buckton, Tab Atkins-Bittner\n\nRedistribution and use in source and binary f"
  },
  {
    "path": "README.md",
    "chars": 36525,
    "preview": "# Pipe Operator (`|>`) for JavaScript\n\n* **Stage**: 2\n* **Champions**: J. S. Choi, James DiGioia, Ron Buckton, Tab Atkin"
  },
  {
    "path": "package.json",
    "chars": 516,
    "preview": "{\n  \"private\": true,\n  \"name\": \"proposal-pipeline-operator\",\n  \"version\": \"1.0.0\",\n  \"description\": \"ECMAScript pipe-ope"
  },
  {
    "path": "spec.html",
    "chars": 39686,
    "preview": "<pre class=metadata>\ntitle: ES pipe operator (2021)\nstatus: proposal\nstage: 2\nlocation: https://github.com/tc39/proposal"
  }
]

About this extraction

This page contains the full source code of the tc39/proposal-pipeline-operator GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (103.9 KB), approximately 27.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!