Skip to main content

5. Use io-classes

· 2 min read




Although we try to contain the use of IO at the outskirt of the Hydra node using Handle pattern and Reactive core, low-level effects are still needed in various places, notably to define concurrently executing actions, and thus need to be tested

Testing asynchronous and concurrent code is notoriously painful

The ouroboros consensus test suite and hydra-sim simulation have demonstrated the effectiveness of abstracting concurrent primitives through the use of typeclasses (MTL-style pattern) and being able to run these as pure code, harvesting and analysing produced execution traces.

There are other such libraries, e.g. concurrency and dejafu, as well as the venerable exceptions (for abstracting exception throwing).


For all IO effects covered by the library, use functions from typeclasses exposed by io-classes. As of this writing, this covers:

  • All STM operations through MonadSTM
  • Time and timers through MonadTime and MonadTimer
  • Concurrency through MonadAsync, MonadFork
  • Exceptions through MonadThrow, MonadCatch and MonadMask


We can use io-sim to evaluate IO-ish functions easily

Instantiation to concrete IO is pushed at the outermost layer, eg. in the Main or tests.

As some of these functions and typeclasses clash with the cardano-prelude we might want to define a custom prelude (candidate for another ADR)