Reference

View Source

< How It Works | Up: Under the Hood | Index

Glossary

Computation — A function (env, k) -> {result, env} that describes an effectful program. Inert until run.

Effect — A named capability a computation can request. Defined by modules (e.g. Skuld.Effects.State). An effect operation returns a computation that looks up its handler at runtime.

Handler — A function that interprets effect requests. Installed via with_handler(comp, args). Same computation, different handlers — production vs test.

Evidence — The map of effect signatures to handler functions in the environment. O(1) lookup.

Continuation (k) — "What happens next" after an effect completes. Normal effects call k once. Throw discards k. Yield captures k for later resumption.

Bind (<-) — Sequences two computations. Desugars to Comp.bind/2.

Sentinel — A special return value (%Throw{}, %ExternalSuspend{}) signalling abnormal completion. The ISentinel protocol dispatches behaviour.

Scope — A handler boundary with cleanup. Comp.scoped/2 guarantees cleanup runs on exit or Throw, but not on Suspend.

Auto-lifting — Bare values in comp blocks are automatically wrapped in Comp.pure/1.

Effect reference

Foundational

EffectModuleOperationsHandlerTest
StateSkuld.Effects.Stateget, put, modify, getswith_handler(comp, init)Same
ReaderSkuld.Effects.Readerask, asks, localwith_handler(comp, val)Same
WriterSkuld.Effects.Writertell, peek, listen, pass, censorwith_handler(comp, [])Same; output: captures
ThrowSkuld.Effects.Throwthrow, catch_errorwith_handler(comp)Same
BracketSkuld.Effects.Bracketbracket, bracket_, finallyNone (combinator)Same
FreshSkuld.Effects.Freshfresh_uuidwith_uuid7_handler()with_test_handler()
RandomSkuld.Effects.Randomrandom, random_int, random_element, shufflewith_handler()with_seed_handler(seed:)
FxListSkuld.Effects.FxListfx_map, fx_reduce, fx_each, fx_filterNone (combinator)Same
CommandSkuld.Effects.Commandexecutewith_handler(comp, fn)Swap fn
TransactionSkuld.Effects.Transactiontransact, rollback, try_transactEcto.with_handler(repo) or Noop.with_handler(comp)Noop.with_handler(comp)

Coroutines & Concurrency

EffectModuleOperationsHandlerTest
YieldSkuld.Effects.Yieldyield, respond, collect, feedwith_handler(comp)Same
CoroutineSkuld.Coroutinenew, run/1,2, call/1,2, cancel/1,2Pure data wrapperSame
FiberPoolSkuld.Effects.FiberPoolfiber, await, await!, await_all, scope, mapwith_handler(comp)Same
ChannelSkuld.Effects.Channelnew, put, take, close, put_async, take_asyncwith_handler(comp)Same
BrookSkuld.Effects.Brookfrom_enum, map, filter, each, reduce, to_listNone (combinator)Same
ParallelSkuld.Effects.Parallelall, race, mapwith_handler(comp)with_sequential_handler(comp)
AtomicStateSkuld.Effects.AtomicStateget, put, modify, caswith_agent_handler(comp, init)with_state_handler(comp, init)

Boundaries

ModulePurpose
PortDispatch effect. request/3, request!/3. with_handler(comp, %{mod => resolver})
Port.EffectfulFacadeTyped contracts via defcallback. __key__ helpers.
RepoBuilt-in DB contract. InMemory, Ecto, Stub handlers.
Adapteruse Skuld.Adapter, contract: M, impl: I, stack: &f/1
AsyncCoroutineCross-process coroutines. run/2,3, run_sync/2,3, cancel/1,2

Cross-cutting

ModulePurpose
EffectLoggerwith_logging, with_resume, mark_loop. Must be innermost handler.
SerializableCoroutinePause-serialize-resume workflows. new, get_log, serialize, deserialize.
Query.Contractdeffetch, with_executor, with_cached_executor. FiberPool dependency.
QueryBlockquery do blocks. Auto-batches independent fetches via dependency analysis.

Running

FunctionBehaviour
Comp.run(comp)Returns {result, env}. Suspends/throws returned as-is.
Comp.run!(comp)Raises on Throw or Suspend.

Comparisons

vs Mox/Mimic

For simple stubs, Mox and Skuld are comparable. Skuld's advantage is stateful call chains (reads-after-writes), deep orchestration (10+ calls), and property tests — reusable in-memory handlers replace ad-hoc per-test stubs.

vs GenStage/Flow

Brook is lighter-weight for effectful streaming within one process. GenStage is better for long-running production pipelines with process isolation.

vs Haskell effect systems

Skuld takes the evidence-passing approach and adapts it for a dynamic language. No type-level effect rows — runtime handler stacking instead. The single computation type with auto-lifting makes it practical without a type system.


< How It Works | Up: Under the Hood | Index