Composing non-distributive monads in recursion-schemes

154 Views Asked by At

One of my favourite things about the recursion schemes in Haskell are the generalised morphisms (gcata etc.) that allow interleaving (co-)monadic computations with recursion, using a monad transformer library. For example, as described in this great blog post.

However, I've run into a problem; to be able to use these functions we need the (co-)monads to be (co-)sequentiable. Consider a type signature for gana:

gana : Monad m => (forall z . m (f z) -> f (m z)) -> (a -> f (m a)) -> a -> b

The first argument essentially says that m must have a sequence operator.

Unfortunately, I've found that in practice, there are monads which are not distributive. For example:

  • A monad representing a database transaction. When aborted, the transaction can be rolled back; if sequenced, it can only be rolled back to the point where it was sequenced.
  • A concurrency monad, representing a lock on a resource, or an atomic computation. When sequenced, the lock is lost momentarily.

In this case, it is still possible to write a specialised recursion scheme that interleaves the monadic execution; but you lose the ability to fuse it using a monad transformer. i.e. if you want to combine such non-distributive monads, they can not be fused using a transformer with the monad/comonad in the f-(co)algebra. Concretely, I couldn't use a monad transformer to combine a DBTransaction monad with an apomorphism (ExceptT/EitherT); I'd need to write a custom recursion-scheme from scratch.

My question is whether anyone has suggestions for working around this limitation.

0

There are 0 best solutions below