MTL is a popular approach to writing applications in Haskell. Monad
constraints provide capabilities such as error handling (MonadError
), writable state (MonadState
), and environmental context (MonadReader
). An application typically has one monad stack, implemented as a monad transformer.
However, a common problem with MTL is that the capabilities are global and the requirements of individual components may conflict. For example, an HTTP component may require a MonadError ServantError
whereas a DB component may require something else. A typical workaround is to introduce a monolithic error ADT, and an unfortunate level of coupling. The problem repeats for other capabilities.
In this post, we will demonstrate a way to encode capabilities that remain local to a single component. Our example builds on the Servant Tutorial and will also show how Servant client endpoints can be mocked out for unit testing. This post is a transcript