Capabilities ​
Effects (declared with with) describe what a function requires from its environment. Capabilities (declared with is) describe what a function guarantees about its behavior.
-- Effect: requires Quantum environment
fn apply_hadamard(q: Qubit) -> Qubit with Quantum
q <- hadamard
q
end fn
-- Capability: guarantees reversibility
fn increment(x: Int) -> Int is Reversible
x += 1
x
end fn
-- Both: requires Quantum, guarantees Reversible
fn quantum_oracle(q: Qubit) -> Qubit with Quantum is Reversible
q <- hadamard
q <- pauli_z
q
end fnHow They Differ ​
Effects (with) | Capabilities (is) | |
|---|---|---|
| Meaning | "I need this to run" | "I promise this property" |
| Propagation | Upward - caller must handle | Downward - caller can rely on it |
| Enforcement | Runtime handler required | Compile-time verification |
| Examples | Quantum, IO, Fail | Reversible, Infallible |
Propagation explained:
- Effects bubble up: if
foohaswith IO, then anything callingfooalso needswith IO(or a handler) - Capabilities flow down: if
fooisInfallible, callers knowfoowon't fail - they benefit without declaring anything
Reversible ​
A function marked is Reversible guarantees all operations can be undone. The compiler enforces this by only allowing reversible operations inside the function.
fn swap_add(x: Int, y: Int) -> Tuple[Int, Int] is Reversible
x += y -- reversible: inverse is x -= y
x ^= y -- reversible: XOR is self-inverse
(x, y)
end fnWhat's allowed:
- Compound assignments on Int:
+=,-=,^= - Compound assignments on Float:
+=,-=,*=,/= - Swaps:
x <-> y - Quantum gates (except
measure) - Calling other
Reversiblefunctions
Note: *= and /= on Int are forbidden because integer division loses information (e.g., 5/2=2, but 2*2=4≠5).
What's forbidden:
- Regular assignment
x = expr- destroys the old value, making reversal impossible measure- collapses quantum state irreversibly- Calling non-reversible functions
See Reversibility for compute blocks and detailed examples.
Infallible ​
A function marked is Infallible guarantees it will always return a value - it cannot fail or panic.
fn safe_add(a: Int, b: Int) -> Int is Infallible
a + b
end fn
fn safe_max(a: Int, b: Int) -> Int is Infallible
match a > b
True -> a
False -> b
end match
end fnWhat's allowed:
- Pure computations (arithmetic, logic, pattern matching)
- Calling other
Infalliblefunctions - Operations that always succeed
What's forbidden:
fail(...)callstryexpressions (implies possible failure)- Calling functions that might fail
When to use Infallible:
- Helper functions in critical paths where failure isn't an option
- Pure mathematical operations
- Functions that must compose without error handling
- Building blocks for larger infallible computations
-- Building infallible pipelines
fn normalize(x: Int, min: Int, max: Int) -> Float is Infallible
to_float(x - min) / to_float(max - min)
end fn
fn clamp(x: Int, lo: Int, hi: Int) -> Int is Infallible
match x < lo
True -> lo
False -> match x > hi
True -> hi
False -> x
end match
end match
end fnCombining Capabilities ​
A function can provide multiple guarantees:
fn pure_increment(x: Int) -> Int is Reversible, Infallible
x += 1
x
end fnThis function is both reversible (can be undone) and infallible (cannot fail). Both constraints are enforced by the compiler.
Combining with effects:
-- Requires Quantum, guarantees Reversible
fn phase_flip(q: Qubit) -> Qubit with Quantum is Reversible
q <- pauli_z
q
end fn
-- Requires IO, guarantees Infallible
fn log_message(msg: String) -> Unit with IO is Infallible
print(msg)
end fnThe with and is clauses are independent - a function can have any combination of effects and capabilities.