ReaderWriterState monad in action

Alexander Zaidel
2 min readOct 24, 2018

--

Reader, Writer, State monads have never been so easy to use in one place with ReaderWriterState monad.

There is IndexedReaderWriterStateT which is implemented in Cats with the type alias ReaderWriterStateT — monad transformer for ReaderWriterState (a bit complicated though). Let’s take a closer look:

To get a good feeling how ReaderWriterStateT works, why don’t we implement a function getUser(uuid: String): IO[Option[User]], which makes an http call, stores result into a cache with a bit of logging.

Cache will be an immutable data structure:

To align with the type parameters for ReaderWriterStateT we should take into account that the context of computation is IO, environment is Environment, log is Log(type alias for List[String]), state is Cache[UUID, User] and result of computation is Option[User]:

ReaderWriterStateT[IO, Environment, Log, Cache[UUID, User], Option[User]]

One of the ways it can be constructed is by using the function:

(Environment, Cache[UUID, User]) => IO[(Log, Cache[UUID, User], Option[User])]

So the body (and I know that it’s ugly) of getUser would look like:

And logic is pretty straightforward: get httpClient from the environment, make an http call. If user was found then return Tuple3: log entry, updated cache and Some(user), otherwise — Tuple3: log entry, cache and None.

The usage is as simple as:

One thing to mention — we have to supply an instance of Environment and an initial state of Cache[UUID, User] in order to run the ReaderWriterStateT.

Once getUser is implemented, our example can be extended with a recursive function preCacheUsers, which will precache users from the list of user UUIDs:

Such function can be used as:

Last but not least it’s worth to take a look at utility methods of ReaderWriterStateT:

ReaderWriterStateT.tell - Add a value to the log, without modifying the input state.
ReaderWriterStateT.ask - Get the provided environment, without modifying the input state.
ReaderWriterStateT.get - Return the input state without modifying it.
ReaderWriterStateT.set - Set the state.

To sum up - ReaderWriterState is an easy way to combine Reader, Writer, State monads together, because no lifting is required, it’s performance overhead is much less and the reason is the same — no lifting of State to Writer to Reader.

--

--