Effect System
Algebraic effects for controlled side effects
The Effect System
Sounio uses algebraic effects to make side effects explicit and controllable.
Why Effects?
In most languages, any function can:
- Read/write files
- Make network requests
- Throw exceptions
- Mutate global state
This makes code hard to reason about. In Sounio, effects are declared in function signatures.
Basic Usage
// This function can perform IO
fn greet(name: str) with IO {
print("Hello, " + name + "!")
}
// This function is pure - no effects
fn double(x: i32) -> i32 {
x * 2
}
// This function can fail
fn parse_number(s: str) -> i32 with Fail {
if s.is_empty() {
perform Fail::error("Empty string")
}
s.parse_i32()?
}
Built-in Effects
| Effect | Purpose |
|---|---|
IO | Input/output operations |
Mut | Mutable state |
Fail | Recoverable errors |
Async | Asynchronous operations |
Alloc | Memory allocation |
GPU | GPU computation |
Effect Handlers
Effects are handled by effect handlers:
fn main() with IO {
// Handle potential failure
handle parse_number("42") {
Fail::error(msg) => {
print("Parse failed: " + msg)
resume(0) // Continue with default value
}
}
}
Composing Effects
Functions can require multiple effects:
fn fetch_data(url: str) -> Data with IO, Fail, Async {
let response = perform Async::fetch(url)?
let data = parse_json(response)?
data
}
Effect Polymorphism
Generic functions can be polymorphic over effects:
fn map<T, U, E>(list: List<T>, f: fn(T) -> U with E) -> List<U> with E {
list.iter().map(f).collect()
}