What should and shouldn't be handled with Reader
Tags: reader, monad, fpts
Published: 2020-02-24 10:45:54

---
title: What should and shouldn't be handled with Reader
published: true
description: a few thought on the Reader monad
tags: reader,monad, fp-ts
---

Recently I had the chance to discuss with a colleague (friend?) of mine about the Reader monad and why all the examples found in literature seem to always be about implicitly threading a `Config` object through your application.

I was frustrated to see every article resort to the same old example and felt there would surely be dozens of other use cases where you could fruitfully put `Reader` to use. This frustration led me to a few thoughts that I want to share with everyone in the hope that it may be helpful to other people naively approaching the subject (as I was).

First of all a little intro: the Reader monad in `fp-ts: 2.0` is nothing else than an alias of  the function profunctor,  its signature is:

```ts
interface Reader<R, A> {
  (r: R): A
}
```

In fact, this very simple (and powerful) structure allows us to define not only profunctor instances for it but also monadic ones! So feel free to chain on it like there's no tomorrow.

The way this abstraction is usually put into use in codebases is by creating a context that can be accessed at any level of your application without the need to thread it to all the places where it needs to be used:

```ts
// src/index.ts
type C = {
  i18n: [[k:string]: string],
  threads: 2,
  dbHost: string
}

const MyApp: Reader<C, unknown>= (c: C) => {...}

// src/repository/entities/arbitraryNesting/../index.ts
import { ask } from 'fp-ts/lib/Reader'

const getById = (id: string) => {
  const { dbHost } = ask<C>()
  ...
}
```

so what I was thinking is: why just configuration? Why don't we use `Reader` every time there is unnecessary chaining of parameters?

In this example (from which I stripped everything not strictly related to the point I am trying to make) I only pass `roles` to the function `getDocsByRoles` and `filter` is retrieved autonomously by `getDocsByRole` from the Reader context, thus avoiding defining it explicitly on `getDocsByRoles` API:

```ts
import { reader, Reader } from "fp-ts/lib/Reader";
import { array } from "fp-ts/lib/Array";


const getDocsByRole = (role: string) => (filter: string): any => {
  ...
}

const getDocsByRoles = (roles: string[]): Reader<string, any> => {
  const docs: Reader<string, any[]> = array.traverse(reader)(roles, getDocsByRole)
  return reader.chain(docs, fancyTransformation)
}

const getUserDocs = (request: DocsRequest) => {
  const roles = ...
  const filter = ...

  return getDocsByRoles(roles)(filter);
}
```

What I did in this example may look right but there is fundamental (and subtle IMHO) flaw: while we gained a little in conciseness by avoiding to chain down the `filter` parameter, we lost a lot in terms of readability of the API. In fact, there would be no working implementation of `getDocsByRoles` without a `filter` and its return value strictly depends on it: **although the function does not use it, it is a fundamental part of its ergonomics**.

This, in my opinion, is the reason why all the `Reader` examples you see around are ones involving App configuration and global contexts.

While db location, secrets, and even i18n are usually details of an app that can be safely hidden from the surface of your internal APIs, other runtime values are usually strictly tied to the behavior of your implementations and, therefore, should not be hidden. It is a matter of ergonomics.