Skip to content
LogoLogo

VIP-23: the pipe operator |>

Status: accepted by Will, 2026-10-28, under his standing approval ("assume I accept now. I will interrupt you if I need to").

Decision

const n = fetchUser(id) |> retry(%, policy) |> timeout(%, "1 second")
try n
  • Its Effect TS is nested calls, with each topic evaluated once into a temporary: const t1 = fetchUser(id); const t2 = retry(t1, policy); const n = timeout(t2, "1 second").
  • |> is exactly TC39's Hack pipe operator, pinned to a dated snapshot of the proposal, like do (VIP-2) and match (VIP-7).
  • % is the topic: the left side's value, evaluated once. The topic-versus-modulo grammar is TC39's, unchanged.
  • Effect's pipe(x, f, g) function remains available through VIP-14.
  • Ambiguity: none beyond TC39's own grammar.
  • Amended 2026-10-29: the pinned snapshot. |> is pinned to tc39/proposal-pipeline-operator commit 5ac4dfd624e68b649e6f947885bf5ef67bac19f4, the head on 2026-10-28 (UTC), vendored in tc39-pipeline-operator/, as pattern matching is pinned. Decided by the agent on 2026-10-29 under Will's rule "when I state a principle, the agent derives the concrete rule and acts; I can override later" (decided detail 52).
  • Amended 2026-10-31: arrows at the right edge. An EffectArrow is an arrow for the pinned pipe-body restriction. The input of a PipeExpression may not end in an open catch arrow handler, so the |> belongs to the handler's body; parentheses, a closed handler and a block-bodied arrow end the handler. Both rules remove second derivations, and neither rejects a program with a greedy parse. Decided by the agent under Will's rule "when I state a principle, the agent derives the concrete rule and acts; I can override later" (decided detail 246).

History

Old findings concerned a topic that ran Effects implicitly. Under VIP-5 a call never runs anything, so they don't apply.

Prior art

TC39's Hack pipes (stage 2), Hack, F#'s and Elixir's |>.

  • Clarified 2026-11-03: arrows at the right edge. The arrow-body covering restriction includes EffectArrow, as it includes AsyncArrowFunction. A pipe input cannot end in an open arrow catch handler, recursively through a pipe body: the following pipe belongs to that handler's concise body. Parentheses or a block-bodied handler close that boundary.

  • Clarified 2026-11-03: pipe diagnostics. Adopted pipe grammar and early errors without a more specific code use EK2301, including a missing topic and a whole bare plain, async or effect arrow body. Parenthesize the arrow, x |> (() => %). This follows VIP-34's diagnostic ownership rule and changes no pipe admission or evaluation rule.