Functional programming using Functors, Monads and Promises
Published: 2024-04-18 18:15:10

Functional programming is a programming paradigm that relies on the use of pure functions and immutable data to write cleaner, more readable, and easier-to-reason-about code. In this approach, Functors, Monads, and Promises are key concepts that allow us to efficiently work with asynchronous functions and data in JavaScript.

**Functors:**
Functors are data structures that can be mapped over with a function. In simple terms, a Functor is like a container that can be transformed by applying a function to each of its elements. In JavaScript, arrays are a common example of Functors, as we can use the map() method to apply a function to each element of the array.

**Monads:**
Monads are a powerful abstraction that allows us to work with asynchronous operations, handle errors, and sequence operations in a controlled manner. Essentially, a Monad is a container that encapsulates a value and provides us with a way to apply functions to that value safely and comprehensively. In JavaScript, Promises are a common example of Monads, as they allow us to handle asynchronous operations elegantly and avoid excessive callback nesting.

**Promises:**
Promises are objects that represent the eventual result (or failure) of an asynchronous operation. Promises allow us to perform asynchronous operations in a cleaner and more understandable way, avoiding the callback hell and facilitating the management of asynchronous data flows. In JavaScript, Promises are widely used for making HTTP requests, accessing databases, and other asynchronous operations.

In summary, Functional Programming using Functors, Monads, and Promises provides powerful tools for writing cleaner, more readable, and more efficient code in JavaScript, allowing us to work more effectively with asynchronous functions and data.


To better understand how it works, let's consider a scenario where we need to process a list of student scores asynchronously. For each student, we want to calculate their grade based on the score and add a comment to it. To achieve this, we'll utilize functors to map functions over the list of students, monads to chain the operations, and promises to handle the asynchronous nature of the operations.

First, we define a list of students that we want to process:

```
// List of objects representing students
const students = [
  { name: "Alice", score: 85 },
  { name: "Bob", score: 70 },
  { name: "Charlie", score: 95 }
];
```
Then, we define two functions that act as functors. Each function takes a number as input and returns a promise that resolves to the result of a specific operation:

```
// Function to calculate the grade of a student (Functor)
const calculateGrade = student => {
  if (student.score >= 90) {
    return Promise.resolve({ ...student, grade: "A" });
  } else if (student.score >= 80) {
    return Promise.resolve({ ...student, grade: "B" });
  } else if (student.score >= 70) {
    return Promise.resolve({ ...student, grade: "C" });
  } else {
    return Promise.resolve({ ...student, grade: "N/A" });
  }
};

// Function to add a comment to the grade of a student (Functor)
const addComment = student => {
  if (student.grade === "A") {
    return Promise.resolve({ ...student, comment: "¡Excelente trabajo!" });
  } else if (student.grade === "B") {
    return Promise.resolve({ ...student, comment: "¡Buen trabajo!" });
  } else if (student.grade === "C") {
    return Promise.resolve({ ...student, comment: "¡Sigue así!" });
  } else {
    return Promise.resolve({ ...student, comment: "Necesita mejorar." });
  }
};

```
Next, we use the map() method on the list of students to apply these functions to each student asynchronously. This creates a new array of promises representing the results of the operations:

```
// Chaining operations with monads and promises
Promise.all(
  students.map(student => calculateGrade(student).then(addComment))
)
```
Finally, we use Promise.all() to wait for all promises to resolve and get the results of the operations. These results are then printed to the console:

```
.then(results => {
  console.log("Results:", results);
})
.catch(error => {
  console.error("Error:", error);
});
```
Output:

```
Results: [
  { name: 'Alice', score: 85, grade: 'B', comment: '¡Buen trabajo!' },
  { name: 'Bob', score: 70, grade: 'C', comment: '¡Sigue así!' },
  { name: 'Charlie', score: 95, grade: 'A', comment: '¡Excelente trabajo!'
  }
]
```