System C — effects and capabilities reconciled
Brachthäuser, Schuster, Lee, Boruch-Gruszecki, Effects, capabilities, and boxes, OOPSLA 2022.
System C is the type-theoretic account of the choice grant makes dynamically: a capability is permission over the effect set, and mentioning that authority is itself an effect. It and ral meet on one identity — in call-by-push-value terms, boxing is thunking and unboxing is forcing the effect of mentioning a capability (System C §6.8, and the thesis on p.3). Where ral resolves authority on the dynamic context and checks it at runtime, System C lifts the same structure into types, toggled term-by-term.
The shared thesis
- Mentioning authority is an effect; the thunk delays it. ral’s value
language is inert —
$-deref, builtins, and the prelude perform nothing (cbpv, syscalls-are-effects). System C makes the same move one level down: a block (second-class function) may close over capabilities, but doing so is the effect, suspended byboxand discharged byunbox. ral is full CBPV and already has the thunk; System C shows the same thunk can carry a capability set.
Correspondences
- Scope-based ↔ type-based is exactly ral’s dynamic-authority choice. System
C’s default (its base System Ξ) is scope-based: authority is whatever is in
scope, with no effect types — the ergonomic pole. That is
scoping’s “dynamic scope for ambient authority; a caller
cannot lexically know which operations a callee performs.”
box/unboxis the selective crossing into types; ral simply never crosses. - Self-masking is capability-set subtraction, with a soundness theorem. ral’s
handlers are deep, self-masking, tail-resumptive
(effects-handlers). System C’s
tryrule reads𝒞 = (𝒞₁ \ {f}) ∪ 𝒞₂— the\ {f}is self-masking, and the ill-formedness ofσ at {f}outside its delimiter is what forbids a capability escaping its handler (Theorem 3, Corollary 9). - grant is the restriction half of the judgment; ral has no requirement
half. System C’s
𝒞is read two ways: as input it is a context restriction (what is available) — grant’s meet-narrowed authority; as output it is a context requirement (what the body actually uses). ral tracks only the restriction. Nothing in ral computes which operations a body will perform. - Capability sets form a lattice; grant’s meet is its dual. System C orders sets by inclusion (subeffecting). grant narrows authority by intersection with anti-monotone denies (grant) — the same lattice, read on permission rather than on use.
- Regions are
within. System C’s scoped state (§5.2) needs no type ceremony while a handle is used second-class, and surfaces in the type only when boxed to escape. ral’swithin [dir: …]is morally a region; the pattern generalises to any scoped resource.
Divergences (deliberate)
- Open external names vs bound capabilities. ral operations are open
external names resolved through
PATH(name-resolution); System C capabilities are lexically bound term variables introduced by handlers — a strict object-capability discipline. This is precisely grant’s recorded concession (“bare command names are ambient … not a strict object-capability”). System C is the principled fix, at the cost of the ergonomics ral keeps. - ral is full CBPV; System C is fine-grain CBV (§6.8). System C cannot
abstract over computations — a block must be boxed to be returned. ral’s thunks
return computations directly, so ral is more expressive on that axis; System
C’s
boxis its workaround for what ral has natively. - No first-class
resumeis vindicated. System C’s handlers reify multi-shot continuations (its cooperative-multitasking example resumes twice). That forces a cyclic capability-set constraint on the continuation binder and fixed-point inference — the authors’ hardest open problem (§5.3, §5.6), which they note “only arises since we offer support for first-class continuations.” ral’s tail-resumptive restriction (handlers-deep-self-masking) buys exactly that inference simplicity.
What ral could borrow
- Selective boxing as a static-guarantee escape hatch — only where wanted.
ral stays scope-based by default. The case for crossing into types is a
boundary that wants a static bound on the effect set rather than only the
runtime grant chokepoint: the exarch sandbox, or
a
spawn’d thunk whose carried authority one wants to read off its type. System C is the recipe; adopting it broadly would contradict syscalls-are-effects’s dynamic-authority commitment, so it stays a hatch, not a turn. Rejected as a language feature on review (related-borrowables-rejected).
Cite: Brachthäuser, Schuster, Lee, Boruch-Gruszecki, Effects, capabilities, and
boxes: from scope-based reasoning to type-based reasoning and back, PACMPL 6
(OOPSLA 2022), doi:10.1145/3527320. Zotero
4CDKYAI4.