OCaml 5 algebraic effects design patterns. Use when Claude needs to: (1) Design APIs that interact with effect-based schedulers, (2) Decide between effects vs exceptions, (3) Integrate libraries with Eio or affect, (4) Handle suspension vs error cases in streaming code, (5) Understand the layered effect design principle
View on GitHubavsm/ocaml-claude-marketplace
ocaml-dev
January 20, 2026
Select agents to install to:
npx add-skill https://github.com/avsm/ocaml-claude-marketplace/blob/main/plugins/ocaml-dev/skills/effects/SKILL.md -a claude-code --skill effectsInstallation paths:
.claude/skills/effects/# OCaml 5 Effects Design
## Core Principle
**Effects for control flow, exceptions for errors.**
| Concern | Mechanism | Example |
|---------|-----------|---------|
| Suspension (wait for data) | Effects | `perform Block`, `perform Yield` |
| Error (EOF, malformed) | Exceptions | `raise End_of_file`, `Invalid_argument` |
## Layered Design
Effects should be handled at the **source level**, not in protocol parsers:
```
Application
↓
Protocol parser (Binary.Reader, Cbor, etc.)
↓ raises exceptions on EOF/error
bytesrw (effect-agnostic)
↓ just calls pull function
Source (Eio flow, affect fd, Unix fd)
↓ performs effects for suspension
Effect handler (Eio scheduler, affect runtime)
```
### Why This Matters
- **Parsers stay pure**: No effect dependencies, easy to test
- **Sources control blocking**: Handler decides wait vs fail vs timeout
- **Composability**: Same parser works with any effect system
## Effect Libraries
### Eio
Effects are internal to the scheduler. User code looks synchronous:
```ocaml
(* Reading blocks via internal effects *)
let data = Eio.Flow.read flow buf
```
### affect
Explicit effects for fiber scheduling:
```ocaml
type _ Effect.t +=
| Block : 'a block -> 'a Effect.t (* suspension *)
| Await : await -> unit Effect.t (* wait on fibers *)
| Yield : unit Effect.t (* cooperative yield *)
(* Block has callbacks for scheduler integration *)
type 'a block = {
block : handle -> unit; (* register blocked fiber *)
cancel : handle -> bool; (* handle cancellation *)
return : handle -> 'a (* extract result *)
}
```
### bytesrw
Effect-agnostic streaming. The pull function you provide can perform any effects:
```ocaml
(* bytesrw just calls your function *)
let reader = Bytesrw.Bytes.Reader.make my_pull_fn
(* If my_pull_fn performs Eio effects, they propagate *)
(* If my_pull_fn performs affect Block, they propagate *)
(* bytesrw doesn't care - it just calls the function *)
```
## Integrat