VIP-22: interruption blocks: uninterruptible, restore, interruptible
Status: accepted by Will, 2026-10-28.
Decision
uninterruptible {
try charge(card)
restore { try notify(user) } // interruptible again inside
try record(receipt)
}
interruptible { try poll() }
// exactly this Effect TS
yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
yield* charge(card)
yield* restore(Effect.gen(function* () { yield* notify(user) }))
yield* record(receipt)
}))
yield* Effect.interruptible(Effect.gen(function* () { yield* poll() }))- These are statements inside an
effectfunction or block, likeprovide(VIP-12). Each is exact sugar for Effect's function. restore { … }is allowed only inside anuninterruptibleblock; anywhere else it is a compile error. It selects the innermost enclosinguninterruptibleblock in its source activation. That entry state may already be uninterruptible;restore,interruptibleandprovideblocks do not rebind the capability.- 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-30:
restorefollows source activations. Capturing an outer mask across aneffectfunction declaration or expression, arrow, method, producer oreffectblock isEK2202: the nested Effect can run after the mask ends. That activation may introduce its ownuninterruptiblemask and restore it. Transparent regions andfor trycallback lowering add no source activation boundary; the followingfor concurrentrestriction remains. See Interruption blocks. - Amended 2026-10-30:
restorein afor concurrentbody.restoreinside anyfor concurrenthead binding initialization or body is a compile error (EK2202), whatever the limit: each item runs as a separate Effect that the enclosing mask does not govern. The limit and the iterable run outside the item region and keep their enclosing rule. Head binding initialization added 2026-10-31: it runs in the same item generator. Mask per item withEffect.uninterruptibleMaskinside the body, or use a plainfor. Decided by the agent on 2026-10-30 under Will's rule "when I state a principle, the agent derives the concrete rule and acts; I can override later" (decided detail 51a). - Under VIP-6, the keywords remove the globals of the same name, so Effect's functions are written
Effect.uninterruptibleandEffect.interruptible. - Ambiguity: none. These are contextual keywords followed by
{on the same line. - 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.
History
About 20 findings, all about masking during using acquisitions. VIP-8 removed using's Effect rules, so none apply.
- 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
Haskell's mask $ \restore -> …, Cats Effect's uncancelable { poll => }, ZIO's uninterruptibleMask, Kotlin's withContext(NonCancellable) { }.
Will's words
"ok we are on a streak of me accepting your recomendation. Keep showing them to me 1 at a time but assume I accept now. I will interrupt you if I need to"