# Part 4: Reader and Writer Monads

In part 3 of this series, we finally tackled the conceptual idea of monads. We learned what they are, and saw how some common types like `IO`

and `Maybe`

work as monads. Now in this part, we’ll start looking at some other useful monads. In particular, we’ll consider the `Reader`

and `Writer`

monads.

If you haven’t started writing your own Haskell yet, you have all the tools and skills you need to do so! Just download our Beginners Checklist to learn how to get started! Writing your own code will help a lot with understanding the examples in these articles!

## Global Variables (or a lack thereof)

In Haskell, our code is generally “pure”, meaning functions can **only interact** with the **arguments passed** to them. This effectively means we cannot have global variables. We can have global expressions, but these are fixed at compile time. If user behavior might change them, we have to wrap them in the `IO`

monad, which means they can’t be used from pure code.

Consider this example. Here, we want to have an `Environment`

containing different parameters as a global variable. However, we might have to load these from a config file or a command line interface, which requires the `IO`

monad.

```
main :: IO ()
main = do
env <- loadEnv
let str = func1 env
print str
data Environment = Environment
{ param1 :: String
, param2 :: String
, param3 :: String }
loadEnv :: IO Environment
loadEnv = …
func1 :: Environment -> String
func1 env = “Result: “ ++ (show (func2 env))
func2 :: Environment -> Int
func2 env = 2 + floor (func3 env)
func3 :: Environment -> Float
func3 env = … -- Some calculation based on the environment
```

The only function **actually using the environment** is `func3`

. However `func3`

is an impure function. This means it cannot directly call `loadEnv`

, an impure function. This means the environment has to be passed through as a variable to the other functions, just so they can ultimately pass it to `func3`

. In a language with global variables, we could save `env`

as a global value in `main`

. Then `func3`

could access it directly. There would be no need to have it as a parameter to `func1`

and `func2`

. In larger programs, these “pass-through” variables can cause a lot of headaches.

## The Reader Solution

The Reader monad solves this problem. It effectively creates a global read-only value of a specified type. All functions within the monad can “read” the type. Let’s look at how the `Reader`

monad changes the shape of our code. Our functions **no longer need** the `Environment`

as an **explicit parameter**, as they can access it through the monad.

```
main :: IO ()
main = do
env <- loadEnv
let str = runReader func1 env
print str
data Environment = Environment
{ param1 :: String
, param2 :: String
, param3 :: String }
loadEnv :: IO Environment
loadEnv = …
func1 :: Reader Environment String
func1 = do
res <- func2
return (“Result: “ ++ (show res))
func2 :: Reader Environment Int
func2 = do
env <- ask
let res3 = func3 env
return (2 + (floor res3))
func3 :: Environment -> Float
...
```

The `ask`

function **unwraps the environment** so we can use it. The monad’s bind action allows us to glue different `Reader`

actions together together. In order to call a reader action from pure code, all we need to do is call the `runReader`

function and supply the environment as a parameter. All functions within the action will be able to treat it like a global variable.

It might not seem like we’ve accomplished much, but our code is much more intuitive now. We keep `func3`

as it was. It makes sense to describe it as a function from an `Environment`

to a value. However, our other two functions no longer take the environment as an explicit parameter. They simply exist in a **context** where the **environment is a global variable**.

## Accumulating Values

Now, to motivate the Writer monad, let’s talk about the accumulation problem. Suppose we have a few different functions. Each will perform some string operations we’ve assigned an arbitrary “cost” to. We want to keep track of how “expensive” it was to run the full computation. We can do this by using **accumulator arguments** to keep track of the cost we’ve seen so far. We then keep passing the accumulated value along.

```
-- Calls func2 if even length, func3 and func4 if odd
func1 :: String -> (Int, String)
func1 input = if length input `mod` 2 == 0
then func2 (0, input)
else (i1 + i2, str1 ++ str2)
where
(i1, str1) = func3 (0, tail input)
(i2, str2) = func4 (0, take 1 input)
-- Calls func4 on truncated version
func2 :: (Int, String) -> (Int, String)
func2 (prev, input) = if (length input) > 10
then func4 (prev + 1, take 9 input)
else (10, input)
-- Calls func2 on expanded version if a multiple of 3
func3 :: (Int, String) -> (Int, String)
func3 (prev, input) = if (length input) `mod` 3 == 0
then (prev + f2resI + 3, f2resStr)
else (prev + 1, tail input)
where
(f2resI, f2resStr) = func2 (prev, input ++ "ab")
func4 :: (Int, String) -> (Int, String)
func4 (prev, input) = if (length input) < 10
then (prev + length input, input ++ input)
else (prev + 5, take 5 input)
```

However, an `Int`

isn’t the only type of value we could accumulate. We could instead be accumulating a list of strings to print as log messages so we know what computations were run. There is a **generalization of this behavior**: the `Monoid`

typeclass.

## The Monoid Typeclass

In this example, `Int`

is a simple example of a Monoid. Let’s look at the monoid typeclass definition:

```
class Monoid a where
mempty :: a
mappend :: a -> a -> a
```

This is effectively an **accumulation class**. It defines two functions. The `mempty`

function is an initial value for our monoid. Then with `mappend`

, we can combine two values of this type into a result. It is quite easy to how we can make a monoid instance for `Int`

:

```
instance Monoid Int where
memty = 0
mappend a b = a + b
```

Our accumulator starts at 0, and we combine values by adding them.

## Using Writer to Track the Accumulator

The `Writer`

monad is parameterized by some monoidal type. Its main job is to keep track of an accumulated value of this type. So it’s operations live in the context of having a global value that they can modify in this particular way. We can change our code examples above to use the `Writer`

monad as follows:

```
func1 :: String -> (String, Int)
func1 input = if length input `mod` 2 == 0
then runWriter (func2 input)
else runWriter $ do
str1 <- func3 input
str2 <- func4 (take 1 input)
return (str1 ++ str2)
func2 :: String -> Writer Int String
func2 input = if (length input) > 10
then do
tell 1
func4 (take 9 input)
else do
tell 10
return input
func3 :: String -> Writer Int String
func3 input = if (length input) `mod` 3 == 0
then do
tell 3
func2 (input ++ “ab”)
else do
tell 1
return $ tail input
func4 :: String -> Writer Int String
func4 input = if (length input) < 10
then do
tell (length input)
return (input ++ input)
else do
tell 5
return (take 5 input)
```

Notice we no longer need to actually **explicitly keep track** of the accumulator. It is now wrapped by the `Writer`

monad. We can increase it in any of our functions by calling “tell”. Now our code is much simpler and our types are cleaner.

## Conclusion

Now that we know about the Reader and Writer monads, it’s time to move on to part 5. There, we’ll discuss the State monad. This monad combines these two concepts into a read/write state, essentially allowing the full privilege of a global variable. If these concepts were still a little confusing, don’t be afraid to take another look at part 3 to solidify your understanding of monads.

Maybe you’ve never programmed in Haskell before, and the realization that we can get all this cool functionality makes you want to try it! Download our Beginners Checklist to get started!