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

EffectPurpose
IOInput/output operations
MutMutable state
FailRecoverable errors
AsyncAsynchronous operations
AllocMemory allocation
GPUGPU 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()
}

Learn More