Blog › ICP guides
Effekt developer on retainer: algebraic effects, handler scoping, capability-based effect safety, and resumable continuations on monthly retainer
September 26, 2026 · ~15 min read
An Effekt file processing pipeline was being built with algebraic effects. The developer defined a FileAccess effect with a readFile operation and a Pipeline effect with a processItem operation. The pipeline needed to read files during processing, so both effects were required. The developer composed the two handlers, but installed the FileAccess handler inside the Pipeline handler rather than outside it: the handle { ... } with FileAccess { ... } block was nested inside the handle { ... } with Pipeline { ... } block. In Effekt, handler installation is lexically scoped — effect operations are dispatched to the innermost handler that is lexically enclosing the call site. With FileAccess installed inside Pipeline, any readFile calls that occurred at call sites not enclosed by the inner FileAccess handle block — including calls made from the outer pipeline setup code — could not be dispatched to the FileAccess handler. The Effekt runtime raised unhandled effect errors at the readFile call sites that were outside the inner handler’s scope. Unhandled effect errors: 2 per pipeline run. The Effekt developer on retainer diagnosed the handler installation order and restructured the composition: the outer handle { ... } with FileAccess { ... } block now wraps the inner handle { ... } with Pipeline { ... } block, so all readFile calls anywhere inside the pipeline computation are covered by the FileAccess handler. Unhandled effect errors: 2 → 0.
The work log entry read “fixed pipeline handler composition errors, 7h.” It names the result and duration. It cannot explain why handler nesting order matters in Effekt in a way that it does not in systems with dynamic effect scoping — Effekt uses lexical scoping for effect dispatch, meaning that whether a given call site can be handled by a given handler depends on whether the handler’s handle { ... } with { ... } block lexically encloses the call site; a handler installed inside another handler only covers call sites inside its own handle block, not call sites in the outer computation; this is unlike dynamic-extent handler dispatch (as in some other algebraic-effects languages) where a handler installed anywhere on the call stack can handle operations from any callee. It cannot explain the resume continuation mechanism — in Effekt, when a handler handles an effect operation, the handler body receives a resume value that is the first-class continuation of the computation that performed the operation; calling resume(value) resumes the computation with value as the result of the operation; for one-shot effects (where resumption happens exactly once), the compiler generates efficient stack-based continuation passing; for multi-shot effects (where the continuation may be called more than once, as in non-determinism or backtracking), the developer must annotate the capability as box resume to indicate that the continuation must be heap-allocated and can be called multiple times. It cannot explain capability-based effect safety — Effekt’s effect system tracks which effects a computation uses in its type signature as T / {Effect1, Effect2}; a function that performs a readFile operation has the type A => B / {FileAccess}; a function that calls such a function must itself declare the FileAccess effect unless it installs a handler that covers it; this propagation makes the set of effects used by any function explicit and checked at compile time. The 7 hours of handler scoping analysis, composition restructuring, and effect type audit are invisible in the diff.
Effekt algebraic effects: lexical handler scoping, resume continuations, and effect type propagation
Effects in Effekt are declared with the effect keyword: effect FileAccess { def readFile(path: String): String } declares an effect named FileAccess with one operation readFile that takes a String and returns a String. Handlers for effects are expressed with the handle { computation } with EffectName { def readFile(path) { resume(/* read content */); } } syntax. The handler body for each operation receives the continuation as the implicit resume function: calling resume(value) continues the computation that called readFile with value as the result. Operations that do not resume (such as exception handlers or abort-style effects) simply do not call resume. Operations that resume multiple times (backtracking, non-determinism) must declare the capability as box resume in the handler signature to indicate that the continuation is captured and may be invoked more than once; Effekt allocates such continuations on the heap rather than the stack, which has a performance cost that the developer must reason about explicitly.
The effect type system in Effekt tracks the set of unhandled effects in function signatures. A computation that calls readFile has type Unit / {FileAccess}: it produces Unit and may perform the FileAccess effect. When that computation is wrapped in a handle { ... } with FileAccess { ... } block, the FileAccess effect is eliminated from the type and the resulting computation type is Unit / {} — a pure computation. If the Pipeline handler is installed outside the FileAccess handler, then the Pipeline computation’s type must include FileAccess in its effect set; the outer FileAccess handler eliminates it. If the FileAccess handler is installed inside the Pipeline handler, then only the computation within the inner handle block has FileAccess handled; code outside that block still carries the unhandled FileAccess effect, which produces a type error or a runtime unhandled-effect exception depending on where the dispatch fails. Effekt was developed as a research language at the Programming Languages and Verification group at TU Darmstadt, exploring capability-based effect safety as an alternative to more permissive effect systems. Its closest retainer neighbors are Eff developer retainers (Eff pioneered algebraic effects with handlers; Effekt’s handler semantics differ in using lexical scoping and second-class capabilities) and Koka developer retainers (Koka is another algebraic effects language with row-polymorphic effects), but Effekt’s lexical handler scoping, capability-based effect passing, and second-class capability safety model make the retainer work distinct in handler composition analysis, effect scope auditing, and resumable continuation design.
Effekt type system, algebraic data types, pattern matching, and module capabilities
Effekt’s type system includes the standard primitive types (Int, Double, String, Bool, Unit) and collection types (List[T], Option[T]). Algebraic data types are declared with type Name { Case1(field: Type); Case2(field1: Type, field2: Type) }; pattern matching uses pipeline match { case Case1(x) => ...; case Case2(x, y) => ... } with exhaustiveness checking. Pure functions have type A => B; effectful functions have type A => B / {Effect1, Effect2} where the effect set lists all unhandled effects. Effekt uses implicit effect polymorphism: a function that calls another effectful function inherits its effects without requiring explicit effect variable annotations in most cases; the compiler infers the effect set by examining call sites. Explicit effect annotations are required at module boundaries and capability-passing positions.
Effekt’s module system uses module ModuleName declarations and import OtherModule for dependencies. Effects can be abstracted as interface types and passed as capabilities to functions: a function that requires a FileAccess capability but does not install the handler itself declares the capability as a parameter, allowing the caller to supply the specific handler. This capability-based approach is the primary mechanism for dependency injection in Effekt — rather than global effect handling or implicit effect propagation through the call stack, Effekt programs thread capabilities explicitly to the functions that need them. The capability parameter approach also enables testing: a test can supply a mock FileAccess capability that returns predefined strings rather than reading actual files, without changing the function under test.
How HourTab tracks Effekt developer retainer hours
Effekt retainer work carries the invisible-hours problem specific to algebraic effect systems: the semantic distinction between “which handler covers this call site” and “where is this handler installed” is not visible in the code diff at all. The handler installation order error described above — where the FileAccess handler installed inside the Pipeline handler failed to cover readFile calls at sites outside the inner block — appears in the code as two handle { ... } with { ... } blocks nested in one order versus the other order. The diff shows a nesting swap: four lines reordered. The work that preceded that swap was understanding Effekt’s lexical scoping rule for effect dispatch (which callers of the effect assume about handler placement), identifying all call sites for readFile and their lexical position relative to each handler block, constructing the correct nesting order from the set of call-site-to-handler relationships, and verifying that the restructured composition satisfied the effect type checker. That analysis — six to ten hours of tracing call sites through the lexical structure of a multi-effect program — is invisible in a diff that shows four lines reordered.
HourTab gives Effekt developers a public retainer-hours URL they send to clients — typically programming-languages researchers using Effekt to prototype effect system designs, backend engineers adopting Effekt for its effect-safe I/O abstractions, and compiler teams using Effekt’s effect system as a reference for effect safety in their own language designs. For Effekt retainers, each work log entry should name the mechanism (handler scoping: effect name, nesting order before and after, unhandled effect error count before and after; continuation: one-shot resume(value) vs multi-shot box resume, effect operation name and resumption count; effect polymorphism: explicit T / {E} annotation, capability parameter passing, effect inference vs annotation boundary). Effekt retainers are often compared to Eff developer retainers for the shared algebraic-effects foundation, but Effekt’s lexical handler scoping (where dispatch is determined by which handle block lexically encloses the call site, not by dynamic extent), its second-class capability model (which prevents effect capabilities from being stored in data structures and called later, ensuring safe resumption), and its capability-based dependency injection pattern (where handlers are passed as explicit capability parameters to functions that need them) make the retainer work distinct in handler composition auditing, lexical scope tracing, and capability-passing architecture. HourTab’s work log makes the handler scoping analysis, nesting restructuring, and effect type propagation audit visible to clients who would otherwise see only the symptom — an unhandled effect error at a call site they thought was covered — and not understand why the fix required knowing Effekt’s lexical dispatch rule, tracing the lexical positions of all call sites for a given effect relative to each handler block, and then deriving the correct handler nesting order from that analysis.
Track Effekt developer retainer hours without the status emails
HourTab gives Effekt developers a public URL per client retainer. One link, no login, live burn-down. Your clients stop asking “how many hours do I have left?” and your algebraic-effects audit log — handler scoping analysis, nesting restructuring, continuation design — becomes the proof of value that gets the retainer renewed.
See HourTab pricing →FAQ: Effekt developer retainers
What does an Effekt developer on retainer typically do?
An Effekt developer on monthly retainer covers algebraic effect system design (effect Name { def op(args): RetType } declarations; handler { def op(args) { resume(value) } } installation; handle { ... } with EffectName { ... } composition; lexical scoping where effect ops are dispatched to the innermost enclosing handler; resume(value) for one-shot continuation; box resume for multi-shot; effect parameter passing; capability-based effect safety where effects are tracked as T / {Effect}), Effekt type system (Int, Double, String, Bool, Unit primitives; List[T], Option[T]; algebraic data types with case classes; pattern matching; pure A => B vs effectful A => B / {E} function types; implicit effect polymorphism; region-based allocation), and Effekt module system (module declaration; import; effect interface types; capability-based dependency injection; capability parameter passing; testing with mock capabilities).
What Effekt work is most commonly underlogged in a retainer?
Handler installation order diagnosis (FileAccess handler installed inside Pipeline handler instead of outside; Effekt lexical scoping dispatches to innermost enclosing handler; readFile calls outside inner handle block not covered; unhandled effect errors: 2/pipeline run; reversed nesting: outer FileAccess wraps inner Pipeline; errors: 2/run → 0; 6–10 hrs invisible); resume continuation design (one-shot resume(value) for normal flow; box resume for multi-shot backtracking/non-determinism; continuation capture in data structures; 5–9 hrs invisible); effect polymorphism debugging (implicit effect inference vs explicit annotation; capability passing vs lexical handling; effect subsumption rules; 4–7 hrs invisible); handler composition architecture (nesting order derivation; state threading via capability passing; effect interaction analysis; 4–6 hrs invisible).
What are typical Effekt developer retainer rates?
Entry-level Effekt developers (1–2 years, basic effect declarations, handler installation, resume continuations) bill at $65–$120/hr. Mid-level Effekt programmers (2–4 years, handler scoping, effect polymorphism, capability passing, resume continuation design) bill at $105–$190/hr. Senior Effekt effect system developers (4–8 years, complex handler composition, multi-shot continuations, capability-based safety, large-scale effect system design) bill at $155–$280/hr. Monthly retainer ranges: $2,400–$4,600/mo advisory (15–25 hrs), $6,500–$16,000/mo for full Effekt effect system engineering.
What should an Effekt developer retainer agreement include?
An Effekt developer retainer agreement should specify: effect system design scope (effect Name { def op(args): RetType } declarations; handler { def op(args) { resume(value) } }; handle { ... } with { ... } composition; lexical scoping; one-shot vs multi-shot continuations; capability-based safety); Effekt type system scope (algebraic data types; case classes; pattern matching; pure vs effectful types; T / {E} effect annotations; implicit polymorphism; region allocation); module system scope (module; import; effect interface types; capability abstraction; dependency injection; mock capabilities); and hour logging format (handler scoping: effect name, nesting order before/after, error count before/after; continuation: one-shot vs multi-shot, operation name; specific effect and operation names).
How should Effekt developer retainer hours be logged?
Log each Effekt retainer session with: handler scoping category (FileAccess handler installed inside Pipeline handler; Effekt lexical scoping: dispatch to innermost enclosing handler; readFile calls outside inner handle block: unhandled; unhandled effect errors: 2/pipeline run; reversed nesting: outer FileAccess wraps inner Pipeline; errors: 2/run → 0; specific effect names and call sites); continuation category (resume(value) one-shot: continuation called exactly once, stack-based; box resume multi-shot: continuation heap-allocated, callable multiple times; which effect operation, whether one-shot or multi-shot, specific handler body); effect polymorphism category (implicit inference: compiler infers effect sets from call sites; explicit T / {E1, E2}: at module boundaries; capability passing: handler passed as parameter vs lexically installed; effect subsumption: T / {E} is subtype of T / {E, F} when F is handled; specific function and effect); and before/after metric including unhandled effect error count per pipeline run or computation run.