Help us improve
Share bugs, ideas, or general feedback.
From ocaml-dev
Guides OCaml 5 algebraic effects patterns for designing scheduler APIs, effects vs exceptions, Eio/affect integration, streaming suspension/errors, layered source-parser design.
npx claudepluginhub avsm/ocaml-claude-marketplace --plugin ocaml-devHow this skill is triggered — by the user, by Claude, or both
Slash command
/ocaml-dev:effectsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Effects for control flow, exceptions for errors.**
Covers core Effect patterns: Effect<A, E, R> type, succeed, fail, sync, promise, and Effect.gen for composing type-safe effects in TypeScript.
Provides expert guidance on Effect-TS patterns including services, layers, error handling, service composition, and refactoring code with 'effect' imports. Covers Effect + Next.js integration.
Provides expert guidance on Effect-TS for functional TypeScript including typed errors, dependency injection, Effect Layers, concurrency, pipelines, and production patterns.
Share bugs, ideas, or general feedback.
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 |
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)
Effects are internal to the scheduler. User code looks synchronous:
(* Reading blocks via internal effects *)
let data = Eio.Flow.read flow buf
Explicit effects for fiber scheduling:
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 *)
}
Effect-agnostic streaming. The pull function you provide can perform any effects:
(* 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 *)
Wire effect-performing sources to effect-agnostic libraries:
(* With Eio *)
let reader = Bytesrw_eio.bytes_reader_of_flow flow in
let r = Binary.Reader.of_reader reader in
parse r (* Eio effects happen in pull function *)
(* With affect *)
let pull () =
let buf = Bytes.create 4096 in
perform (Block { block; cancel; return = fun _ ->
Slice.make buf ~first:0 ~length:n })
in
let reader = Bytesrw.Bytes.Reader.make pull in
parse (Binary.Reader.of_reader reader)
Slice.eod from bytesrw means final EOF - no more data will ever come.
Don't: Define Await effect in protocol parsers
(* WRONG - parser shouldn't know about suspension *)
let get_byte t =
if no_data then perform Await; ...
Do: Let the source handle suspension
(* RIGHT - parser just reads, source handles waiting *)
let get_byte t =
match pull_next_slice t with (* may perform effects *)
| Some slice -> ...
| None -> raise End_of_file (* true EOF *)