Nest.JS | Monads -> List

The List monad, also known as the Array monad, is a monad that is used to represent computations that can have multiple possible outcomes. It is a monad that wraps an array and allows you to use the monadic methods such as map, chain, flatMap, etc. on the array.

The List monad is typically implemented as an object that has a single method flatMap which applies a function to each element of an array and concatenates the results.

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

class List {
  constructor(values) {
    this.values = values;
  }
  flatMap(f) {
    return new List(this.values.reduce((acc, x) => acc.concat(f(x)), []));
  }
}

const list = new List([1, 2, 3]);
const result = list.flatMap(x => [x * 2, x * 3]);
console.log(result.values); // [2, 3, 4, 6, 6, 9]

In this example, the List class takes an array in its constructor and has a method flatMap that allows you to apply a function to each element of the array, and concatenate the results.

For example, you could create a List that wraps an array of numbers, and then use the flatMap method to apply a function to each element of the array, and concatenate the results. You could then chain multiple of these computations together to create a more complex computation.


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

import { Injectable } from '@nestjs/common';
import { List } from 'fp-ts/lib/List';

@Injectable()
export class MyService {
  public async getUsers(): Promise<List<User>> {
    const users = await this.userRepository.find();
    return List.fromArray(users);
  }

  public async getUserById(id: string): Promise<List<User>> {
    const user = await this.userRepository.findOne(id);
    return List.of(user);
  }
}

In this example, the MyService class has two methods getUsers() and getUserById(id:string) that returns an instance of the List Monad which contains an array of users.

You can chain multiple computations using the .map(), .filter(), .reduce() methods.

  myService.getUsers()
  .map(users => users.filter(user => user.age > 18))
  .map(users => users.map(user => user.name))
  .map(users => users.reduce((acc, curr) => acc + ', ' + curr))
  .fold(error => console.log(error), result => console.log(result))

In this example, it filters the users that have age greater than 18, then maps the names of the remaining users, and finally, concatenates all the names using reduce method.

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.