Reader Monad Explored
- 3 minsThe Reader
Monad (also referred to as the Environment monad) is commonly used to thread configuration through application code to call sites that depend on it. It offers a form of dependency injection.
Ex. Scala
Ex. JavaScript via the excellent lib monet
You’ve probably noticed that in both examples we’re using the Reader monad for threading state below the top-level entry point to our application. At the top level, we rely upon ordinary functions for passing dependencies.
At this point you might be wondering why we bother to use the Reader
instead of a simple function. That seems to work well enough for the applications top-level, so why not for the remainder of the application?
Well, Reader
is not without its problems.
Downsides:
Each function of a module (.e.g, DAO) replicates the Reader monad signature. This is fine for small modules, but becomes tiresome and error prone as the module size increases. Refactoring becomes a pain if the Reader
signature changes as well.
The advantage of being able to write new functions in terms of existing functions that return a Reader
turns out to not come up that often in my experience. I have used it a few times, but I’m not sure that I gained a ton from its use.
The well-known awkwardness of dealing with stacked monads (in the absence of Monad Transformers) receives an additional monad on the stack. It would not be unusual for a DAO
module to have a function with the signature id => Reader[Env, Future[Either[Error, Maybe[Model]]]]
. This is unwieldy.
In JavaScript, the Promise/A+ specification does not conform to the monad contract, so one has to reach for a library with a conformant implementation.
After using this pattern in several applications, I’ve decided that while it’s a nice tool to have in your belt, other methods for dependency injection are better suited to most use cases.
I’ve largely moved to using functions and constructors for injecting dependencies in JavaScript for medium to large applications.