A Simple Explanation of Monad
Introduction
Based on the previous concepts of Functor and Applicative Functor:
- Functor transforms values inside the container via
.map, with results still in the container.
[2].map(x => x + 3) // [5]- Applicative allows applying “wrapped functions” to “wrapped values”, where computations happen within the wrapping.
Maybe.of(x => x + 3).ap(Maybe.of(2)) // Maybe(5)But what if the content returned by the wrapping is itself wrapped?
Maybe.of(2).map(x => Maybe.of(x + 3))// The result is Maybe(Maybe(5))What is Monad?
function Maybe(value) { return value == null ? Nothing() : Just(value);}
function Just(value) { return { map: (fn) => Maybe(fn(value)), flatMap: (fn) => fn(value), getOrElse: () => value, };}
function Nothing() { return { map: () => Nothing(), flatMap: () => Nothing(), getOrElse: (defaultValue) => defaultValue, };}Using flatMap (also called chain) will automatically flatten the wrapping after computation, allowing the computation to continue smoothly:
getUser(id) .flatMap(user => getProfile(user)) .flatMap(profile => getPosts(profile))Example with arrays:
const arr = [1, 2, 1];const result = arr.flatMap((num) => (num === 2 ? [2, 2] : 1));
console.log(result);// Expected output: Array [1, 2, 2, 1]Monad Definition
-
Left Identity
// Inserting a value into the container and immediately flatMapping a function// is equivalent to directly passing that value to the functionMonad.of(a).flatMap(f) ≡ f(a)// Exampleconst f = x => Maybe.of(x + 1)Maybe.of(2).flatMap(f) // => Maybe(3)f(2) // => Maybe(3) -
Right Identity
// FlatMapping a Monad with of// is equivalent to doing nothing, indicating that flatMap does not break the original structure.m.flatMap(Monad.of) ≡ m// ExampleMaybe.of(2).flatMap(Maybe.of) // => Maybe(2)Maybe.of(2) // => Maybe(2) -
Associativity
// Consecutively flatMapping two functions// is equivalent to flatMapping a composed function// This guarantees that regardless of how you place parentheses in nested links, the result remains consistent.m.flatMap(f).flatMap(g)≡m.flatMap(x => f(x).flatMap(g))// Exampleconst f = x => Maybe.of(x + 1)const g = x => Maybe.of(x * 2)Maybe.of(2).flatMap(f).flatMap(g) // => Maybe(6)Maybe.of(2).flatMap(x => f(x).flatMap(g)) // => Maybe(6)
Conclusion
Monad ⊇ Applicative ⊇ Functor
- Monad is a type of Applicative and Functor
- Applicative is a type of Functor
In development, flatMap is more powerful than ap (it can do everything Applicative can do and more), so it is not necessary to implement ap.