ferrule

effects

α1
effect-syntaxeffect-subset-rulestandard-effectseffect-inferencecapability-flow-linteffect-polymorphism (α2)suspend-effect (β)async-suspension (β)

effects

effects declare what a function might do. io, allocation, time access, that kind of thing. they're part of the function type but separate from type generics.

no effects means pure. you can tell from the signature whether a function does io.

syntax

types go in <>, effects go in []:

function process<T, U>(input: T) -> U effects [alloc, io] {
    // ...
}

a function with no effects clause is pure:

function add(x: i32, y: i32) -> i32 {
    return x + y;
}

standard effects

effectmeaningneeds capability?
allocallocate/free memoryno
fsfile system operationsyes, Fs
netnetworkingyes, Net
iogeneric device ioyes, device cap
timeaccess clocks, sleepyes, Clock
rngrandomnessyes, Rng
atomicsatomic memory operationsno
simdsimd intrinsicsno
cpuprivileged cpu instructionsno
ffiforeign abi callsdepends

some effects need capabilities (fs, net, time, rng). others don't (alloc, atomics, simd). see ../modules/capabilities.md.

subset rule

for any call g() inside f, the effects of g must be a subset of f's effects:

function caller() -> Unit effects [fs] {
    netCall();  // error: requires [net], not in [fs]
}

this is the core enforcement. you can't sneak effects past the caller.

effects vs capabilities

this is important to understand: they're related but different.

effects are markers. they say "this function might do io" or "this function might allocate". they're compile-time information.

capabilities are values. they're the authority to actually do the io. you pass them around.

the relationship:

effectcapabilityexplanation
fsFsneed Fs cap to do fs effect
netNetneed Net cap to do net effect
timeClockneed Clock cap to do time effect
rngRngneed Rng cap to do rng effect
allocnonejust marks allocation
atomicsnonejust marks atomic ops

if a function has effects [fs], it must have a cap fs: Fs somewhere in the call chain. this is checked by the capability flow lint.

function readAll(p: Path, cap fs: Fs) -> Bytes error IoError effects [fs] {
    const f = check fs.open(p);
    return ok check fs.readAll(f);
}

purity

a pure function has no effects and no error clause:

function add(x: i32, y: i32) -> i32 {
    return x + y;
}

functions with error E are not pure even if they have no effects. error propagation is a form of control flow.

effect inference

within a module, the compiler can infer effects from the function body. but public symbols must spell them out:

// private, inference ok
function helper() {
    println("hello");  // inferred: effects [io]
}

// public, must be explicit
pub function api() -> Unit effects [io] {
    helper();
}

exports without explicit effects are rejected.

higher-order functions

when you take a function as parameter, you need to handle its effects:

function forEach<T>(items: View<T>, f: (T) -> Unit effects F) -> Unit effects [...F] {
  for item in items {
    f(item);
  }
}

the ...F spreads the effects from f into forEach's effects. whatever effects f has, forEach also has.

threading capabilities

for higher-order functions that need capabilities:

function processFiles(paths: View<Path>, cap fs: Fs) -> View<String> 
    error IoError 
    effects [fs, alloc] 
{
    return paths.map(function(p: Path) -> String effects [fs] {
    return check fs.readAllText(p);
    });
}

the capability flows through the closure.

what's planned

effect polymorphism (α2) with named effect variables:

function map<T, U>(arr: View<T>, f: (T) -> U effects F) -> View<U> 
    effects [alloc, ...F] 
{
    // F is whatever effects f has
}

suspend effect (β) for async:

function fetch(url: String, cap net: Net) -> Response 
    error NetError 
    effects [net, suspend] 
{
    const socket = net.connect(url.host, url.port)?;
    return ok socket.readAll()?;  // may suspend here
}

the suspend effect means the function may pause and resume. this is how async works without function coloring. see the async rfc for details.

summary

syntaxmeaning
effects [fs, net]has fs and net effects
effects [alloc, ...]has alloc plus spread from params
effects [...F]has all effects from F
no effects clausepure (private) or error (public)

On this page