francesco agati

Posted on

# Introduction to Functional Programming in JavaScript: Different monads #11

Monads are a fundamental concept in functional programming that provide a way to handle computations and data transformations in a structured manner. There are various types of monads, each designed to solve specific problems and handle different kinds of data and effects.

#### What is a Monad?

A monad is an abstraction that allows for the chaining of operations on wrapped values. It is defined by three primary properties:

1. Unit (also called `of` or `return`): A function that takes a value and wraps it in a monad.
2. Bind (also called `flatMap` or `chain`): A function that takes a monadic value and a function that returns a monad, applies the function to the wrapped value, and returns a new monad.
3. Associativity: The composition of monadic operations should be associative.

#### 1. Maybe Monad

The Maybe Monad is used to handle optional values. It represents a computation that might fail or return `null` or `undefined`.

##### Implementation
``````class Maybe {
constructor(value) {
this.value = value;
}

static of(value) {
return new Maybe(value);
}

isNothing() {
return this.value === null || this.value === undefined;
}

map(fn) {
return this.isNothing() ? this : Maybe.of(fn(this.value));
}

flatMap(fn) {
return this.isNothing() ? this : fn(this.value);
}
}

// Usage
const maybeValue = Maybe.of('hello')
.map(str => str.toUpperCase())
.flatMap(str => Maybe.of(`\${str} WORLD`));
console.log(maybeValue); // Maybe { value: 'HELLO WORLD' }
``````

#### 2. Either Monad

The Either Monad is used to handle computations that can return either a success value (`Right`) or an error value (`Left`).

##### Implementation
``````class Either {
constructor(value, isRight = true) {
this.value = value;
this.isRight = isRight;
}

static right(value) {
return new Either(value, true);
}

static left(value) {
return new Either(value, false);
}

map(fn) {
return this.isRight ? Either.right(fn(this.value)) : this;
}

flatMap(fn) {
return this.isRight ? fn(this.value) : this;
}
}

// Usage
const rightValue = Either.right(5)
.map(x => x + 1)
.flatMap(x => Either.right(x * 2));
console.log(rightValue); // Either { value: 12, isRight: true }

const leftValue = Either.left('error')
.map(x => x + 1)
.flatMap(x => Either.right(x * 2));
console.log(leftValue); // Either { value: 'error', isRight: false }
``````

#### 3. Promise Monad

The Promise Monad is used to handle asynchronous computations.

##### Usage
``````const fetchData = url => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Data from \${url}`);
}, 1000);
});
};

// Usage
fetchData('https://api.example.com')
.then(data => {
console.log(data); // 'Data from https://api.example.com'
return fetchData('https://api.example.com/2');
})
.then(data => {
console.log(data); // 'Data from https://api.example.com/2'
})
.catch(error => {
console.error(error);
});
``````

#### 4. List Monad

The List Monad is used to handle computations that produce a list of values.

##### Implementation
``````class List {
constructor(values) {
this.values = values;
}

static of(values) {
return new List(values);
}

map(fn) {
return List.of(this.values.map(fn));
}

flatMap(fn) {
return List.of(this.values.flatMap(value => fn(value).values));
}
}

// Usage
const list = List.of([1, 2, 3])
.map(x => x + 1)
.flatMap(x => List.of([x, x * 2]));
console.log(list); // List { values: [ 2, 4, 3, 6, 4, 8 ] }
``````

The Reader Monad is used to handle computations that depend on some shared environment or configuration.

##### Implementation
``````class Reader {
constructor(fn) {
this.fn = fn;
}

static of(value) {
return new Reader(() => value);
}

map(fn) {
return new Reader(env => fn(this.fn(env)));
}

flatMap(fn) {
return new Reader(env => fn(this.fn(env)).fn(env));
}

run(env) {
return this.fn(env);
}
}

// Usage
const config = { baseURL: 'https://api.example.com' };

const fetchUser = new Reader(env => `\${env.baseURL}/user`);
const fetchPosts = new Reader(env => `\${env.baseURL}/posts`);

const fetchUserAndPosts = fetchUser.flatMap(userURL =>
fetchPosts.map(postsURL => ({ userURL, postsURL }))
);

console.log(fetchUserAndPosts.run(config));
// { userURL: 'https://api.example.com/user', postsURL: 'https://api.example.com/posts' }
``````

#### 6. Writer Monad

The Writer Monad is used to handle computations that produce a value along with a log or additional data.

##### Implementation
``````class Writer {
constructor(value, log) {
this.value = value;
this.log = log;
}

static of(value) {
return new Writer(value, '');
}

map(fn) {
const result = fn(this.value);
return new Writer(result.value, this.log + result.log);
}

flatMap(fn) {
const result = fn(this.value);
return new Writer(result.value, this.log + result.log);
}

tell(log) {
return new Writer(this.value, this.log + log);
}
}

// Usage
const writer = Writer.of(3)
.map(value => new Writer(value + 1, 'Incremented\n'))
.flatMap(value => new Writer(value * 2, 'Doubled\n'));

console.log(writer);
// Writer { value: 8, log: 'Incremented\nDoubled\n' }
``````

#### 7. State Monad

The State Monad is used to handle computations that maintain state.

##### Implementation
``````class State {
constructor(runState) {
this.runState = runState;
}

static of(value) {
return new State(state => [value, state]);
}

map(fn) {
return new State(state => {
const [value, newState] = this.runState(state);
return [fn(value), newState];
});
}

flatMap(fn) {
return new State(state => {
const [value, newState] = this.runState(state);
return fn(value).runState(newState);
});
}

run(initialState) {
return this.runState(initialState);
}
}

// Usage
const increment = new State(state => [state + 1, state + 1]);

const result = increment
.flatMap(() => increment)
.flatMap(() => increment)
.run(0);

console.log(result); // [3, 3]
``````

#### Conclusion

Monads provide a structured and predictable way to handle computations and data transformations in functional programming. Each type of monad serves a specific purpose, from handling optional values with the Maybe Monad to managing asynchronous operations with the Promise Monad.