Why Sakura Lang
The problem with reviewing agent-written code, the thesis that the signature should be the contract, and the principles that follow from it.
The problem
AI agents now write a large share of new code, and that share is growing. The bottleneck has moved from writing code to trusting it.
The usual answer is code review, but reviewing diffs line by line does not scale when an agent can produce thousands of lines an hour. Reviewers skim. Skimming works only if the important facts about a change are easy to find, and in most languages they are not.
Four properties of mainstream languages make agent-written code risky to accept:
- Exceptions. Any call can throw, and nothing in the signature says what. A reviewer cannot tell which failures are handled without reading every callee.
- Null. A value that may be absent looks the same as one that is always present.
- Ambient authority. Any function in any dependency can open a socket, read an environment variable, or write a file. Supply-chain attacks rely on this.
- Hidden IO. A function named
format_pricemight call a currency API. Nothing stops it, and nothing in the signature reveals it.
Agents make these problems sharper, not different. An agent will happily add a network call to a helper to make a test pass, catch and ignore an exception to silence an error, or interpolate a request parameter into a SQL string. Each change is small and plausible in a diff.
The thesis
Make the signature the contract, and make the compiler enforce it.
In Sakura Lang, a function signature states what the function takes, what it returns, every effect it may perform, every error it may fail with, and the guarantees it makes about its result:
fn signup(email: Email) -> User ! {Users, Clock, Random, Fail<SignupError>}
ensures result.email == emailThe body cannot perform an effect that is not listed or fail with a tag that is not listed; both are compile errors. The ensures clause is checked at runtime on every normal return, in every build mode.
This changes what review means. A human reviews the signatures, which are short and change rarely. The compiler checks the bodies, which are long and change constantly. When an agent's change needs a new effect, the signature has to change, and a signature change is exactly the thing a reviewer looks for. Compiler fixes that would widen authority or weaken a contract are never applied automatically.
Why not an existing language
Every language below is excellent at what it was designed for. None was designed for this workflow, and each shapes Sakura Lang in some way. The prior art page lists, language by language, what Sakura Lang takes and what it rejects.
TypeScript. Huge ecosystem, fast feedback, and the language most agents write best today. But exceptions are untyped, any function can perform any IO, any and type assertions are one keystroke away, and there are several ways to do almost everything.
Python. The most readable mainstream language and a strong fit for scripts and data work. Types are optional and unchecked at runtime, exceptions and IO are invisible in signatures, and dependencies run with full authority at import time.
Rust. Proves that a language can be fast and memory safe without a garbage collector, and that a strict compiler with good diagnostics changes how people work. The cost is lifetimes and the borrow checker. Agents spend effort satisfying ownership rules unrelated to the task, and IO and panics are not tracked in signatures.
Go. Small, consistent, and quick to compile, with one toolchain and a formatter with no options. That simplicity is a direct inspiration. But errors can be discarded, nil remains, effects are untracked, and any package can reach the network.
Effect-TS. Brings typed errors and typed requirements to TypeScript, and shows that the model works for real services. It is a library on top of a language that cannot enforce it: plain TypeScript can still throw and still perform IO directly. Sakura Lang's ! {Users, Fail<E>} is that model with no wrapper type, and it is the only way to write code.
Koka, Unison, and Roc. Koka contributed row-typed effects, effect-polymorphic higher-order functions, and Perceus reference counting. Unison contributed domain-named abilities with plain handlers and a compiler that speaks MCP; Sakura Lang keeps ordinary files and Git rather than a codebase database. Roc contributed platform-owned authority and in-place updates behind a value interface. Sakura Lang combines these with contracts, capabilities, Untrusted/Secret types, and machine-readable tooling, aimed at agents writing code for humans to review.
Design principles
These follow design §1.
The signature is the contract
Parameter types with refinements, result type, effect row, error union, and requires/ensures: a reader can review a function from its signature alone. Every top-level function has a full signature; only local bindings are inferred.
One way
One function syntax, one call syntax, one recovery construct (try/catch), one handler syntax (with), one absence type (Option), one failure protocol (Fail<E>), one formatter. No operator overloading, implicit conversions, macros, user-defined operators, or inheritance.
Omission has one meaning
A function type without ! is pure. Missing cases, missing effects, and missing error handling are compile errors, never defaults.
Diagnostics are an API
Every diagnostic has a stable code, byte spans, structured expected and actual values, causal links, and fixes that declare their impact. See Diagnostics.
Honest guarantees
The compiler never prints "verified" for a program that merely compiled. Every claim about a program has exactly one status:
| Status | Meaning | Established by |
|---|---|---|
proved | Holds on all executions under the stated assumptions. | The type and effect checker; later, SMT-discharged contracts. |
runtime_checked | Violations are detected at the boundary and become a defect. | Contract lowering, checked arithmetic, checked refinements. |
tested | Held on the executed inputs. | sakura test. |
trusted | Assumed. | FFI, host handlers, @trust sites, the toolchain itself. |
The type system proves that every performed effect has a handler, every Fail tag is caught or declared, every match is exhaustive, no value is null, no index is unchecked, a Secret is never shown, and a capability cannot be forged. It does not prove business correctness, termination, or that the specification is the right one.
Compiler owns memory
No lifetimes, borrow annotations, or Rc in user code. The price is a restricted data model (no aliasable mutation, no cycles) and explicit resource scopes.
Non-goals for v0
- Multi-shot or non-tail-resumptive handlers,
resume, first-class continuations. - An
Asynceffect or a sync/async split in source. - User-defined traits or interfaces. A fixed set of derived constraints (
Eq,Ord,Hash,Show,Schema) covers v0. - Higher-rank types, GADTs, type-level computation.
- Information-flow control.
Secret<T>prevents accidental disclosure, not deliberate leakage. - Automatic termination proofs.
- Durable workflows, a distributed runtime, or a UI engine. These are later packages.
- A tracing GC on native or WASM.
Next
Read the Quickstart preview, or go straight to the Language Tour.