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 accessible 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 pure). 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