In functional programming, a monad is a design pattern that allows structuring programs generically while automating away boilerplate code needed by the program logic.
In a strongly typed liked language like Scala, with built-in types like the Option
type, you are very likely
to have already used or will eventually end up using monads without even knowing you are using them. This project
is my attempt at understanding monads by writing them, and I decided to use TypeScript because it forces me to
build monads from "scratch", but with support for strong typing.
npm i monads-typescript
Import the appropriate static methods from the library like so:
import { left, right } from 'monads-typescript';
import { maybe } from 'monads-typescript';
...
The first monad, the Maybe
monad, is based on Scala's
Option
type. It's very useful when you want
to encapsulate computation on values that might be null
or undefined
.
Here's an example of some code you might find yourself writing in JavaScript/TypeScript:
const greetUser = (userId) => {
const user = getUser(userId);
let name;
if (user) {
name = user.getName();
}
if (name) {
return `Hello ${name}`;
} else {
return 'Hello, guest';
}
}
Using the Maybe
monad:
const greetUser = (userId: number) => {
return maybe(getUser(userId))
.map((user) => user.getName())
.map((name) => `Hello, ${name}`)
.getOrElse('Hello, guest');
};
Depending on your application, you could simplify this even further, like so:
const getName = (user: User) => user.getName();
const greetName = (name: string) => `Hello, ${name}`;
const greetUser = (userId: number) => {
return maybe(getUser(userId))
.map(getName)
.map(greetName)
.getOrElse(greetName('guest'));
};
As you can see, using the Maybe
monad abstracts away a lot of boilerplate, giving you a flat, composable
interface to work with that's much more readable.
Similarly, the Either
monad is based on Scala's
Either
type.
The Either
monad is useful when you want to represent one of two possible types. An instance of
Either
is either a Left
or Right
. By convention, Right
is used to represent a success type, while
Left
is used to hold a failure type.
Here's another example of some code you might write in imperative TypeScript:
const getAge = (uid: number): number | never => ...;
const canDrink = (age: number): boolean => age >= 18;
const okToDrink = (uid: number) => {
try {
const age = getAge(uid);
if (age) {
return canDrink(age);
}
} catch {
return false;
}
}
Rewriting this using the Either
monad:
const getAge = (uid: number): Either<string, number> => ...; // the left represents an error string
const canDrink = (age: number): boolean => age >= 18;
const okToDrink = (uid: number) => {
getAge(uid).fold(
(error) => {
console.log(`Error getting age: ${error}`);
return false;
},
(age) => canDrink(age)
)
}
- Demystifying the Monad in Scala, Sinisa Louc, a well-written, technical, yet approachable explanation of monads in Scala.
- Douglas Crockford, Monads and Gonads, Google Tech Talks Most talks about Monads involve some assumption that types are essential in creating monadic abstractions. This talk is a refreshing non-academic take on why we need monads (in programming) to begin with, and how you could build one in vanilla (untyped) JavaScript.