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
This is the formal specification for a proposed Hack-style pipe operator `|>` in JavaScript. It modifies the original ECMAScript specification with several new or revised clauses. See the proposal's explainer for the proposal's background, motivation, and usage examples.
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.
This section augments the original IsFunctionDefinition clause.
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.
This section augments the original IsIdentifierRef clause.
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.
This section is a wholly new sub-clause of the original Contains clause.
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.
This guarantees that any topic reference in a program must be present within a topic-binding environment created by a |PipeBody| within that program.
Every grammar production alternative in this specification which is not listed below implicitly has the following default definition of ContainsOuterTopic:
This section augments the original AssignmentTargetType clause.
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.
This section augments the original Environment Records clause.
| Method | Purpose |
|---|---|
| Determine whether an Environment Record is a topic-binding environment. Return *true* if it establishes a topic binding and *false* if it does not. |
This section augments the original Declarative Environment Records clause.
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.
Declarative Environment Records have the additional state fields
listed in
| Method | Value | Purpose |
|---|---|---|
| [[TopicValues]] | List of any | 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. |
[[TopicValues]] is a List in order to be forward compatible with future extensions that would use more than one topic value, e.g., "pipe functions".
Declarative Environment Records support all of the abstract methods
of Environment Records listed in
| Method | Purpose |
|---|---|
| BindTopicValues(V) | 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. |
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., "pipe functions".
The behaviour of the concrete and additional specification methods for declarative Environment Records is defined by the following algorithms.
This section is a wholly new sub-clause of the original Declarative Environment Records clause.
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.
This section is a wholly new sub-clause of the original Declarative Environment Records clause.
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.
This section is a wholly new sub-clause of the original Object Environment Records clause.
An Object Environment Record may never have a topic binding.
This section is a wholly new sub-clause of the original Global Environment Records clause.
A Global Environment Record may never have a topic binding.
This section is a wholly new sub-clause of the original Environment Records clause.
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.
The topic binding of a declarative Environment Record immutably binds the topic reference `%` to one value of any ECMAScript language type (called the topic value or simply the topic), 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".
A topic-binding environment is a declarative Environment Record that establishes a topic binding. The topic environment 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.
An Environment Record is a topic-binding environment only when it is a declarative Environment Record that was created by a |PipeBody|.
This section augments the original Execution Contexts clause.
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.
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.
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*.
This section augments the original Punctuators clause.
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.
This section augments the original Primary Expression clause.
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.
This section is a wholly new sub-clause to be inserted between the original `this` Keyword clause and the original Identifier Reference clause.
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.
The topic reference, 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|.
The concept of the topic binding is further discussed in
An unbound topic reference 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.
This guarantees that every topic reference in a program must be present within a topic-binding environment created by a |PipeBody| within that program.
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.
This section is a wholly new sub-clause to be inserted between the original Conditional Operator (`?` `:`) clause and the original Assignment Operators clause.
This section is a wholly new sub-clause.
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.
A |PipeBody| must use its topic at least once. `value |> 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.
A |PipeBody| must not be an unparenthesized |AssignmentExpression|, such as |YieldExpression|, |ArrowFunction|, or |ConditionalExpression|—unless it is a |ShortCircuitExpression|.
This is to prevent confusing expressions from being valid, such as:
x |> yield % |> % + 1 // Syntax Error
This expression would otherwise be equivalent to:
x |> (yield % |> % + 1)
Likewise, this expression:
x |> y ? % : z |> % + 1 // Syntax Error
…would otherwise be equivalent to:
x |> (y ? % : z |> % + 1)
These expressions are visually unclear and are therefore made invalid. The developer may make them valid with explicit parentheses:
x |> (yield %) |> % + 1
x |> (yield % |> % + 1)
x |> (y ? % : z) |> % + 1
x |> (y ? % : z |> % + 1)
This section augments the original Scripts clause.
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.
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.
This guarantees that every topic reference in a |Script| must be present within a topic-binding environment created by a |PipeBody| within that |Script|.
This section augments the original Module Semantics clause.
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.
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.
This guarantees that every topic reference in a |ModuleBody| must be present within a topic-binding environment created by a |PipeBody| within that |ModuleBody|.
This section augments the original CreateDynamicFunction clause.
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.