igogo
Version:
Fast Either and Maybe from Fantasy Land with Flow and TypeScript support
231 lines (172 loc) • 4.66 kB
JavaScript
/* @flow */
import * as either from './Either';
import { toIterator } from './toIterator';
import { toEmptyIterator } from './toEmptyIterator';
import { getSet } from './getSet';
export interface Maybe<+T> extends Iterable<T> {
+isJust: boolean;
+isNothing: boolean;
map<T1>(transform: (T) => T1): Maybe<T1>;
mapTo<T1>(value: T1): Maybe<T1>;
ap<T1>(maybe: Maybe<(T) => T1>): Maybe<T1>;
chain<T1>(transform: (T) => Maybe<T1>): Maybe<T1>;
filter(predicate: typeof Boolean): Maybe<$NonMaybeType<T>>;
filter<P: $Pred<1>>(predicate: P): Maybe<$Refine<T, P, 1>>;
tap(call: (T) => any): Maybe<T>;
and<T1>(maybe: Maybe<T1>): Maybe<T1>;
or<T1>(maybe: Maybe<T1>): Maybe<T | T1>;
alt<T1>(maybe: Maybe<T1>): Maybe<T | T1>;
getOr<T1>(value: T1): T | T1;
getOrElse<T1>(fn: () => T1): T | T1;
reduce<T1>(transform: (T1, T) => T1, or: T1): T1;
match<T1, T2>(fromJust: (T) => T1, fromNothing: () => T2): T1 | T2;
toEither<L>(left: L): either.Either<L, T>;
promise(error?: Error): Promise<T>;
}
class MaybeJust<+T> implements Maybe<T> {
constructor(value: T) {
setValue(this, value);
}
get isJust() {
return true;
}
get isNothing() {
return false;
}
/* :: +@@iterator: () => Iterator<T>; */
map<T1>(transform: T => T1): Maybe<T1> {
return Just(transform(getValue(this)));
}
mapTo<T1>(value: T1): Maybe<T1> {
return Just(value);
}
ap<T1>(maybe: Maybe<(T) => T1>): Maybe<T1> {
return maybe.map(transform => transform(getValue(this)));
}
chain<T1>(transform: T => Maybe<T1>): Maybe<T1> {
return transform(getValue(this));
}
filter<P: $Pred<1>>(predicate: P | (T => boolean)): Maybe<$Refine<T, P, 1>> {
const value = getValue(this);
return predicate(value) ? Just(value) : nothing;
}
tap(call: T => any): Maybe<T> {
call(getValue(this));
return this;
}
and<T1>(maybe: Maybe<T1>): Maybe<T1> {
return maybe;
}
or(): Maybe<T> {
return this;
}
alt(): Maybe<T> {
return this;
}
getOr(): T {
return getValue(this);
}
getOrElse(): T {
return getValue(this);
}
reduce<T1>(transform: (T1, T) => T1, or: T1): T1 {
return transform(or, getValue(this));
}
match<T1, T2>(
fromJust: T => T1,
/* :: fromNothing: () => T2 */
): T1 | T2 {
return fromJust(getValue(this));
}
toEither<L>(): either.Either<L, T> {
return either.Right(getValue(this));
}
promise(): Promise<T> {
return Promise.resolve(getValue(this));
}
}
class MaybeNothing<+T> implements Maybe<T> {
get isJust() {
return false;
}
get isNothing() {
return true;
}
/* :: +@@iterator: () => Iterator<T>; */
map() {
return nothing;
}
mapTo() {
return nothing;
}
ap() {
return nothing;
}
chain() {
return nothing;
}
filter() {
return nothing;
}
tap() {
return nothing;
}
and() {
return nothing;
}
or<T1>(maybe: Maybe<T1>): Maybe<T1> {
return maybe;
}
alt<T1>(maybe: Maybe<T1>): Maybe<T1> {
return maybe;
}
getOr<T1>(value: T1): T1 {
return value;
}
getOrElse<T1>(fn: () => T1): T1 {
return fn();
}
reduce<T1>(transform: (T1, T) => T1, or: T1): T1 {
return or;
}
match<T1, T2>(fromJust: T => T1, fromNothing: () => T2): T1 | T2 {
return fromNothing();
}
toEither<L>(left: L): either.Either<L, T> {
return either.Left(left);
}
promise(error?: Error): Promise<T> {
return Promise.reject(error);
}
}
export const nothing: Maybe<any> = new MaybeNothing();
const [getValue, setValue]: [
<T>(MaybeJust<T>) => T,
<T>(MaybeJust<T>, T) => T,
] = getSet('value');
(MaybeJust.prototype: any)[Symbol.iterator] = toIterator(getValue);
(MaybeNothing.prototype: any)[Symbol.iterator] = toEmptyIterator;
export const Just = <T>(value: T): Maybe<T> => new MaybeJust(value);
export const Nothing = <T>(/* :: value: T */): Maybe<T> => nothing;
export const fromNullable = <T>(value: ?T): Maybe<T> =>
value == null ? nothing : Just(value);
export const fromFalsy = <T>(value: ?T): Maybe<T> =>
!value ? nothing : Just(value);
export const opt = fromNullable;
declare export function when<V>(condition: boolean, value: V): Maybe<V>;
declare export function when<V>(
predicate: typeof Boolean,
value: V,
): Maybe<$NonMaybeType<V>>;
declare export function when<P: $Pred<1>, V>(
predicate: P,
value: V,
): Maybe<$Refine<V, P, 1>>;
export function when<V>(
predicate: boolean | (V => boolean),
value: V,
): Maybe<V> {
const condition =
typeof predicate == 'boolean' ? predicate : predicate(value);
return condition ? Just(value) : nothing;
}