Composing functions with Reader monad

Alexander Zaidel
2 min readSep 15, 2019

--

Today we will learn what is a Reader Monad and how to use it.
Reader Monad can be extracted from any code which uses constructor injection in 6 steps. Let’s take a look at UserRepository code snippet:

Step 1 — move ConnectionPool as input parameter to createUser function:

Step 2 — make createUser curried:

Step 3 — change return type of createUser from User to function ConnectionPool => User:

Step 4 — create a wrapper for the return type of createUser let’s call it Reader:

Step 5 — generalize Reader with type parameters:

Step 6 — add map and flatMap to the Reader:

And here you are — Reader Monad.

Think of A as an Enviroment — place where you keep your dependencies, in the previous example it was ConnectionPool but could be something like:
case class Environment(userService: UserService, httpClient: Client)

Luckily cats library provides Reader Monad out of the box.

The main purpose of the Reader monad is to compose functions and delay dependency injection phase until the later moment:

Combining Reader together with other monads requires to write a bit of boilerplate:

And ReaderT is here to help to reduce the boilerplate:

Reader Monad can also be used for passing a Diagnostic Context .
Image that you have to add requestId to every log message in your code base, thus you are forced to pass requestId in every function. While with the Reader Monad information about Diagnostic Context will be embedded in return type.

For composing Readers with different environments take a look at the next example, the idea is to align environments using local:

That’s it for today.

--

--