-
Notifications
You must be signed in to change notification settings - Fork 108
Home
The threads are becoming unwieldy, so here's the current status of the pipeline proposals.
- 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
- 2018-03-20 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(?) |
x + 1 |
x |> (y=>y + 1) |
x |> (y=>y + 1) |
x |> ? + 1 |
[0, x] |
x |> (y=>[0, y]) |
x |> (y=>[0, y]) |
x |> [0, x] |
{ 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. Proposal 0 is specifically for F# style only.
- 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 a special 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 “binding”, “placeholder”, and “parameterized” style. Proposal 2 is specifically for Hack style only, modulo some possible minor enhancements; Proposals 3 and 4 also have 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#
(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#
(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;