VIP-12: provide blocks
Status: accepted by Will, 2026-10-28.
Decision
// VibeLang
provide AppLive, Users = testUsers {
try a()
try b()
}
// exactly this Effect TS
yield* Effect.provide(
Effect.gen(function* () { yield* a(); yield* b() }),
[AppLive, Layer.succeed(Users, testUsers)]
)-
provide providers { … }is exact sugar forEffect.provide. It is a statement that runs where it stands, likedo { }, and every Effect inside it is still run withtry. -
A provider is a Layer, or
Key = value, which meansLayer.succeed(Key, value). Effect's ownprovidetypes everything, so VibeLang adds no typing rules of its own. -
Ambiguity: none.
provideis a contextual keyword, and its providers and block must be on one line, as withasync. Trailing blocks don't exist (VIP-11), soprovide(x) { … }can't be read as a call. -
Amended 2026-10-29: these are ordinary blocks.
return,breakandcontinueinside them reach the enclosing function or loop, as in any TypeScript block. (for concurrentandfor trykeep their own rule, because their bodies run once per item.) Recorded under Will's standing approval of recommendations. -
Amended 2026-10-29: when
provideis a keyword.provideis a keyword only when the tokens fromprovidethrough a{on the line where the last provider ends parse as the head of a provide statement, as an arrow needs its whole head. Otherwise it is an identifier, in any code:provide(x),provide[0] = 1andprovide - 1stay TypeScript. 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 34). -
Amended 2026-10-29: no line break at a provider comma. A line break after a comma between providers is a syntax error, as "its providers and block must be on one line" says. Breaks inside a provider are free. The formatter never breaks a
providelist at a comma; a long list stays on one line or usesLayer.mergeAll(…). 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 35). -
Under VIP-6's rule, a keyword removes the global of the same name, so Effect's function is written
Effect.provide. -
Amended 2026-10-30: observer payloads and source completions. A
provideor interruption region preserves its source completion separately from the primitive Effect success value. Normal completion and outwardbreak/continuegive primitive successundefined; a sourcereturn valuegives that exact value, and barereturngivesundefined. Nested regions apply this at each boundary. Finalizers observe these generator payloads, never compiler control records; a normal source value still contributes to an enclosingdo. Real returned Effects and record-shaped objects remain data. Completion state is fresh for each execution/item, and failed, dying or interrupted bodies/finalizers transfer no pending source completion. This preserves the original generator counterpart and Effect's own finalization rules; see Region bodies. -
Amended 2026-10-30: bounded provider-comma recovery. At a statement start, a head otherwise parsing through its body-opening
{remains recognized when only a line break immediately before or after a top-level provider-separating comma violates the grammar. This recovered head stays invalid and usesEK1202on either side; outside Effect code the existingEK1201rule still applies. Relax only comma-adjacent restrictions for recognition: keep the ordinary provider grammar, no break afterprovideand same-line final{. No broader body search or global keyword reservation is added. Breaks inside provider expressions, ordinary calls without that body and expression readings afterreturnare unchanged. Preserve native provider evaluation, bindings, typing and runtime behavior; invalid heads have no lowering.
History
A union of providers was mistyped in round 1 (local, fixed). Nothing since.
- Amended 2026-10-30: ordinary-block completion values. A transparent block preserves the source completion's kind, value (including
empty) and target. A surroundingdocan observe its normal value or a value carried through a consumed jump; generated callbacks do not discard it. The compiler's transport is its choice; transfer the completion only after the region Effect and its finalizers succeed. Failures and finalization retain Effect's rules.
Prior art
Effect's Effect.provide; Kotlin's withContext(ctx) { … }.
Will's words
"I really like this syntax"
"Ok so with that I'm pretty sure all the issues for service and provide go away in which case I accept the proposal"
Corrected 2026-10-29: the example names a declared service (Users), so it runs.