VIP-18: running Effects concurrently: try.all, try.race, try.any, try.allSettled
Status: accepted by Will, 2026-10-28.
Decision
const [u, o] = try.all [fetchUser(id), fetchOrders(id)] // yield* Effect.all([…], { concurrency: "unbounded" })
const fast = try.race [primary(), mirror()] // yield* Effect.raceAllFirst([…]) first to finish
const ok = try.any [primary(), mirror()] // yield* Effect.raceAll([…]) first to succeed
const rs = try.allSettled [a(), b()] // yield* Effect.all([…].map(Effect.exit), { concurrency: "unbounded" })-
These are TC39's await operations, run with
try(VIP-5) instead ofawait. Each is exact sugar for an Effect function. -
The names keep JavaScript's meanings:
try.allruns everything at once, likePromise.all, even thoughEffect.alldefaults to one at a time.try.racegives the first to finish, andtry.anythe first to succeed. -
Amended 2026-10-29:
try.allSettledgives each element'sExit(success, failure, defect or interruption), and the whole never fails, asPromise.allSettlednever rejects. Recorded under Will's standing approval of recommendations. -
They use the same
keyword.nameshape as VIP-17'sfork.in. -
Amended 2026-10-29: operands are arrays. An object of Effects uses the
allglobal:try all({ u: fetchUser(id), o: fetchOrders(id) }, { concurrency: "unbounded" }). An object form oftry.allcan be added later without breaking anything. Recorded under Will's standing approval of recommendations. -
Amended 2026-10-29: the
try.allSettledcounterpart for an array literal. The comment above writes[…].map(Effect.exit), which is a TypeScript error when the elements differ in type. For an array literal the counterpart wraps each element,Effect.all([Effect.exit(a()), Effect.exit(b())], { concurrency: "unbounded" }), which gives the sameExitand keeps the tuple type; any other operand usesxs.map(Effect.exit). Erratum to the comment, following the TypeScript counterpart (Will: VibeLang mirrors the TypeScript counterpart where a VIP is silent). -
Ambiguity: none.
tryis a reserved word, sotry.can't start anything else. -
Amended 2026-10-30: shared static rules.
try.all,try.race,try.anyandtry.allSettledall run Effects under VIP-5: outside effect code they receiveEK0501. Each takes an array of Effects under the operand rule; an invalid operand receivesEK1801. These are the existing context and operand checks for the complete family, not new accepted contexts or operand forms. -
Amended 2026-10-31: spreads in a
try.allSettledliteral. A literal containing a spread is evaluated once, in native element order, with each spread using its value's own iterator; the counterpart wraps that array by index (source.map(Effect.exit)) and adds no iteration of a compiler-created array, so a changedArray.prototype[Symbol.iterator]sees exactly the calls native[a(), ...xs, b()]makes. Literal tuple typing, spread-as-array typing and the non-literal heterogeneous-tuple rule are unchanged. Decided by the agent on 2026-10-31 from the exact-sugar contract (decided detail 252; loop round 5 OPUS-3 and ASTRA-2).
History
About 37 findings over rounds 2 to 11, about when elements are evaluated and about do elements. VIP-1, VIP-2 and VIP-5 removed both, and none apply to this design.
Prior art
Promise.all, race, any and allSettled; TC39's await-operations proposal.
Will's words
"this is a yes"