Skip to main content

4. Use Handle to model Effects

· 2 min read

Status

Accepted

Context

Given we are structuring Hydra node as a reactive core we need a way to ensure a strict separation of pure and impure (or effectful) code.

We want to be able to test those impure/effectful parts of the code. This requires a means for exchanging the actual implementation for e.g. the function to send messages over a network.

Also we want the ability to swap implementations not only for testing, but also be able to accommodate different usage scenarios, e.g. use a different middleware depending on peer configuration.

In Haskell there are various common patterns to model effects:

  • Tagless final encoding also known as MTL-style although using typeclasses to implement is not necessary, whereby Effect(s) are expressed as typeclass(es) which are propagated as constraints
  • Free monads, or any variant thereof like Eff, freer, extensible-effects, whereby effect(s) are expressed as ADTs which are interpreted in the context of an Effect stack
  • Handle pattern also known as record-of-functions whereby effects are grouped together in a datatype with a single record constructor

(These tradeoffs also appear in other functional languages like F#)

There is not one most favored solution though and we all have various experiences with these techniques.

Decision

Effectful components of the Hydra node (our code) will be defined using the Handle pattern.

There might be other techniques in use because of libraries used etc.

Consequences

For example, the network component is defined as:

newtype Network m = Network
{ broadcast :: MonadThrow m => HydraMessage -> m ()
}

There might be multiple createNetwork :: m (Network m) functions