Last active
February 12, 2020 10:20
-
-
Save mdubourg001/640c2ae1e34f2e3d81c71d662126373f to your computer and use it in GitHub Desktop.
TypeScript Maybe Monad Implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract class Monad<T> { | |
protected value: T; | |
} | |
class Maybe<T> extends Monad<T> { | |
constructor(val: T | null) { | |
super(); | |
this.value = val; | |
} | |
static Just<T>(val: T): Maybe<T> { | |
return new Maybe(val); | |
} | |
static Nothing<T>(val?: T): Maybe<T> { | |
return new Maybe(null); | |
} | |
static of<T>(val: T): Maybe<T> { | |
return Maybe.Just(val); | |
} | |
map<B>(f: (val: T) => B): Maybe<T | B> { | |
return this.isNothing() ? this : new Maybe(f(this.value)); | |
} | |
join(): T { | |
return this.value; | |
} | |
isNothing(): boolean { | |
return this.value === null; | |
} | |
equals(m: Maybe<T>): boolean { | |
return this.value === m.join(); | |
} | |
toString(): string { | |
return this.isNothing() ? "Nothing" : `Just ${this.value}`; | |
} | |
} | |
// ----- | |
// utils | |
// ----- | |
const log = (...args: any[]) => console.log(`=> ${args.join(' ')}`); | |
/** | |
* calls and returns the result of either 'iftrue' or 'iffalse' functions | |
* based on the result of the pred function evalutation | |
*/ | |
const cond = ( | |
pred: (val: any) => boolean, | |
iftrue: (val: any) => any, | |
iffalse: (val: any) => any, | |
) => (val: any) => (pred(val) ? iftrue(val) : iffalse(val)); | |
const isUndefined = (x: any) => x === undefined; | |
// ----- | |
// examples | |
// ----- | |
const { Just, Nothing } = Maybe; | |
let m = new Maybe(42); | |
log(m); // => Just 42 | |
m = Just(42); | |
log(m); // => Just 42 | |
m = Nothing(42); | |
log(m); // => Nothing | |
const double = (x: number) => x * 2; | |
m = Nothing(10) | |
.map(double) | |
.map(double); | |
log(m); // => Nothing | |
log(m.equals(Nothing())); // => true | |
m = Just(5) | |
.map(double) | |
.map(double); | |
log(m); // => Just 20 | |
let b = Just(5) | |
.map(double) | |
.map(double) | |
.equals(m); | |
log(b); // => true | |
let n = Just(5) | |
.map(double) | |
.map(double) | |
.join(); | |
log(n); // => 20 | |
const getDataThatMayBeUndefined = (): Object | undefined => { | |
const r: number = Math.random(); | |
switch (true) { | |
case r <= 0.5: | |
return { | |
firstname: 'John', | |
lastname: 'Doe', | |
age: 21, | |
}; | |
case r > 0.5: | |
return undefined; | |
} | |
}; | |
const doubleAge = (x: any) => ({ ...x, age: x.age * 2 }); | |
const maybeDoubleAge = (x: any) => Maybe.of({ ...x, age: x.age * 2 }); | |
const data = cond(isUndefined, Nothing, Just)(getDataThatMayBeUndefined()); | |
log(data); // => Just [object Object] | Nothing | |
data | |
.map(doubleAge) | |
.flatMap(maybeDoubleAge) | |
.map(doubleAge) | |
.map((x: any) => log(x.age)); // => 168 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment