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.
People are debating about a proposed bind operator ::
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.
They also explore other syntaxes such as “placeholder” pipes (i.e., Hack pipes).
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.
On 2017-09-26, @littledan makes a first presentation for F#-or-Elixir pipes to TC39, successfully advancing to Stage 1.
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. However, parts of TC39 push back against its syntax.
@littledan and collaborators attempt to make a stopgap with |> await
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, @littledan makes
another presentation for F# pipes and does not succeed in advancing to Stage 2.
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, 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”. @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 and @js-choi writes a spec for smart-mix pipes.
On 2018-03-22, @littledan presents F# pipes again—alongside smart-mix pipes—in an update presentation, trying to gain more consensus within TC39 for Stage 2. 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. 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. 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.
The State of JS 2020 is published, and it reports that one of the language’s top requested features 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.
@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, 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.
@tabatkins (Google) schedules a TC39 incubator meeting devoted to pipes (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). @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.)
@syg (Google V8) voices concerns again about engine performance of partial function application and F# pipes (see § 2018–2020).
@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.
@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 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, 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. There are several responses from other representatives:
@ljharb voices concern that advancing pipe would kill any future bind operator (see § 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), 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’s comments (see § 2021-03) and in the pipe proposal’s issues.
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, @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.
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 for bikeshedding the pipe operator’s topic token.
On 2021-10-25, PFA syntax is presented again to the Committee plenary 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 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.
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
… |> …
(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
…::…
,…::…:…
, andconst ::{ … } = …;
- Partial function application
…~(…)
(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.
- In late December, @js-choi wrote an article detailing how these proposals overlap.
- @tabatkins then wrote a response article on their own blog.
- Later, @hax would also write another response article. (@hax is a TC39 champion of the Extensions syntaxes.)
On January 26, 2022, a plenary meeting was held to discuss these overlapping proposals holistically. This discussion overflowed into an ad-hoc meeting on the next day.
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.
At the 2022-03 plenary, several candidate tokens for the topic reference were presented. The Committee mildly preferred @
as the topic reference. (However, a delegate subsequently raised serious concerns about @
, and thus @
was excluded again as a topic reference.)
Additionally, an update about call-this was also presented at the plenary. The call-this proposal continues to polarize the Committee due to ecosystem-schism concerns.
In the plenary on July 21, proposal-function-pipe-flow was formally presented to the Committee, and it was rejected for Stage 1. 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, which might or might not occur before the September plenary.