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