1. Effect Handlers

Effect handlers provide a type-safe, modular way to reason about effects in your code. They let you work with state and exceptions in a purely functional (immutable) way — the state changes are observable, but only within a certain scope.

However, effect handlers are much more powerful than the most common examples. In a language with effect handlers, you can define the scheduling and logic behind async/await, cooperative coroutines, generators, and iterators — as ordinary library code, not special language features.

1.1. A Simple Example

The following Koka program defines an err effect and handles it:

effect err
  ctl err(message: string): a

fun main()
  with final ctl err(x) println("Error: " ++ x)
  err("something went wrong")

The with handler installs a handler for err; final means the handler does not resume after handling the effect.

1.2. Effects as Types

Koka tracks effects in the type system. A function that throws err has type:

fun risky() : <err> int

A function that handles all effects is typed as total (or just <> or omitted effect type). This means the compiler can statically guarantee that all effects are handled.

1.3. Why This Matters

Effect handlers unify many control-flow abstractions under one mechanism:

Pattern As an effect handler
Exceptions ctl throw: non-resumable
State fun get, fun set: resumable
Async/Await Resumable continuation scheduling
Generators ctl yield: resume after each value
Backtracking Multiple resumptions