-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
284 changed files
with
21,305 additions
and
20 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { getValueOr, MonadType, Mappable, Predicate, Effect } from "./main.ts"; | ||
|
||
import { Monad } from "./types.ts"; | ||
|
||
export class Chain<A> extends Monad<A> { | ||
readonly __value: A; | ||
constructor(value: A) { | ||
super(); | ||
this.__value = value; | ||
} | ||
_<B>(fn: Mappable<A, MonadType<B>>): Chain<B> { | ||
return chain(fn(this.__value)); | ||
} | ||
bind = this._; | ||
bindM<B>(fn: Mappable<Monad<A>, Monad<B>>): Chain<B> { | ||
return chain(fn(this)); | ||
} | ||
filter(pred: Predicate<A>): Chain<A> { | ||
return pred(this.__value) ? this : chain(null as any); | ||
} | ||
getValue() { | ||
return this.__value; | ||
} | ||
getValueOr(alt: A) { | ||
return this.__value || alt; | ||
} | ||
effect(eff: Effect<A>) { | ||
eff(this.__value); | ||
return this; | ||
} | ||
} | ||
|
||
export function chain<A>(value: MonadType<A>): Chain<A> { | ||
return new Chain(getValueOr(null as any, value)); | ||
} | ||
|
||
export function isChain(candidate: unknown) { | ||
return candidate instanceof Chain; | ||
} |
Empty file.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import { Effect, identity, isNil, getValue, Mappable, Predicate } from "./main.ts"; | ||
import { Monad, MonadType, NilError, InvalidValueError } from "./types.ts"; | ||
|
||
export type Either<R, L = Error> = Left<R, L> | Right<R, L>; | ||
|
||
export type EitherMatcher<R, L, B> = { | ||
left?: (err: L) => B; | ||
right?: (val: R) => B; | ||
}; | ||
export class Left<R, L = Error> extends Monad<R> { | ||
readonly errorValue: L; | ||
|
||
constructor(errVal: L) { | ||
super(); | ||
this.errorValue = errVal; | ||
} | ||
|
||
_<B>(_: Mappable<R, B>, _hint?: L): Either<NonNullable<B>, L> { | ||
return this as any; | ||
} | ||
bind = this._; | ||
bindM<B>(_: Mappable<Monad<R>, Monad<B>>): Either<NonNullable<B>, L> { | ||
return this as any; | ||
} | ||
filter(_: any, _hint?: L): Either<NonNullable<R>, L> { | ||
return this as any; | ||
} | ||
effect(_: any): Either<NonNullable<R>, L> { | ||
return this as any; | ||
} | ||
getValue(): NonNullable<R> { | ||
throw new Error(`Can not get value of Left(${String(this.errorValue)})`); | ||
} | ||
getValueOr(alt: R): R { | ||
return alt; | ||
} | ||
match<B>(matcher: EitherMatcher<R, L, B>): Either<NonNullable<B>, L | Error> { | ||
return matcher.left ? either<B, L>(matcher.left(this.errorValue)) : (this as any); | ||
} | ||
isLeft() { | ||
return true; | ||
} | ||
isRight() { | ||
return false; | ||
} | ||
getReason() { | ||
return this.errorValue; | ||
} | ||
} | ||
|
||
export class Right<R extends NonNullable<any>, L = Error> extends Monad<R> { | ||
readonly value: R; | ||
constructor(value: R) { | ||
super(); | ||
this.value = value; | ||
} | ||
_<B>(fn: Mappable<R, B>, hint?: L): Either<NonNullable<B>, L | Error> { | ||
try { | ||
const result = fn(this.value); | ||
const bound = either(getValue(result)); | ||
return bound.isLeft() && hint ? left(hint) : bound; | ||
} catch (err) { | ||
return left<NonNullable<B>, Error>(err); | ||
} | ||
} | ||
bind = this._; | ||
bindM<B>(fn: Mappable<Monad<R>, Monad<B>>): Either<NonNullable<B>, L | Error> { | ||
try { | ||
const result = fn(this); | ||
return either(getValue(result)); | ||
} catch (err) { | ||
return left<NonNullable<B>, L>(err); | ||
} | ||
} | ||
filter(fn: Predicate<R>, hint?: L): Either<NonNullable<R>, L | Error> { | ||
try { | ||
return fn(this.value) | ||
? (this as any) | ||
: left<NonNullable<R>>(hint instanceof Error ? hint : new InvalidValueError(hint as any)); | ||
} catch (err) { | ||
return left<NonNullable<R>>(new Error(`Exception while filtering: ${err.text}`)); | ||
} | ||
} | ||
|
||
effect(fn: Effect<R>): Either<NonNullable<R>, L> { | ||
try { | ||
fn(this.value); | ||
} catch (err) {} | ||
return this as any; | ||
} | ||
getValue(): NonNullable<R> { | ||
return this.value as NonNullable<R>; | ||
} | ||
getValueOr(_: R): R { | ||
return this.value; | ||
} | ||
match<B>(matcher: EitherMatcher<R, L, B>): Either<NonNullable<B>, L> { | ||
const m = matcher.right ?? (identity as any); | ||
return either(m(this.value)) as any; | ||
} | ||
isLeft() { | ||
return false; | ||
} | ||
isRight() { | ||
return true; | ||
} | ||
getReason() { | ||
throw new Error("Can not get error reason for Right"); | ||
} | ||
} | ||
|
||
export function left<R, L = Error>(errVal: L) { | ||
return new Left<R, L>(errVal); | ||
} | ||
|
||
export function right<R, L = Error>(value: R) { | ||
return new Right<R, L>(value); | ||
} | ||
|
||
export function either<R, L = Error>( | ||
value: MonadType<R>, | ||
errVal?: L | ||
): Either<NonNullable<R>, L | Error> { | ||
const innerValue = getValue(value); | ||
return isNil(innerValue) | ||
? errVal | ||
? new Left<NonNullable<R>, L>(errVal) | ||
: new Left<NonNullable<R>, Error>(new NilError()) | ||
: new Right<NonNullable<R>, L>(innerValue as any); | ||
} | ||
|
||
export function isLeft<R = any, L = Error>(el: unknown): el is Left<R, L> { | ||
return el instanceof Left; | ||
} | ||
|
||
export function isRight<R = any, L = Error>(el: unknown): el is Right<R, L> { | ||
return el instanceof Right; | ||
} | ||
|
||
export function isEither<R = any, L = Error>(el: unknown): el is Either<R, L> { | ||
return isLeft<R>(el) || isRight<L>(el); | ||
} | ||
|
||
export const throwLeftAsError = { | ||
right: (data: any) => data, | ||
left: (reason: Error | string) => { | ||
throw reason instanceof Error ? reason : new Error(reason); | ||
return reason as any; | ||
}, | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { Effect, Mappable, UnboxPromise } from "./types.ts"; | ||
|
||
export interface ErrorLog<T> { | ||
_: <U>(f: Mappable<T, U>) => ErrorLog<U>; | ||
effect: (f: Effect<T>) => ErrorLog<T>; | ||
try: <U>(f: Mappable<T, U>, alt: U) => ErrorLog<U>; | ||
getErrors: () => Error[]; | ||
getValue: () => T; | ||
getValueOr: <U>(alt: U) => NonNullable<T> | U; | ||
hasErrors: () => boolean; | ||
} | ||
function ErrorLog<T>(expr: T, errors: Array<Error> = []) { | ||
return { | ||
_: <U>(f: Mappable<T, U>) => ErrorLog(f(expr), errors), | ||
try: <U>(f: Mappable<T, U>, alt: U): ErrorLog<U> => { | ||
try { | ||
return ErrorLog(f(expr), errors); | ||
} catch (err) { | ||
return ErrorLog(alt, [...errors, err]); | ||
} | ||
}, | ||
effect: (f: Effect<T>): ErrorLog<T> => { | ||
try { | ||
f(expr); | ||
return ErrorLog(expr, errors); | ||
} catch (err) { | ||
return ErrorLog(expr, [...errors, err]); | ||
} | ||
}, | ||
getErrors: () => errors, | ||
getValue: () => expr, | ||
getValueOr: <U>(alt: T | U) => (expr || alt) as NonNullable<T> | U, | ||
hasErrors: () => errors.length > 0, | ||
}; | ||
} | ||
|
||
export interface FutureErrorLog<T> { | ||
_: <U>(f: Mappable<T, U>) => FutureErrorLog<U>; | ||
effect: (f: Effect<T>) => FutureErrorLog<T>; | ||
try: <U>(f: Mappable<T, U>, alt: UnboxPromise<U>) => FutureErrorLog<U>; | ||
getErrors: () => Promise<Error[]>; | ||
getValue: () => Promise<UnboxPromise<T>>; | ||
getValueOr: <U>(alt: U) => Promise<NonNullable<UnboxPromise<T>> | UnboxPromise<U>>; | ||
hasErrors: () => Promise<boolean>; | ||
} | ||
const FutureErrorLog = <T>( | ||
expr: T | Promise<T>, | ||
errors: Promise<Error[]> = Promise.resolve([]) | ||
): FutureErrorLog<T> => { | ||
const exprP = expr instanceof Promise ? expr : Promise.resolve(expr); | ||
const tryOrAppendError = (p: Promise<any>, es: Promise<Error[]>) => | ||
p.then(() => es).catch(async (err) => [...(await errors), err]); | ||
return { | ||
_: <U>(f: Mappable<T, U>): FutureErrorLog<U> => FutureErrorLog(exprP.then(f), errors), | ||
effect: (fx: Effect<T>) => FutureErrorLog(exprP, tryOrAppendError(exprP.then(fx), errors)), | ||
try: <U>(f: Mappable<T, U>, alt: UnboxPromise<U>): FutureErrorLog<U> => { | ||
const p = exprP.then(f); | ||
return FutureErrorLog( | ||
p.catch(() => alt as any), | ||
tryOrAppendError(p, errors) | ||
); | ||
}, | ||
getErrors: () => errors, | ||
getValue: () => (exprP as unknown) as Promise<UnboxPromise<T>>, | ||
getValueOr: async <U>(alt: U) => (await exprP) || (alt instanceof Promise ? await alt : alt), | ||
hasErrors: () => errors.then((e) => e.length > 0), | ||
}; | ||
}; | ||
|
||
export const errorLogged = <T>(value: T) => ErrorLog(value); | ||
export const errorLoggedFuture = <T>(value: T) => FutureErrorLog(value); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function F(..._: any) { | ||
return false; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Mappable } from "./types.ts"; | ||
|
||
export class Free<T> { | ||
private val: T = undefined as any; | ||
static of = <A>(val: A) => { | ||
const m = new Free<A>(); | ||
m.val = val; | ||
return m; | ||
}; | ||
|
||
_ = <U>(f: Mappable<T, U>) => Free.of(f(this.val)); | ||
} |
Oops, something went wrong.