spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
129 lines (122 loc) • 5.54 kB
text/typescript
import { AtomicPromise } from './promise';
import { curry } from './curry';
import { push } from './array';
export interface Either<a, b> {
fmap<c>(f: (b: b) => c): Either<a, c>;
ap<b, z>(this: Either<a, (b: b) => z>, b: Either<a, b>): Either<a, z>;
ap<b, c, z>(this: Either<a, (b: b, c: c) => z>, b: Either<a, b>): Either<a, (c: c) => z>;
ap<b, c, d, z>(this: Either<a, (b: b, c: c, d: d) => z>, b: Either<a, b>): Either<a, (c: c, d: d) => z>;
ap<b, c, d, e, z>(this: Either<a, (b: b, c: c, d: d, e: e) => z>, b: Either<a, b>): Either<a, (c: c, d: d, e: e) => z>;
ap<b, c, d, e, f, z>(this: Either<a, (b: b, c: c, d: d, e: e, f: f) => z>, b: Either<a, b>): Either<a, (c: c, d: d, e: e, f: f) => z>;
bind<c>(f: (b: b) => Either<a, c>): Either<a, c>;
join<c>(this: Either<a, Either<a, c>>): Either<a, c>;
extract(): b;
extract(left: (a: a) => b): b;
extract<c>(left: (a: a) => c): b | c;
extract<c>(left: (a: a) => c, right: (b: b) => c): c;
}
class Right<b> implements Either<never, b> {
constructor(
private readonly value: b,
) {
}
public fmap<c>(f: (b: b) => c): Right<c> {
return new Right(f(this.value));
}
public ap<b, z>(this: Either<never, (b: b) => z>, b: Either<never, b>): Either<never, z>;
public ap<b, c, z>(this: Either<never, (b: b, c: c) => z>, b: Either<never, b>): Either<never, (c: c) => z>;
public ap<b, c, d, z>(this: Either<never, (b: b, c: c, d: d) => z>, b: Either<never, b>): Either<never, (c: c, d: d) => z>;
public ap<b, c, d, e, z>(this: Either<never, (b: b, c: c, d: d, e: e) => z>, b: Either<never, b>): Either<never, (c: c, d: d, e: e) => z>;
public ap<b, c, d, e, f, z>(this: Either<never, (b: b, c: c, d: d, e: e, f: f) => z>, b: Either<never, b>): Either<never, (c: c, d: d, e: e, f: f) => z>;
public ap<b, z>(this: Either<never, (b: b) => z>, b: Either<never, b>): Either<never, z> {
return Either.ap(this, b);
}
public bind<c, a = never>(f: (b: b) => Left<a>): Left<a>;
public bind<c, a = never>(f: (b: b) => Right<c>): Right<c>;
public bind<c, a>(f: (b: b) => Either<a, c>): Either<a, c>;
public bind<c, a>(f: (b: b) => Either<a, c>): Either<a, c> {
return f(this.extract());
}
public join<c>(this: Right<Either<never, c>>): Either<never, c> {
return this.value;
}
public extract(): b;
public extract(left: (a: never) => b): b;
public extract<c>(left: (a: never) => c): b;
public extract<c>(left: (a: never) => c, right: (b: b) => c): c;
public extract<c>(left?: (a: never) => c, right?: (b: b) => c): b | c {
if (right !== undefined) return right(this.value);
return this.value;
assert([left]);
}
}
class Left<a> implements Either<a, never> {
constructor(
private readonly value: a,
) {
}
public fmap<c>(f: (b: never) => c): Left<a> {
return this;
assert(f);
}
public ap<b, z>(this: Either<a, (b: b) => z>, b: Either<a, b>): Either<a, z>;
public ap<b, c, z>(this: Either<a, (b: b, c: c) => z>, b: Either<a, b>): Either<a, (c: c) => z>;
public ap<b, c, d, z>(this: Either<a, (b: b, c: c, d: d) => z>, b: Either<a, b>): Either<a, (c: c, d: d) => z>;
public ap<b, c, d, e, z>(this: Either<a, (b: b, c: c, d: d, e: e) => z>, b: Either<a, b>): Either<a, (c: c, d: d, e: e) => z>;
public ap<b, c, d, e, f, z>(this: Either<a, (b: b, c: c, d: d, e: e, f: f) => z>, b: Either<a, b>): Either<a, (c: c, d: d, e: e, f: f) => z>;
public ap<b, z>(this: Either<a, (b: b) => z>, _: Either<a, b>): Either<a, z> {
return this as Left<a>;
}
public bind<c, _ = a>(f: (b: never) => Either<a, c>): Left<a> {
return this;
assert(f);
}
public join<c>(this: Either<a, Either<a, c>>): Left<a> {
return this as Left<a>;
}
public extract(): never;
public extract<c>(left: (a: a) => c): c;
public extract<c>(left: (a: a) => c, right: (b: never) => c): c;
public extract<c>(left?: (a: a) => c): c {
if (left !== undefined) return left(this.value);
throw this.value;
}
}
type right<b> = Right<b>;
function right<b>(b: b): Right<b>;
function right<a, b>(b: b): Either<a, b>;
function right<a, b>(b: b): Either<a, b> {
return new Right(b);
}
type left<a> = Left<a>;
function left<a>(value: a): Left<a> {
return new Left(value);
}
export {
right as Right,
left as Left,
};
export namespace Either {
export function fmap<a, b, c>(f: (b: b) => c, m: Either<a, b>): Either<a, c> {
return m.fmap(f);
}
export const pure = right;
export function ap<a, b, c>(mf: Either<a, (b: b) => c>, ma: Either<a, b>): Either<a, c>;
export function ap<a, b, c>(mf: Either<a, (b: b) => c>): (ma: Either<a, b>) => Either<a, c>;
export function ap<a, b, c>(af: Either<a, (b: b) => c>, aa?: Either<a, b>): Either<a, c> | ((aa: Either<a, b>) => Either<a, c>) {
return aa
? af.bind(f => aa.fmap(curry(f)))
: (aa: Either<a, b>) => ap(af, aa);
}
export const Return = pure;
export function bind<a, b, c>(f: (b: b) => Either<a, c>, m: Either<a, b>): Either<a, c> {
return m.bind(f);
}
export function sequence<a, b>(fm: Either<a, b>[]): Either<a, b[]>;
export function sequence<a, b>(fm: Either<a, PromiseLike<b>>): AtomicPromise<Either<a, b>>;
export function sequence<a, b>(fm: Either<a, b>[] | Either<a, PromiseLike<b>>): Either<a, b[]> | AtomicPromise<Either<a, b>> {
return Array.isArray(fm)
? fm.reduce((acc, m) => acc.bind(as => m.fmap(a => push(as, [a]))), Return([]))
: fm.extract(b => AtomicPromise.resolve(new Left(b)), a => AtomicPromise.resolve(a).then<Either<a, b>>(Return));
}
}