Categorisation of monads

Most of the monads I can think of belong in one or both of these categories:

State

Can be modelled as a state monad

  • Reader
  • Writer
  • State
  • ST
  • STM
  • function (((->) r))
  • Identity (state with no state)

Error

Sequencing with early exit

  • Maybe
  • Either
  • ErrorT

Both

  • IO (with the real world as state)
  • parser combinators

The only (useful) exception I can think of, whose functionality is clearly distinct from both these categories, is List (and Cont, which is merely a generalization of Monad itself).

Are there any other monads, that can’t be modelled exclusively by either category, that I have missed?

2 Likes

Logic is another special monad related to Cont and lists.

I would classify the errors under ā€œNon-determinismā€ together with List, because early exit is the same as an empty list of branches.

2 Likes

I see your point. By error, I mean ā€˜just perform regular pure (or stateful) computation, unless stopped by some exceptional condition,’ i.e. only carrying a single (optional) value. List does more than that, in that it applies computation to multiple items, so it can’t be ā€˜classed’ with the error monads, under my definition.

My objective is to provide a perspective on monads from a practical (maybe application) programmer’s perspective. I’m musing that maybe monads are over-abstracted, and it’s possible to define an easier-to-reason-about abstraction based on the concepts of state and early exit for practical programming.

For me personally, I don’t even find the list monad useful - i find it obfuscates more than writing the code explicitly, and doesn’t really give any helpful intuitions. I’d be curious to hear if someone has a different perspective.

This reminds me a little bit about the division between data and control functors in Linear Haskell.

3 Likes

That’s a good insight. I’ll have to think long and hard about that.

I’d argue that monad transformers are really useful but also very clunky without MTL/some effect system, either of which need abstract monads. Being able to mix state+non-determinism or streaming+async+resource management seems a lot harder without abstract monads.

For extra monad types: There is a (maybe a bit underused) pattern of profunctor monads that can do bidirectional things, basically a question-answer free monad

data QA query answer answer' = Done answer' | Ask query (answer -> QA query answer answer') | ...

This is useful if you need bidirectional things like parse/pretty print, query/update, etc. The refinery library uses it, but I’m pretty sure I’ve seen it in some serialization libraries as well.

There are also variants on a bunch of monads like adding codensity on top or tricks like ā€˜a smart view in datatypes’ which can really help performance and for which an abstract view on monads can help.

1 Like