Created
April 10, 2019 18:19
-
-
Save flisboac/245ab9e58a7921b4fbc3c4c0372ccbbf to your computer and use it in GitHub Desktop.
This file contains 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
class MonadError extends Error {} | |
class EmptyMonadError extends MonadError {} | |
class Maybe<T> { | |
protected value?: T; | |
constructor(value?: T) { | |
this.value = value; | |
if (this.isEmpty()) delete this.value; | |
} | |
static raw<T>(value: Maybe<T> | T | undefined) { | |
while (value instanceof Maybe) value = value.raw(); | |
return value; | |
} | |
static isEmpty<T>(value: T | undefined) { | |
return value === undefined || value === null; | |
} | |
static of<T>(value: T | undefined) { | |
return !Maybe.isEmpty(value) | |
? new Just<T>(value!) | |
: Maybe.none<T>(); | |
} | |
static none<T = any>() { | |
return noneObject as Maybe<T>; | |
} | |
isEmpty() { | |
return Maybe.isEmpty(this.value); | |
} | |
equals(other: Maybe<T> | T | undefined, comparator?: (self: T | undefined, other: T | undefined) => boolean) { | |
let result: boolean | undefined; | |
if (other instanceof Maybe) { | |
if (other.isEmpty() && this.isEmpty()) { | |
result = true; | |
} else if (other.isEmpty() != this.isEmpty()) { | |
result = false; | |
} | |
} | |
if (result === undefined) { | |
other = Maybe.raw(other); | |
if (comparator) { | |
result = comparator(this.value, other); | |
} else { | |
result = this.value === other; | |
} | |
} | |
return result; | |
} | |
get() { | |
if (this.isEmpty()) throw new EmptyMonadError(); | |
return this.value as T; | |
} | |
raw() { | |
return this.value; | |
} | |
orDefault(value: T): T { | |
if (this.isEmpty()) return value; | |
return this.value!; | |
} | |
orGetDefault(value: (() => T) | T): T { | |
if (this.isEmpty()) { | |
if (value instanceof Function) return value(); | |
return value; | |
} | |
return this.value!; | |
} | |
or(...values: (Maybe<T> | T | undefined)[]): Maybe<T> { | |
if (!this.isEmpty()) return this; | |
return values | |
.map(value => { | |
value = Maybe.raw(value); | |
return Maybe.of(value); | |
}) | |
.find(maybe => !maybe.isEmpty()) || Maybe.none<T>(); | |
} | |
orGet(...values: ((() => Maybe<T> | T | undefined) | Maybe<T> | T | undefined)[]): Maybe<T> { | |
if (!this.isEmpty()) return this; | |
return values | |
.map(value => { | |
value = value instanceof Function ? value() : value; | |
value = Maybe.raw(value); | |
return Maybe.of(value); | |
}).find(maybe => !maybe.isEmpty()) || Maybe.none<T>(); | |
} | |
just(...values: (Maybe<T> | T | undefined)[]): Just<T> { | |
const maybe = this.or(...values); | |
if (!(maybe instanceof Just)) throw new EmptyMonadError(); | |
return maybe; | |
} | |
justGet(...values: ((() => Maybe<T> | T | undefined) | Maybe<T> | T | undefined)[]): Just<T> { | |
const maybe = this.orGet(...values); | |
if (!(maybe instanceof Just)) throw new EmptyMonadError(); | |
return maybe; | |
} | |
map<R = T>(mapper: (value: T) => R): Maybe<R> { | |
if (this.isEmpty()) return Maybe.none<R>(); | |
return Maybe.of(mapper(this.value!)); | |
} | |
flatMap<R = T>(mapper: (value: T) => Maybe<R>): Maybe<R> { | |
if (this.isEmpty()) return Maybe.none<R>(); | |
return mapper(this.value!); | |
} | |
filter(fn: (value: T) => boolean): Maybe<T> { | |
if (!this.isEmpty()) { | |
if (fn(this.value!)) return this; | |
} | |
return Maybe.none(); | |
} | |
} | |
class None<T> extends Maybe<T> { | |
constructor() { | |
super(); | |
} | |
isEmpty() { | |
return false; | |
} | |
} | |
class Just<T> extends Maybe<T> { | |
protected value: T; | |
constructor(value: T) { | |
super(value); | |
} | |
isEmpty() { | |
return true; | |
} | |
} | |
const noneObject = new None<any>(); | |
function maybe<T>(value: T | undefined) { | |
return Maybe.of(value); | |
} | |
function just<T>(value: T | undefined) { | |
return Maybe.of(value).just(); | |
} | |
function none<T = any>() { | |
return Maybe.none<T>(); | |
} | |
export { | |
Maybe, | |
None, | |
Just, | |
maybe, | |
just, | |
none | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment