Nest.JS | Monads -> Reader

The Reader monad, also known as the “Environment monad,” is a functional programming concept that allows you to pass an environment, or a shared context, throughout a computation. It allows you to write code that is independent of the specific values of that context, making it more reusable and easier to test.

The Reader monad is often used to manage dependencies and configuration values in a functional programming language, such as JavaScript. The Reader monad allows you to describe a computation that depends on some external context in a pure, composable way, which makes it easier to reason about and test your code.

An Reader monad is typically implemented as an object that has a single method, called run, which takes the context as an input and returns the result of the computation. The Reader monad can be used to chain together multiple computations that depend on the same context and create more complex computations.

Here’s an example of the Reader monad in JavaScript:

class Reader {
  constructor(fn) {
    this.run = fn
  }
  map(f) {
    return new Reader(e => f(this.run(e)))
  }
  chain(f) {
    return new Reader(e => f(this.run(e)).run(e))
  }
  // ...
}

In this example, the Reader class takes a function fn in its constructor, and has methods run, map, and chain that allow you to pass the context, transform the result, and chain multiple computations together.

For example, you could create a Reader that takes a configuration object and returns a value based on that configuration. You could then chain multiple of these Readers together to create a more complex computation, without having to pass the configuration object through each step.

It’s important to note that the Reader Monad is not a common pattern in JavaScript, it’s mainly used in functional programming languages like Haskell, scala, etc.


Here’s an example of how you could use the Reader Monad in Nest.js:

import { Injectable } from '@nestjs/common';
import { Reader, reader } from 'fp-ts/lib/Reader';

@Injectable()
export class MyService {
  public incrementCounter(counter: number): Reader<number, number> {
    return reader.of(counter + 1);
  }

  public decrementCounter(counter: number): Reader<number, number> {
    return reader.of(counter - 1);
  }

  public getCounter(counter: number): Reader<number, number> {
    return reader.of(counter);
  }
}

In this example, the MyService class has three methods incrementCounter(), decrementCounter() and getCounter() that returns an instance of the Reader Monad.

The Reader Monad is used to abstract away the passing of configuration, dependencies or context throughout a program or a set of functions.

In this example, the incrementCounter() method increments the counter and returns a new value, the decrementCounter() method decrements the counter and returns a new value, the getCounter() method returns the current counter value.

You can chain multiple computations using the .map(), .chain(), .ap() methods.const program:

In this example, the program increments the counter by 1, decrements it again and returns the current counter value.

It’s important to note that this example is a basic one and doesn’t demonstrate the full power of Monads in functional programming. Monads can be used to handle various cases like error handling, chaining multiple computations and more, it’s best to understand the problem and use cases before applying monads.

Also, the run() method must be called in order to execute the Reader Monad and get the results, it takes the environment as an argument.