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