- Notifications
You must be signed in to change notification settings - Fork108
This is the wiki, which just about anyone can edit.
Please check the sidebar for subpages, such as thetopic-token bikeshedding page andtooling status page.
Other useful documents you may want to read include:
Overview of previous proposals
The following is an archive of a previous wiki home page that summarized our public discussions.
For more detailed information on the history of the pipe proposal, please seeHISTORY.md.
- Proposal 0: Minimal F# style (explainer #0,specification #0)
- Proposal 1: F# style with
await(explainer #1,spec #1) - Proposal 2: Hack style (explainer #2,spec #2)
Proposal 3: Split mixProposal 4: Smart mix (explainer #4,specification #4)
- 2021-03 Tab Atkins’ thoughts about why pipe operator is important and about F# style vs. Hack style vs. smart mix
- 2021-03 TC39 presentation
- 2018-03 TC39 presentation
- Easy composition of functions for immediate invocation
- Support for any function arity, not just unary functions
- Easy composition of other expressions (method calls, math, array/object literals,
newobject construction, etc.) - Able to
awaitin the middle of a pipeline - Method calls without binding
- Avoiding accidental footguns that would result in runtime errors
| Original expression | Proposal 0 (minimal F#) | Proposal 1 (F# pipes +await) | Proposal 2 (Hack pipes) |
|---|---|---|---|
o.m(x) | x |> o.m | x |> o.m | x |> o.m(^) |
o.m(0, x) | x |> y=>o.m(0, y) | x |> y=>o.m(0, y) | x |> o.m(0, ^) |
new o.m(x) | x |> y=>new o.m(y) | x |> y=>new o.m(y) | x |> new o.m(^) |
o[x] | x |> y=>o[y] | x |> y=>o[y] | x |> o[^] |
x[i] | x |> y=>y[i] | x |> y=>y[i] | x |> ^[i] |
x + 1 | x |> y=>y + 1 | x |> y=>y + 1 | x |> ^ + 1 |
[0, x] | x |> y=>[0, y] | x |> y=>[0, y] | x |> [0, ^] |
{ key: x } | x |> y=>({ key: y }) | x |> y=>({ key: y }) | x |> { key: ^ } |
await o.m(x) | Not supported | x |> o.m |> await | x |> await o.m(^) |
yield o.m(x) | Not supported | Not supported | x |> (yield o.m(^)) |
Minimal-F#-styleexplainer
Minimal-F#-styleexplainer
The F# (F-sharp / F♯) style|> invokes the right-hand side with the evaluated result of the left. It has also been called “implicit call”, “tacit” and “point-free” style.
- Unary function calls are very terse
- Anything else must be wrapped in an arrow function
- Arrow functions might or might not have to be wrapped in parentheses
- Cannot handle
awaitoryieldexpressions, which are scoped to their innermost function.
F#-style-with-awaitexplainer
F#-style-with-awaitspecification
The F# (F-sharp / F♯) style|> invokes the right-hand side with the evaluated result of the left. It has also been called “implicit call”, “tacit” and “point-free” style. Proposal 1 is specifically for F# style only along withspecial extra syntax forawait.Proposal 0 is similarexcept it forbidsawait in its RHS.
- Unary function calls are very terse
- Anything else must be wrapped in an arrow function
- Arrow functions might or might not have to be wrapped in parentheses
- Handles
awaitexpressions with special extra syntax (|> await) - Cannot handle
yieldexpressions, which are scoped to their innermost function.
// Basic Usagex|>f//--> f(x)x|>f(y)//--> f(y)(x)// 2+ Arity Usagex|>(a=>f(a,10))//--> f(x,10)// Async Solutionx|>f|>await//--> await f(x)x|>f|>await|>g//--> g(await f(x))// Other Expressionsf(x)|>(a=>a.data)//--> f(x).dataf(x)|>(a=>a[a.length-1])//--> let temp=f(x), temp[temp.length-1]f(x)|>(a=>({result:a}))//--> { result: f(x) }// Complex exampleanArray=>(anArray|>a=>pickEveryN(a,2)|>a=>a.filter(...)|>makeQuery|>a=>readDB(a,config)|>await|>extractRemoteUrl|>fetch|>await|>parse|>console.log);
Hack-styleexplainer
Hack-stylespecification
The Hack style|> evaluates the left-hand side and assigns it to a temporary binding scoped to the right-hand side. First proposed inissue #84. It has also been called “topic”, “binding”, “placeholder”, and “parameterized” style.
- Pipe to any expression using an explicit placeholder token
^ - Placeholder token can go where any normal variable can go
- Placeholder token might be
?or^or@or#(seeissue #91) - No tacit unary function calls, need to add
(^)to their ends - Handles
awaitandyieldexpressions without special extra syntax - Forward compatible with both split mix (Proposal 3) and smart mix (Proposal 4)
Tab Atkins explains why he prefers Hack style.
// Basic Usagex|>f(^)//--> f(x)x|>f(y)(^)//--> f(y)(x)x|>f//--> Syntax Error// 2+ Arity Usagex|>f(^,10)//--> f(x,10)// Async Solution (Note this would not require special casing)x|>awaitf(^)//--> await f(x)x|>awaitf(^)|>g(^)//--> g(await f(x))// Other Expressionsf(x)|>^.data//--> f(x).dataf(x)|>^[^.length-1]//--> let temp=f(x), temp[temp.length-1]f(x)|>{result:^}//--> { result: f(x) }// Complex exampleanArray=>anArray|>pickEveryN(^,2)|>^.filter(...)|>makeQuery(^)|>awaitreadDB(^,config)|>extractRemoteUrl(^)|>awaitfetch(^)|>parse(^)|>console.log(^);
Since both F# and Hack style proposals have desirable properties, it’s worth considering proposals that mix them together in a cohesive manner. Discussed inissue #89; previously discussed inissue #75 andissue #84.
- Requires browser vendors to agree to implement two similar pipe operators, so nobody is currently backing this proposal
- One operator for expressions with placeholder tokens (Hack style), e.g.,
|>or|:- Pipe to any expression using an explicit placeholder token
^ - Placeholder token can go where any normal variable can go
- Pipe to any expression using an explicit placeholder token
- Placeholder token might be
?or^or@or#(seeissue #91)- Handles
awaitandyieldexpressions without special extra syntax
- Handles
- One operator for tacit unary function calls (F# style), e.g.,
|>>or|>- Unary function calls are very terse
// Basic Usagex|>>f//--> f(x)x|>f(^)//--> f(x)x|>>f(y)//--> f(y)(x)x|>f(y)(^)//--> Syntax Error// 2+ Arity Usagex|>f(^,10)//--> f(x,10)// Async solution (does not require special casing)x|>awaitf(^)//--> await f(x)x|>awaitf(^)|>g//--> g(await f(x))// Other expressionsf(x)|>^.data//--> f(x).dataf(x)|>^[^.length-1]//--> let temp=f(x), temp[temp.length-1]f(x)|>{result:^}//--> { result: f(x) }// Complex exampleanArray=>anArray|>pickEveryN(^,2)|>^.filter(...)|>>makeQuery|>awaitreadDB(^,config)|>>extractRemoteUrl|>>awaitfetch(^)|>>parse|>>console.log;
Smart-mixexplainer
Smart-mixspecification
- This proposal waswithdrawn in favor of Hack style, which is forward compatible with this proposal
- Combines features of the two main proposals into a single operator
- Topic/Hack style:
- Pipe to any expression using an explicit placeholder token
^ - Placeholder token can go where any normal variable can go
- Placeholder token might be
^or?or%or@or#(seeissue #91) - Handles
awaitandyieldexpressions without special extra syntax
- Pipe to any expression using an explicit placeholder token
- Tacit/bare/F# style:
- Unary function calls are very terse if a specific syntax
- The expression must be a “simple” reference: one or more identifiers separated by
., likeconsole.log - The cannot contain parentheses, brackets, braces, the placeholder token, or other operators
- Syntax error when body of pipeline does not match either style
- Ensures easy distinguishability between topic mode and F# mode
x |> f(a, b)is invalid and must be disambiguated intox |> f(^, a, b),x |> f(a, ^, b),x |> f(a, b, ^), orx |> f(a, b)(^)
- If body of pipeline is currently written in F# style, and you realize you want to do something else to it, you must shift it to Hack style;
it’s a compile-time syntax error if you forget
// Basic Usagex|>f//--> f(x)x|>f(^)//--> f(x)// 2+ Arity Usagex|>f(y)//--> Syntax Errorx|>f(y,^)//--> f(y, x)x|>f(^,y)//--> f(x, y)x|>f(y)(^)//--> f(y)(x)// Async Solution (Note this would not require special casing)x|>awaitf(^)//--> await f(x)x|>awaitf(^)|>g//--> g(await f(x))// Arbitrary Expressionsf(x)|>^.data//--> f(x).dataf(x)|>^[^.length-1]//--> let temp=f(x), temp[temp.length-1]f(x)|>{result:^}//--> { result: f(x) }// Complex ExampleanArray=>anArray|>pickEveryN(^,2)|>^.filter($=>$>0)|>makeQuery|>awaitreadDB(^,config)|>extractRemoteUrl|>awaitfetch(^)|>parse|>newUser.Result(^)|>console.log;