• This repository has been archived on 26/Jan/2022
  • Stars
    star
    181
  • Rank 212,110 (Top 5 %)
  • Language
    HTML
  • License
    MIT License
  • Created almost 7 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Old archived draft proposal for smart pipelines. Go to the new Hack-pipes proposal at js-choi/proposal-hack-pipes.

This proposal has been archived in favor of a simpler Hack-pipes proposal, which is a subset of this proposal.

Smart pipelines

ECMAScript Stage-0 Proposal. Living Document. J. S. Choi, 2018-12.

This proposal introduces a new binary pipe operator |> to JavaScript. It's similar to the pipe operators of other languages: Clojure, Elixir and Erlang, Elm, F#, Hack, Julia, LiveScript, OCaml, Perl 6, R with magrittr, and Unix shells and PowerShell.

value
|> await #
|> doubleSay(#, ', ')
|> capitalize // This is a function call.
|> # + '!'
|> new User.Message(#)
|> await #
|> console.log; // This is a method call.

// (The # token isn't final; it might instead be @ or ? or %.)

The proposal is currently at Stage 0 of the TC39 process and is planned to be presented, along with a competing proposal, to TC39 by Daniel “littledan” Ehrenberg of Igalia. The Core Proposal is a variant of the first pipe-operator proposal also championed by Ehrenberg; this variant is listed as Proposal 4: Smart Mix in the pipe-proposal wiki. The variant resulted from previous discussions in the previous pipe-operator proposal, discussions which culminated in an invitation by Ehrenberg to try writing a specification draft.

An update to the existing pipeline Babel plugin is also being developed jointly between the author of this proposal and James DiGioia, the author of the competing proposal. The update will support both this proposal and the other proposal, configurable with a flag.

You can take part in discussions on the original proposal's GitHub issue tracker. When you file an issue, please note in it that you are talking specifically about “Proposal 4: Smart Mix”.

This specification currently uses # as a “topic reference”, but that choice is not set in stone. @, ?, %, or many other symbols could also be used. Bikeshedding discussions over what characters to use for the topic token have been occurring on GitHub at tc39/proposal-pipeline-operator issue #91.

This proposal makes many other trade-offs that are also not set in stone. The proposal can and will change in response to feedback once the Babel plugin is implemented. The Babel plugin is planned to be configurable, allowing hands-on experimentation with these trade-offs.

With smart pipelines Status quo

The infix “smart” pipe operator |> proposed here would provide a backwards- and forwards-compatible style of chaining nested expressions into a readable, left-to-right manner.

Using a zero-cost abstraction, nested data transformations become untangled into short steps.

Nested, deeply composed expressions occur often in real-world JavaScript. They occur whenever any single value must be processed by a series of data transformations, whether they be operations, functions, or constructors. Unfortunately, these deeply nested expressions often result in messy spaghetti code, due to their mixing of prefix, infix, and postfix expressions together. Writing such code requires many nested levels of indentation and parentheses. Reading such code requires checking both the left and right of each subexpression to understand its data flow.

promise
|> await #
|> # || throw new TypeError(
  `Invalid value from ${promise}`)
|> doubleSay(#, ', ')
|> capitalize
|> # + '!'
|> new User.Message(#)
|> await stream.write(#)
|> console.log;

With smart pipelines, code becomes terser and, literally, more straightforward. Prefix, infix, and postfix expressions would be less tangled together in threads of spaghetti. Instead, data values would be piped from left to right through a single linear thread of postfix expressions, with a single level of indentation and four fewer pairs of parentheses – essentially forming a reverse Polish notation.

The resulting code’s terseness and linearity may be both easier for the JavaScript developer to read and to edit. This uniform postfix notation preserves locality between related code; the reader may follow the flow of data more easily through this single linear thread of postfix operations. And the developer may more easily add or remove operations at the beginning, end, or middle of the thread, without changing the indentation of unrelated lines.

console.log(
  await stream.write(
    new User.Message(
      capitalize(
        doubledSay(
          await promise
            || throw new TypeError(
              `Invalid value from ${promise}`)
        ), ', '
      ) + '!'
    )
  )
);

Compared with the pipeline version, this non-pipeline version requires additional indentation and grouping on each step. This requires four more levels of indentation and four more pairs of parentheses.

In addition, much related code is here separated by unrelated code. Rather than a uniform postfix chain, operations appear either before the previous step’s expression (await stream.write(…),new User.Message(…), capitalize(…), doubledSay(…), await …) but also after (… || throw new TypeError(), … + '!'). An additional argument to function calls (such as , in doubledSay(…, ', ')) is also separated from its function calls, forming another easy-to-miss “postfix” argument.

Each step of a pipeline creates its own lexical scope, within which the # token (the topic reference) is immutably to the result of the previous step (the topic). In this way, it acts as a placeholder for each step’s input.

input // step 0
|> # + 1 // step 1
|> f(x, #, y) // step 2
|> await g(#, z) // step 3
|> console.log(`${#}!`); // step 4

The use of # as the topic reference is not set in stone. @, ?, %, or many other symbols could also be used. Bikeshedding discussions over what characters to use for the topic token have been occurring on GitHub at tc39/proposal-pipeline-operator issue #91.

console.log(`${ // step 4
  await g( // step 3
    f(x, // step 2
      input + 1, // step 1
      y), // step 2
    z) // step 3
}!`); // step 4
input |> (# = 50);
// 🚫 Reference Error:
// Cannot assign to topic reference.

The topic binding is immutable, established only once per pipeline step. It is an error to attempt to assign a value to it using =, whether inside or outside a pipeline step.

This chained pipeline:

input
|> # - 3
|> -#
|> # * 2
|> Math.max(#, 0)
|> console.log;

…is equivalent to the tangled nested expression:

console.log(
  Math.max(
    -(input - 3) * 2,
    0
  )
);

The syntax is statically term rewritable into already valid code in this way, with theoretically zero runtime cost.

Similar use cases appear numerous times in real-world JavaScript code, whenever any input is transformed by expressions of any type: function calls, property calls, method calls, object constructions, arithmetic operations, logical operations, bitwise operations, typeof, instanceof, await, yield and yield *, and throw expressions.

function doubleSay (str, separator) {
  return `${str}${separator}${string}`;
}

function capitalize (str) {
  return str[0].toUpperCase()
    + str.substring(1);
}

promise
|> await #
|> # || throw new TypeError()
|> doubleSay(#, ', ')
|> capitalize
|> # + '!'
|> new User.Message(#)
|> await stream.write(#)
|> console.log;

This pipeline is also relatively linear, with only one level of indentation, and with each transformation step on its own line.

… |> capitalize uses a special shortcut called the bare style, explained further below. It is a bare unary function call that is, in this case, equivalent to … |> capitalize(#).

function doubleSay (str, separator) {
  return `${str}${separator}${str}`;
}

function capitalize (str) {
  return str[0].toUpperCase()
    + str.substring(1);
}

console.log(
  await stream.write(
    new User.Message(
      capitalize(
        doubledSay(
          await promise
            || throw new TypeError(
              `Invalid value from ${promise}`)
        ), ', '
      ) + '!'
    )
  )
);

This deeply nested expression has four levels of indentation instead of two. Reading its data flow requires checking both the beginning of each expression (new User.Message, capitalizedString, doubledSay, await promise and end of each expression (|| throw new TypeError(), , ', ', + '!')).

x =  |> f(#, #);
x =  |> [#, # * 2, # * 3];

The topic reference may be used multiple times in a pipeline step. Each use refers to the same value (wherever the topic reference is not overridden by another, inner pipeline’s topic scope). Because it is bound to the result of the topic, the topic is still only ever evaluated once.

{
  const $ = ;
  x = f($, $);
}
{
  const $ = ;
  x = [$, $ * 2, $ * 3];
}

This is equivalent to assigning the topic value to a unique variable, then using that variable multiple times in an expression.

promise
|> await #
|> # || throw new TypeError()
|> `${#}, ${#}`
|> #[0].toUpperCase() + #.substring(1)
|> # + '!'
|> new User.Message(#)
|> stream.write
|> console.log;

When tiny functions are only used once, and when their bodies would be obvious and self-documenting in meaning, then they might be ritual boilerplate that a developer may prefer to inline: trading off self-documentation for localization of code.

{
  const promiseValue = await promise
    || throw new TypeError();
  const doubledValue =
    `${promiseValue}, ${promiseValue}`;
  const capitalizedValue
    = doubledValue[0].toUpperCase()
      + doubledValue.substring(1);
  const exclaimedValue
    = capitalizedValue + '!';
  const userMessage =
    new User.Message(exclaimedValue);
  const writeValue =
    stream.write(userMessage);
  console.log(writeValue);
}

Using a sequence of variables instead has both advantages and disadvantages. The variable names may be self-documenting. But they also are verbose. They visually distract from the crucial data transformations (overemphasizing the expressions’ nouns over their verbs), and it is easy to typo their names.

promise
|> await #
|> # || throw new TypeError()
|> normalize
|> `${#}, ${#}`
|> #[0].toUpperCase() + #.substring(1)
|> # + '!'
|> new User.Message(#)
|> await stream.write(#)
|> console.log;

With a pipeline, there are no unnecessary variable identifiers. Inserting a new step in between two steps (or deleting a step) only touches one new line. Here, a call of a function normalize was inserted between the second and third steps.

{
  const promiseValue = await promise
    || throw new TypeError();
  const normalizedValue = normalize();
  const doubledValue =
    `${normalizedValue}, ${normalizedValue}`;
  const capitalizedValue =
    doubledValue[0].toUpperCase()
      + doubledValue.substring(1);
  const exclaimedValue =
    capitalizedValue + '!';
  const userMessage =
    new User.Message(exclaimedValue);
  const writeValue =
    stream.write(userMessage);
  console.log(writeValue);
}

This code underwent a similar insertion of normalize. With a series of variables, inserting a new step in between two other steps (or deleting a step) requires editing the variable names in the following step.

input |> f |> [0, 1, 2, ...#] |> g;

A pipeline step may contain array literals.

g([0, 1, 2, ...f(input)]);

A topic-style pipeline step may also contain object literals. However, pipeline steps that are entirely object literals must be parenthesized. It is similar to how arrow functions distinguish between object literals and blocks.

input |> f |> ({ x: #, y: # }) |> g;

This is for forward compatibility with Additional Feature BP, which introduces block pipeline steps. (It is expected that block pipelines would eventually be much more common than pipelines with object literals.) This restriction could be dropped, although that would make Additional Feature BP impossible forever.

input |> f |> { x: #, y: # } |> g;
// 🚫 Syntax Error:
// Unexpected token `{`.
// Cannot parse base expression.
{
  const $ = f(input);
  g({ x, $: y: f($) });
}
f = input
|> f
|> (x => # + x);

A topic-style pipeline step may contain an inner arrow function. Both versions of this example result in an arrow function in a closure on the previous pipeline’s result input |> f.

{
  const $ = f(input);
  x => $ + x;
}

The arrow function lexically closes over the topic value, takes one parameter, and returns the sum of the topic value and the parameter.

input
|> f
|> settimeout(() => # * 5)
|> processIntervalID;

This ability to create arrow functions, which do not lexically shadow the topic, can be useful for using callbacks in a pipeline.

{
  const $ = f(input);
  const intervalID = settimeout(() => $ * 5);
  processIntervalID(intervalID);
}

The topic value of the second pipeline step (here represented by a normal variable $) is still lexically accessible within its body, an arrow function, in both examples.

input
|> f
|> (() => # * 5)
|> settimeout
|> processIntervalID;

The arrow function can also be created on a separate pipeline step.

{
  const $ = f(input);
  const callback = () => $ * 5;
  const intervalID = settimeout(callback);
  processIntervalID(intervalID);
}

The result here is the same.

input
|> f
|> () => # * 5
|> settimeout
|> processIntervalID;
// 🚫 Syntax Error:
// Unexpected token `=>`.
// Cannot parse base expression.

Note, however, that arrow functions have looser precedence than the pipe operator. This means that if a pipeline creates an arrow function alone in one of its steps, then the arrow-function expression must be parenthesized. (The same applies to assignment and yield operators, which are also looser than the pipe operator.) The example above is being parsed as if it were:

(input |> f |> ()) =>
  (# * 5 |> settimeout |> processIntervalID);
// 🚫 Syntax Error:
// Unexpected token `=>`.
// Cannot parse base expression.

The arrow function must be parenthesized, as with any other looser-precedence expression:

input
|> (f, g)
|> (() => # * 5)
|> settimeout
|> processIntervalID;

There is a shortcut for the very common case of unary function calls on variables or variables’ methods. In these cases, the topic reference can be left out. (This is the bare style of the pipe operator.)

promise
|> await #
|> # || throw new TypeError()
|> doubleSay(#, ', ')
|> capitalize
|> # + '!'
|> new User.Message(#)
|> await stream.write(#)
|> console.log;

In this example, it is not necessary to include the parenthesized argument (#) for capitalize and console.log. They were tacitly implied, forming tacit unary function calls. In other words, the example above is equivalent to the version in the next row.

console.log(
  await stream.write(
    new User.Message(
      capitalize(
        doubledSay(
          await promise
            || throw new TypeError(
              `Invalid value from ${promise}`)
        ), ', '
      ) + '!'
    )
  )
);
promise
|> await #
|> # || throw new TypeError(
    `Invalid value from ${#}`)
|> doubleSay(#, ', ')
|> capitalize(#)
|> # + '!'
|> new User.Message(#)
|> await stream.write(#)
|> console.log(#);

This version is equivalent to the version above, except that the |> capitalize(#) and |> console.log(#) pipeline steps explicitly include optional topic references #, making the expressions slightly wordier than necessary. (Any pipeline step that isn’t in bare style is said to be in topic style, because it uses the topic reference somewhere in its expression.)

console.log(
  await stream.write(
    new User.Message(
      capitalize(
        doubledSay(
          await promise
            || throw new TypeError(
              `Invalid value from ${promise}`)
        ), ', '
      ) + '!'
    )
  )
);

Being able to automatically detect this bare style is the smart part of the “smart pipe operator”. The styles of functional programming, dataflow programming, and tacit programming may particularly benefit from bare pipelines and their terse function application.

const object = input
|> f
|> # + 2
|> # * 3
|> -#
|> g(#, x)
|> o.unaryMethod
|> await asyncFunction(#)
|> await o.asyncMethod(#)
|> new Constructor(#);

This pipeline is a very linear expression, with only one level of indentation, and with each transformation step on its own line.

As with the previous examples, the … |> f is a bare unary function call, equivalent to … |> f(#). The topic reference # is unnecessary; it is invisibly, tacitly implied. The same goes for o.unaryMethod, which is a unary function call on o.unaryMethod.

This is the smart part of the smart pipe operator, which can distinguish between two syntax styles (bare style vs. topic style) by using a simple rule: bare style uses only identifiers and dots – and never parentheses, brackets, braces, or other operators. And topic style always contains at least one topic reference. For more information, see the reference below about the smart step syntax.

const object =
  new Constructor(
    await o.asyncMethod(
      await asyncFunction(
        o.unaryMethod(
          g(
            -(f(input) + 2)
              * 3,
            x
          )
        )
      )
    )
  );

In contrast to the version with pipes, this code without pipes is deeply nested, not linear.

The expression has two levels of indentation instead of one. Reading its data flow requires checking both the beginning and end of each expression, and each step expression gradually increases in size.

Inserting or removing any step of the data flow also requires changes to the indentation of any previous steps’ lines.

input |> x + 50 |> f |> g(x, 2);
// 🚫 Syntax Error:
// Topic-style pipeline step
// `|> x + 50`
// binds topic but contains
// no topic reference.
// 🚫 Syntax Error:
// Topic-style pipeline step
// `|> g(x, 2)`
// binds topic but contains
// no topic reference.

In order to fulfill the goal of “don’t shoot me in the foot”, when a pipeline step is in topic style but it contains no topic reference, that is currently an early error. Such a degenerate pipeline step has a very good chance of actually being an accidental bug. (The bare-style pipeline step |> f is not an error. The bare style is not supposed to contain any topic references #.)

For instance, this code may be clear enough:

input |> object.method;

It is a valid bare-style pipeline. Bare style is designed to be strictly simple: it must either be a simple reference or it is not in bare style.

It means:

object.method(input);

But this code would be less clear. That is why it is an early Syntax Error:

input |> object.method();
// 🚫 Syntax Error:
// Topic-style pipeline step
// `|> object.method()`
// binds topic but contains
// no topic reference.

It is an invalid topic-style pipeline. It is in topic style because it is not a simple reference; it has parentheses. And it is invalid because it is in topic style yet it does not have a topic reference.

Had that code not been an error, it could reasonably mean either of these lines:

object.method(input);
object.method()(input);

Instead, the developer must clarify what they mean, using a topic reference, into either of these two valid topic-style pipelines:

input |> object.method(#);
input |> object.method()(#);

The reading developer benefits from explicitness and clarity, without sacrificing the benefits of untangled flow that pipelines bring.

object.method(input);
object.method()(input);

Adding other arguments:

input |> object.method(x, y);
// 🚫 Syntax Error:
// Topic-style pipeline step
// `|> object.method(x, y)`
// binds topic but contains
// no topic reference.

…would make this problem of semantic ambiguity worse. But the reader is protected from this ambiguity by the same early error.

That code could have any of these reasonable interpretations:

object.method(input, x, y);
object.method(x, y, input);
object.method(x, y)(input);

Both inserting the input as the first argument and inserting it as the last argument are reasonable interpretations, as evidenced by how other programming languages’ pipe operators variously do either. Or it could be a factory method that creates a function that is in turn to be called with a unary input argument.

The writer must clarify which of these reasonable interpretations is correct:

input |> object.method(#, x, y);
input |> object.method(x, y, #);
input |> object.method(x, y)(#);
object.method(input, x, y);
object.method(x, y, input);
object.method(x, y)(input);

And this example’s ambiguity would be even worse:

input |> await object.method(x, y);
// 🚫 Syntax Error:
// Topic-style pipeline step
// `|> await object.method(x, y)`
// binds topic but contains
// no topic reference.

…were it not an invalid topic-style pipeline.

It could reasonably mean any of these lines:

await object.method(input, x, y);
await object.method(x, y, input);
await object.method(x, y)(input);
(await object.method(x, y))(input);

So the developer must clarify their intent using one of these lines:

input |> await object.method(#, x, y);
input |> await object.method(x, y, #);
input |> await object.method(x, y)(#);
input |> (await object.method(x, y))(#);
await object.method(input, x, y);
await object.method(x, y, input);
await object.method(x, y)(input);
(await object.method(x, y))(input);

Both the head and the steps of a pipeline may contain nested inner pipelines.

x = input
|> f(x =>
  # + x |> g |> # * 2)
|> #.toString();

However, just as with any other syntax, nested pipelines can quickly become complicated if not used judiciously; they are discouraged otherwise.

A nested pipeline works consistently. It merely shadows the outer context’s topic with the topic within its own steps’ inner contexts.

{
  const $ = input;
  x = f(x =>
    g($ + x) * 2
  ).toString();
}
x = input
|> # ** 2
|> f(x => #
  |> g(#, x)
  |> [# * 3, # * 5]);
{
  const $ = input ** 2;
  x = f(x => {
    const _$ = g($, x);
    return [_$ * 3, _$ * 5];
  });
}

Currently, four kinds of statements cannot use an outside context’s topic in their expressions. These are:

  • function definitions (including those for async functions, generators, and async generators; but not arrow functions, as explained above),
  • class definitions,
  • for and while statements,
  • catch clauses (but see Additional Feature TS), and
  • with statements.
x = input |> function () { return #; };
// 🚫 Syntax Error:
// Lexical context `function () { return #; }`
// contains a topic reference
// but has no topic binding.
// 🚫 Syntax Error:
// Pipeline step `|> function () { … }`
// binds topic but contains
// no topic reference.

This behavior is in order to fulfill the goals of simple scoping and of “don’t shoot me in the foot”: it prevents the origin of any topic from being difficult to find. It also fulfills the goal of forward compatibility with future additional features.

However, this behavior is subject to change, depending on feedback after the proposal is implemented in the Babel plugin.

x = input |> class { m: () { return #; } };
// 🚫 Syntax Error:
// Pipeline step `|> class { … }`
// binds topic but contains
// no topic reference.
x = input
|> await f(#, 5)
|> () => {
  if (#)
    return # + 30;
  else
    return #;
}
|> g;

Any other nested blocks may contain topic references from outer lexical environments. These include arrow functions, if statements, try statements and their finally clauses (though not their catch clauses), switch statements, and bare block statements.

x = g(await f(input, 5) + 30);

A function definition that is a pipeline step may contain topic references in its default parameters’ expressions, because their scoping is similar to that of the outside context’s: similar enough such that also allowing topic references in them would fulfill the goal of simple scoping. However, as stated above, the function body itself still may not contain topic references.

value
|> processing
|> function (x = #) { return x; }
function (x = processing(value)) {
  return x;
}

The same applies to the parenthesized antecedents of for and while loops.

input
|> process
|> (x, y) => {
  for (const element of #)
    
}
input
|> process
|> (x, y) => {
  let element;
  while (element = getNextFrom(#))
    
}
(x, y) => {
  for (const element
    of process(input))
    
}
(x, y) {
  let element;
  while (element =
    getNextFrom(input))
    
}

Real-world examples

See core-real-examples.md.

Additional features

This document is an explainer for the formal specification of a proposed smart pipe operator |> in JavaScript, along with several other additional features. The specification is divided into one Stage-0 Core Proposal plus six mutually independent-but-compatible Additional Features.

The Core Proposal is currently at Stage 0 of the TC39 process and is planned to be presented, along with a competing proposal, to TC39 by Daniel "littledan" Ehrenberg of Igalia.

There are also additional features are not part of the Stage-0 Core Proposal. They are included to illustrate possible separate follow-up proposals for the case in which the Core Proposal advances past Stage 1. Together, the Core Proposal and the additional features demonstrate a unified vision of a future in which composition, partial application, method extraction, and error handling are all tersely expressible with the same simple pipeline/topic concept.

Name Status Features Purpose
Core Proposal Stage 0 Infix pipelines … |> …
Lexical topic #
Unary function/expression application
Additional Feature BC None Bare constructor calls … |> new … Tacit application of constructors
Additional Feature BA None Bare awaited calls … |> await … Tacit application of async functions
Additional Feature BP None Block pipeline steps … |> {…} Application of statement blocks
Additional Feature PF None Pipeline functions +> Partial function/expression application
Function/expression composition
Method extraction
Additional Feature TS None Pipeline try statements Tacit application to caught errors
Additional Feature NP None N-ary pipelines (…, …) |> …
Lexical topics ##, ###, and ...
N-ary function/expression application

Legacy link anchors

This explainer used to be a single long document before it was split up into separate appendices. These sections are to point links to subsections of the older versions of the explainer—toward the new appendices.

Core Proposal real-world examples

See core-real-examples.md.

WHATWG Fetch Standard (Core Proposal only)

See WHATWG Fetch + CP.

jQuery (Core Proposal only)

See jQuery + CP.

Underscore.js (Core Proposal only)

See Underscore.js + CP.

Lodash (Core Proposal only)

See Lodash + CP.

Smart pipe style

See core syntax.

Bare style

See core syntax, bare style.

Topic style

See core syntax, topic style.

Additional Feature BC

See Additional Feature BC.

Additional Feature BA

See Additional Feature BA.

Additional Feature BP

See Additional Feature BP.

WHATWG Fetch Standard (Core Proposal + Additional Feature BP)

See Additional Feature BP.

jQuery (Core Proposal + Additional Feature BP)

See Additional Feature BP.

Lodash (Core Proposal + Additional Feature BP)

See Additional Feature BP.

Additional Feature TS

See Additional Feature TS.

Additional Feature PF

See Additional Feature PF.

Ramda (Core Proposal + Additional Feature BP+PF)

See Additional Feature PF.

WHATWG Streams Standard (Core Proposal + Additional Features BP+PP+PF)

See Additional Feature PF.

Additional Feature NP

See Additional Feature NP.

Lodash (Core Proposal + Additional Features BP+PP+PF+NP)

See Additional Feature NP.

Ramda (Core Proposal + Additional Features BP+PF+NP)

See Additional Feature NP.

WHATWG Streams Standard (Core Proposal + Additional Features BP+PP+PF+NP)

See Additional Feature NP.

Goals

See Goals.

“Don’t break my code.”

See “Don’t break my code”.

Backward compatibility

See Backward compatibility.

Zero runtime cost

See Zero runtime cost.

Forward compatibility

See Forward compatibility.

“Don’t shoot me in the foot.”

See “Don’t shoot me in the foot”.

Opt-in behavior

See Opt-in behavior.

Simple scoping

See Simple scoping.

Static analyzability

See Static analyzability.

“Don’t make me overthink.”

See “Don’t make me overthink”.

Syntactic locality

See Syntactic locality.

Semantic clarity

See Semantic clarity.

Expressive versatility

See Expressive versatility.

Cyclomatic simplicity

See Cyclomatic simplicity.

“Make my code easier to read.”

See “Make my code easier to read”.

Untangled flow

See Untangled flow.

Distinguishable punctuators

See Distinguishable punctuators.

Terse parentheses

See Terse parentheses.

Terse variables

See Terse variables.

Terse function calls

See Terse function calls.

Terse composition

See Terse composition.

Terse partial application

See Terse partial application.

Other Goals

See Other Goals.

Conceptual generality

See Conceptual generality.

Human writability

See Human writability.

Novice learnability

See Novice learnability.

Relations to other work

See Relations to other work.

Pipelines in other programming languages

See Relations to other work.

Topic references in other programming languages

See Topic references in other programming languages.

do expressions

See do expressions.

Function binding

See Function binding.

Function composition

See Function composition.

Partial function application

See Partial function application.

Optional catch binding

See Optional catch binding.

Pattern matching

See Pattern matching.

Block parameters

See Block parameters.

do expressions

See do expressions.

Explanation of nomenclature

See Nomenclature.

Term rewriting

See Term rewriting.

More Repositories

1

proposals

Tracking ECMAScript Proposals
17,177
star
2

ecma262

Status, process, and documents for ECMA-262
HTML
14,437
star
3

proposal-pipeline-operator

A proposal for adding a useful pipe operator to JavaScript.
HTML
7,534
star
4

proposal-pattern-matching

Pattern matching syntax for ECMAScript
HTML
5,498
star
5

proposal-optional-chaining

HTML
4,942
star
6

proposal-type-annotations

ECMAScript proposal for type syntax that is erased - Stage 1
JavaScript
4,252
star
7

proposal-signals

A proposal to add signals to JavaScript.
3,387
star
8

proposal-temporal

Provides standard objects and functions for working with dates and times.
HTML
3,321
star
9

proposal-observable

Observables for ECMAScript
JavaScript
3,058
star
10

proposal-decorators

Decorators for ES6 classes
2,640
star
11

proposal-record-tuple

ECMAScript proposal for the Record and Tuple value types. | Stage 2: it will change!
HTML
2,496
star
12

test262

Official ECMAScript Conformance Test Suite
JavaScript
2,073
star
13

proposal-dynamic-import

import() proposal for JavaScript
HTML
1,863
star
14

proposal-bind-operator

This-Binding Syntax for ECMAScript
1,742
star
15

proposal-class-fields

Orthogonally-informed combination of public and private fields proposals
HTML
1,722
star
16

proposal-async-await

Async/await for ECMAScript
HTML
1,578
star
17

proposal-object-rest-spread

Rest/Spread Properties for ECMAScript
HTML
1,493
star
18

proposal-shadowrealm

ECMAScript Proposal, specs, and reference implementation for Realms
HTML
1,429
star
19

proposal-iterator-helpers

Methods for working with iterators in ECMAScript
HTML
1,307
star
20

proposal-nullish-coalescing

Nullish coalescing proposal x ?? y
HTML
1,232
star
21

proposal-top-level-await

top-level `await` proposal for ECMAScript (stage 4)
HTML
1,083
star
22

proposal-partial-application

Proposal to add partial application to ECMAScript
HTML
1,002
star
23

proposal-do-expressions

Proposal for `do` expressions
HTML
990
star
24

proposal-binary-ast

Binary AST proposal for ECMAScript
961
star
25

agendas

TC39 meeting agendas
JavaScript
952
star
26

proposal-built-in-modules

HTML
891
star
27

proposal-async-iteration

Asynchronous iteration for JavaScript
HTML
857
star
28

proposal-explicit-resource-management

ECMAScript Explicit Resource Management
JavaScript
746
star
29

proposal-set-methods

Proposal for new Set methods in JS
HTML
655
star
30

proposal-string-dedent

TC39 Proposal to remove common leading indentation from multiline template strings
HTML
614
star
31

proposal-operator-overloading

JavaScript
610
star
32

proposal-import-attributes

Proposal for syntax to import ES modules with assertions
HTML
591
star
33

proposal-async-context

Async Context for JavaScript
HTML
587
star
34

proposal-bigint

Arbitrary precision integers in JavaScript
HTML
561
star
35

ecmascript_simd

SIMD numeric type for EcmaScript
JavaScript
540
star
36

ecma402

Status, process, and documents for ECMA 402
HTML
529
star
37

proposal-slice-notation

HTML
523
star
38

proposal-change-array-by-copy

Provides additional methods on Array.prototype and TypedArray.prototype to enable changes on the array by returning a new copy of it with the change.
HTML
511
star
39

notes

TC39 meeting notes
JavaScript
496
star
40

proposal-class-public-fields

Stage 2 proposal for public class fields in ECMAScript
HTML
489
star
41

proposal-iterator.range

A proposal for ECMAScript to add a built-in Iterator.range()
HTML
483
star
42

proposal-decimal

Built-in exact decimal numbers for JavaScript
HTML
477
star
43

proposal-uuid

UUID proposal for ECMAScript (Stage 1)
JavaScript
463
star
44

proposal-module-expressions

HTML
433
star
45

proposal-throw-expressions

Proposal for ECMAScript 'throw' expressions
JavaScript
425
star
46

proposal-UnambiguousJavaScriptGrammar

413
star
47

proposal-weakrefs

WeakRefs
HTML
409
star
48

proposal-array-grouping

A proposal to make grouping of array items easier
HTML
407
star
49

proposal-error-cause

TC39 proposal for accumulating errors
HTML
380
star
50

proposal-cancelable-promises

Former home of the now-withdrawn cancelable promises proposal for JavaScript
Shell
376
star
51

proposal-ecmascript-sharedmem

Shared memory and atomics for ECMAscript
HTML
374
star
52

proposal-module-declarations

JavaScript Module Declarations
HTML
369
star
53

proposal-first-class-protocols

a proposal to bring protocol-based interfaces to ECMAScript users
352
star
54

proposal-relative-indexing-method

A TC39 proposal to add an .at() method to all the basic indexable classes (Array, String, TypedArray)
HTML
351
star
55

proposal-global

ECMAScript Proposal, specs, and reference implementation for `global`
HTML
346
star
56

proposal-private-methods

Private methods and getter/setters for ES6 classes
HTML
345
star
57

proposal-numeric-separator

A proposal to add numeric literal separators in JavaScript.
HTML
330
star
58

proposal-private-fields

A Private Fields Proposal for ECMAScript
HTML
319
star
59

tc39.github.io

Get involved in specifying JavaScript
HTML
318
star
60

proposal-object-from-entries

TC39 proposal for Object.fromEntries
HTML
318
star
61

proposal-promise-allSettled

ECMAScript Proposal, specs, and reference implementation for Promise.allSettled
HTML
314
star
62

proposal-await.ops

Introduce await.all / await.race / await.allSettled / await.any to simplify the usage of Promises
HTML
310
star
63

proposal-regex-escaping

Proposal for investigating RegExp escaping for the ECMAScript standard
JavaScript
309
star
64

proposal-export-default-from

Proposal to add `export v from "mod";` to ECMAScript.
HTML
306
star
65

proposal-logical-assignment

A proposal to combine Logical Operators and Assignment Expressions
HTML
302
star
66

proposal-promise-finally

ECMAScript Proposal, specs, and reference implementation for Promise.prototype.finally
HTML
279
star
67

proposal-json-modules

Proposal to import JSON files as modules
HTML
272
star
68

proposal-asset-references

Proposal to ECMAScript to add first-class location references relative to a module
270
star
69

proposal-cancellation

Proposal for a Cancellation API for ECMAScript
HTML
267
star
70

proposal-promise-with-resolvers

HTML
255
star
71

proposal-string-replaceall

ECMAScript proposal: String.prototype.replaceAll
HTML
253
star
72

proposal-export-ns-from

Proposal to add `export * as ns from "mod";` to ECMAScript.
HTML
242
star
73

proposal-structs

JavaScript Structs: Fixed Layout Objects
230
star
74

proposal-ses

Draft proposal for SES (Secure EcmaScript)
HTML
223
star
75

proposal-intl-relative-time

`Intl.RelativeTimeFormat` specification [draft]
HTML
215
star
76

proposal-json-parse-with-source

Proposal for extending JSON.parse to expose input source text.
HTML
214
star
77

proposal-flatMap

proposal for flatten and flatMap on arrays
HTML
214
star
78

proposal-defer-import-eval

A proposal for introducing a way to defer evaluate of a module
HTML
208
star
79

ecmarkup

An HTML superset/Markdown subset source format for ECMAScript and related specifications
TypeScript
201
star
80

proposal-promise-any

ECMAScript proposal: Promise.any
HTML
200
star
81

proposal-optional-chaining-assignment

`a?.b = c` proposal
186
star
82

proposal-decorators-previous

Decorators for ECMAScript
HTML
184
star
83

proposal-array-from-async

Draft specification for a proposed Array.fromAsync method in JavaScript.
HTML
178
star
84

proposal-upsert

ECMAScript Proposal, specs, and reference implementation for Map.prototype.upsert
HTML
176
star
85

proposal-collection-methods

HTML
171
star
86

proposal-array-filtering

A proposal to make filtering arrays easier
HTML
171
star
87

proposal-ptc-syntax

Discussion and specification for an explicit syntactic opt-in for Tail Calls.
HTML
169
star
88

proposal-extractors

Extractors for ECMAScript
JavaScript
166
star
89

proposal-error-stacks

ECMAScript Proposal, specs, and reference implementation for Error.prototype.stack / System.getStack
HTML
166
star
90

proposal-intl-duration-format

164
star
91

how-we-work

Documentation of how TC39 operates and how to participate
161
star
92

proposal-Array.prototype.includes

Spec, tests, reference implementation, and docs for ESnext-track Array.prototype.includes
HTML
157
star
93

proposal-promise-try

ECMAScript Proposal, specs, and reference implementation for Promise.try
HTML
154
star
94

proposal-extensions

Extensions proposal for ECMAScript
HTML
150
star
95

proposal-hashbang

#! for JS
HTML
148
star
96

proposal-import-meta

import.meta proposal for JavaScript
HTML
146
star
97

proposal-intl-segmenter

Unicode text segmentation for ECMAScript
HTML
146
star
98

proposal-resizablearraybuffer

Proposal for resizable array buffers
HTML
145
star
99

proposal-seeded-random

Proposal for an options argument to be added to JS's Math.random() function, and some options to start it with.
HTML
143
star
100

eshost

A uniform wrapper around a multitude of ECMAScript hosts. CLI: https://github.com/bterlson/eshost-cli
JavaScript
142
star