-
Notifications
You must be signed in to change notification settings - Fork 108
Home
This is the wiki, which just about anyone can edit.
Please check the sidebar for subpages, such as the topic-token bikeshedding page and tooling 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 see HISTORY.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,
new
object construction, etc.) - Able to
await
in 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#-style explainer
Minimal-F#-style explainer
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
await
oryield
expressions, which are scoped to their innermost function.
F#-style-with-await
explainer
F#-style-with-await
specification
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 with special extra syntax for await
. Proposal 0 is similar except it forbids await
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
await
expressions with special extra syntax (|> await
) - Cannot handle
yield
expressions, which are scoped to their innermost function.
// Basic Usage
x |> f //--> f(x)
x |> f(y) //--> f(y)(x)
// 2+ Arity Usage
x |> (a => f(a,10)) //--> f(x,10)
// Async Solution
x |> f |> await //--> await f(x)
x |> f |> await |> g //--> g(await f(x))
// Other Expressions
f(x) |> (a => a.data) //--> f(x).data
f(x) |> (a => a[a.length-1]) //--> let temp=f(x), temp[temp.length-1]
f(x) |> (a => ({ result: a })) //--> { result: f(x) }
// Complex example
anArray => (anArray
|> a => pickEveryN(a, 2)
|> a => a.filter(...)
|> makeQuery
|> a => readDB(a, config)
|> await
|> extractRemoteUrl
|> fetch
|> await
|> parse
|> console.log
);
Hack-style explainer
Hack-style specification
The Hack style |>
evaluates the left-hand side and assigns it to a temporary binding scoped to the right-hand side. First proposed in issue #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#
(see issue #91) - No tacit unary function calls, need to add
(^)
to their ends - Handles
await
andyield
expressions 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 Usage
x |> f(^) //--> f(x)
x |> f(y)(^) //--> f(y)(x)
x |> f //--> Syntax Error
// 2+ Arity Usage
x |> f(^,10) //--> f(x,10)
// Async Solution (Note this would not require special casing)
x |> await f(^) //--> await f(x)
x |> await f(^) |> g(^) //--> g(await f(x))
// Other Expressions
f(x) |> ^.data //--> f(x).data
f(x) |> ^[^.length-1] //--> let temp=f(x), temp[temp.length-1]
f(x) |> { result: ^ } //--> { result: f(x) }
// Complex example
anArray => anArray
|> pickEveryN(^, 2)
|> ^.filter(...)
|> makeQuery(^)
|> await readDB(^, config)
|> extractRemoteUrl(^)
|> await fetch(^)
|> 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 in issue #89; previously discussed in issue #75 and issue #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#
(see issue #91)- Handles
await
andyield
expressions without special extra syntax
- Handles
- One operator for tacit unary function calls (F# style), e.g.,
|>>
or|>
- Unary function calls are very terse
// Basic Usage
x |>> f //--> f(x)
x |> f(^) //--> f(x)
x |>> f(y) //--> f(y)(x)
x |> f(y)(^) //--> Syntax Error
// 2+ Arity Usage
x |> f(^, 10) //--> f(x,10)
// Async solution (does not require special casing)
x |> await f(^) //--> await f(x)
x |> await f(^) |> g //--> g(await f(x))
// Other expressions
f(x) |> ^.data //--> f(x).data
f(x) |> ^[^.length-1] //--> let temp=f(x), temp[temp.length-1]
f(x) |> { result: ^ } //--> { result: f(x) }
// Complex example
anArray => anArray
|> pickEveryN(^, 2)
|> ^.filter(...)
|>> makeQuery
|> await readDB(^, config)
|>> extractRemoteUrl
|>> await fetch(^)
|>> parse
|>> console.log;
Smart-mix explainer
Smart-mix specification
- This proposal was withdrawn 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#
(see issue #91) - Handles
await
andyield
expressions 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 Usage
x |> f //--> f(x)
x |> f(^) //--> f(x)
// 2+ Arity Usage
x |> f(y) //--> Syntax Error
x |> 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 |> await f(^) //--> await f(x)
x |> await f(^) |> g //--> g(await f(x))
// Arbitrary Expressions
f(x) |> ^.data //--> f(x).data
f(x) |> ^[^.length-1] //--> let temp=f(x), temp[temp.length-1]
f(x) |> { result: ^ } //--> { result: f(x) }
// Complex Example
anArray => anArray
|> pickEveryN(^, 2)
|> ^.filter($ => $ > 0)
|> makeQuery
|> await readDB(^, config)
|> extractRemoteUrl
|> await fetch(^)
|> parse
|> new User.Result(^)
|> console.log;