Composing readers
Tags: functional
Published: 2021-02-03 11:01:18

---
title: Composing readers
published: true
description: How readers can be used to progressively enrich immutable contexts
tags: functional
cover_image: https://images.unsplash.com/photo-1491309055486-24ae511c15c7?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80
---

> This article is written using PureScript code snippets, but hopefully the learnings are valuable anywhere!

In a lot of apps, there is some sort of global immutable state that is accessed and used to decide what to present.  There are two "classic" ways to represent this.

- As a `Function`
- Using a `Reader` monad

The two are conceptually very similar, but I find myself increasingly using the `Reader` monad.  In this article, I'll show how both can be used and why I'm a fan of `Reader` these days.

# Function vs Reader - the basics

Let's create an app that reads a number `age` from a global immutable state and produces a boolean. Using a function, that app could look like this:

```haskell
app { age } = age > 18
app { age: 19 } -- true
app { age: 17 } -- false
```

Using a `Reader`, the app could look like this:

```haskell
app = ask <#> \{ age } -> age > 18
runReader app { age: 19 } -- true
runReader app { age: 17 } -- false
```

They're not that different: one is a function to which immutable arguments are passed, and one is a context in which immutable arguments are read.

# Composition

When working with an environment or state, one big reason that functions are used instead of readers is because they can be composed.  For example, let's say that we want to enrich our state with an extra variable `ageAsString` before it reaches our interpreting function.  We'll also modify the interpreting function to use both variables.  Using composition, that would look like this:

```haskell
addStringRep { age } = { age, ageAsString: show age }

app =
  addStringRep
    >>> \{ age, ageAsString } -> show age == ageAsString

app { age: 19 } -- true
app { age: 17 } -- true
```

The signature of app hasn't changed: it is still `{ age :: Int } -> Boolean`. What's changed is that we have squished function composition in there so that the internals contain `{ age :: Int } -> { age :: Int, ageAsString :: String }` composed with `{ age :: Int, ageAsString :: String } -> Boolean`.

With a little elbow grease, readers can be composed as well.

```haskell
composeReadersFlipped ::
  forall a b c. Reader b c -> Reader a b -> Reader a c
composeReadersFlipped = map <<< runReader

composeReaders ::
  forall a b c. Reader a b -> Reader b c -> Reader a c
composeReaders = flip composeReadersFlipped

infixr 9 composeReadersFlipped as <|<

infixr 9 composeReaders as >|>
```

And now, we can do `addStringRep` in `Reader`-land.

```haskell
addStringRep = ask <#> \{ age } -> { age, ageAsString: show age }

app =
  addStringRep
    >|> ask
    <#> \{ age, ageAsString } -> show age == ageAsString

runReader app { age: 19 } -- true
runReader app { age: 17 } -- true
```

# Where the reader wins

So far, the two syntaxes - `Function` and `Reader` - have been more or less equivalent. If anything, function is easier to work with because it is shorter and perhaps more intuitive.

However, where `Reader` _really shines_ is the way that you can interrupt composition to inspect the environment at any time.

Let's now create a new (more-than-slightly contrived) example that composes three functions before the final one.

```haskell
addAgePlus1 { age } = { age, agePlus1: age + 1 }
addAgePlus2 { age, agePlus1 } =
  { age, agePlus1, agePlus2: age + 2 }
addAgePlus3 { age, agePlus1, agePlus2 } =
  { age, agePlus1, agePlus2, agePlus3: age + 3 }

app =
  addAgePlus1
    >>> addAgePlus2
    >>> addAgePlus3
    >>> \{ age, agePlus1, agePlus2, agePlus3 } ->
          age + agePlus1 + agePlus2 + agePlus3 == 4 * age + 7

app { age: 19 } -- true
app { age: 17 } -- true
```

And in `Reader`-land.

```haskell
addAgePlus1 = ask <#> \{ age } -> { age, agePlus1: age + 1 }

addAgePlus2 =
  ask
    <#> \{ age, agePlus1 } ->
        { age, agePlus1, agePlus2: age + 2 }

addAgePlus3 =
  ask
    <#> \{ age, agePlus1, agePlus2 } ->
        { age, agePlus1, agePlus2, agePlus3: age + 3 }

app =
  addAgePlus1
    >|> addAgePlus2
    >|> addAgePlus3
    >|> ask
    <#> \{ age, agePlus1, agePlus2, agePlus3 } ->
        age + agePlus1 + agePlus2 + agePlus3 == 4 * age + 7

runReader app { age: 19 } -- true
runReader app { age: 17 } -- true
```

What if we want to terminate early if `addAgePlus2` is greater than 55?

In the function version, it gets pretty clunky.

```haskell
addAgePlus1 { age } = { age, agePlus1: age + 1 }

addAgePlus2 { age, agePlus1 } = { age, agePlus1, agePlus2: age + 2 }

addAgePlus3 { age, agePlus1, agePlus2 } = { age, agePlus1, agePlus2, agePlus3: age + 3 }

app =
  addAgePlus1
    >>> addAgePlus2
    >>> \env@{ agePlus2 } ->
        if agePlus2 > 55 then
          false
        else
          env
            # ( addAgePlus3
                  >>> \{ age, agePlus1, agePlus2, agePlus3 } ->
                      age + agePlus1 + agePlus2 + agePlus3 == 4 * age + 7
              )

app { age: 19 } -- true
app { age: 100 } -- false
```

Every time we interrupt the composition to introspect the environment, we have to pass the environment `env` to the continuation of the composition.  That leads to at least three problems:

1. The code becomes harder to follow.
1. It makes it hard to refactor because we now have an extra `env` to carry around if we want to move parts of this out.
1. There is a chance, in complex projects, that we may accidentally modify `env` before passing it along, which breaks the abstraction where the `withX` functions augment the environment.

On the other hand, using readers, this problem goes away:

```haskell
addAgePlus1 = ask <#> \{ age } -> { age, agePlus1: age + 1 }

addAgePlus2 =
  ask
    <#> \{ age, agePlus1 } ->
        { age, agePlus1, agePlus2: age + 2 }

addAgePlus3 =
  ask
    <#> \{ age, agePlus1, agePlus2 } ->
        { age, agePlus1, agePlus2, agePlus3: age + 3 }

app =
  addAgePlus1
    >|> addAgePlus2
    >|> do
        { agePlus2 } <- ask
        if agePlus2 > 55 then
          pure false
        else
          addAgePlus3
            >|> ask
            <#> \{ age, agePlus1, agePlus2, agePlus3 } ->
                age + agePlus1 + agePlus2 + agePlus3 == 4 * age + 7

runReader app { age: 19 } -- true
runReader app { age: 100 } -- false
```

The code interrupts the sequence to ask a question (is `agePlus2` greater than 55?) and then continues without allowing us to touch the original environment passed to `addAgePlus3`.  This is one of the classic advantages of monads: they allow for computations to bifurcate using `bind` (or `do`).

# Conclusion

The reader monad and functions are very similar. In a lot of category theory texts, they're treated as the same thing.

The virtue of reader monads has been touted mostly because it can be part of a monadic stack using a library like `mtl`, which means that a monad can take on the quality of being a reader in addition to other qualities (like being a writer or allowing for exceptions).  However, the usefulness of composing readers has been IMHO undervalued.  Treating reader composition like function composition with the "magical" possibility to interrupt the composition anywhere in the chain, look at what's going, and keep going is a great reason to use readers.

If you're not using PureScript, fear not!  Here are some other great reader monads:

- [Reader](https://dev.to/gcanti/getting-started-with-fp-ts-reader-1ie5) in TypeScript
- [Reader](https://medium.com/rahasak/dependency-injection-with-reader-monad-in-scala-fe05b29e04dd) in Scala

Have fun with readers!