§7 Effect System
Every function that performs side effects must declare them with ![...]:
fn pure(x: int) -> int { return x * 2; } // no effects — guaranteed purefn read_file(path: string) ![io] { ... } // requires io capabilityfn fetch(url: string) ![net] { ... } // requires net capabilityfn analyze(text: string) ![io, model] { } // multiple effectsCanonical effects:
| Effect | Token | Grants | Enforced Today |
|---|---|---|---|
| IO | io | Filesystem, console, logging | Yes |
| Network | net | HTTP, WebSocket, serve | Yes |
| Clock | clock | sleep, wall-clock | Yes |
| Model | model | AI library invocation via sfn/ai (post-1.0) | Reserved (no detector yet) |
| GPU | gpu | Tensor operations | Reserved (no detector yet) |
| Random | rand | Random generation | Reserved (no detector yet) |
Enforcement rules:
- Any function calling an effectful operation directly must declare that effect — violations produce diagnostics with fix-it hints and are errors by default
- Enforcement runs on every build path (
make compile,sfn build,sfn run,sfn test,sfn check); theSAILFIN_EFFECT_ENFORCEenv var lets capsule authors opt into=warning(telemetry-only) or=off(build-path bypass;sfn checkstill validates) - Tests follow the same rules as functions
- Cross-module call-graph propagation (Phase E, shipped): if A imports B and calls it, A must declare every effect B declares. Diagnostic code
E0402. Aliased imports (import { foo as bar }) resolve under the local name.Member-callee resolution (mod.fn()) is a Phase E2 follow-up - Capsule capability cross-check (Phase F, shipped): every function’s declared effects must be a subset of the capsule manifest’s
[capabilities] required = [...]surface. Diagnostic codeE0403. Empty surface (no[capabilities]section, or standalone .sfn outside any capsule) skips the cross-check so pre-Phase-F projects keep building
See Effect System Reference for the complete API surface per effect.