mutoid
Version:
Reactive library for data fetching, caching, state management
146 lines (145 loc) • 5.3 kB
JavaScript
import * as E from 'fp-ts/Either';
import * as Eq from 'fp-ts/Eq';
import { constFalse, flow, pipe } from 'fp-ts/function';
// -------------------------------------------------------------------------------------
// guards
// -------------------------------------------------------------------------------------
export const isInit = (r) => r._tag === 'init';
export const isSubmitted = (r) => r._tag === 'submitted';
export const isDone = (r) => r._tag === 'done';
export const isFail = (r) => r._tag === 'fail';
export const isPending = (r) => isInit(r) || isSubmitted(r);
export const isStarted = (r) => isSubmitted(r) || isFail(r) || isDone(r);
export const isAcknowledged = (r) => isFail(r) || isDone(r);
// -------------------------------------------------------------------------------------
// constructors
// -------------------------------------------------------------------------------------
export const init = { _tag: 'init' };
export const submitted = { _tag: 'submitted' };
export const done = (d) => ({
_tag: 'done',
data: d,
});
export const fail = (error) => ({
_tag: 'fail',
error: error,
});
export const ajaxFail = (error) => ({
_tag: 'fail',
error: error,
});
export const failAppError = (detail) => ({
_tag: 'fail',
error: appError(detail),
});
export const appError = (detail) => ({
type: 'appError',
detail,
});
export const ajaxFailOnly = (error) => ({
_tag: 'fail',
error: error,
});
export const fromEither = E.fold(e => fail(e), done);
export const of = done;
// -------------------------------------------------------------------------------------
// Destructors
// -------------------------------------------------------------------------------------
export const match = (onInit, onSubmitted, onDone, onFail) => (r) => {
switch (r._tag) {
case 'init':
return onInit();
case 'submitted':
return onSubmitted();
case 'done':
return onDone(r.data);
case 'fail':
return onFail(r.error);
}
};
export const matchD = (dodo) => match(dodo.onInit || dodo.onPending, dodo.onSubmitted || dodo.onPending, dodo.onDone, dodo.onFail);
export const match_ = (r) => (onInit, onSubmitted, onDone, onFail) => pipe(r, match(onInit, onSubmitted, onDone, onFail));
// -------------------------------------------------------------------------------------
// instances
// -------------------------------------------------------------------------------------
/* istanbul ignore next */
const _map = (fa, f) => pipe(fa, map(f));
/* istanbul ignore next */
const _bimap = (fa, f, g) => pipe(fa, bimap(f, g));
/* istanbul ignore next */
const _mapLeft = (fa, f) => pipe(fa, mapLeft(f));
/* istanbul ignore next */
const _ap = (fa, f) => pipe(fa, ap(f));
/* istanbul ignore next */
const _chain = (fa, f) => pipe(fa, chain(f));
export const URI = 'Resource';
export function getShow(SE, SA) {
return {
show: ma => {
switch (ma._tag) {
case 'done':
return `done(${SA.show(ma.data)})`;
case 'fail':
return `fail(${SE.show(ma.error)})`;
case 'submitted':
return 'submitted';
case 'init':
return 'init';
}
},
};
}
export function getEq(EE, EA) {
return Eq.fromEquals((a, b) => pipe(a, match(() => b._tag === 'init', () => b._tag === 'submitted', fa => pipe(b, match(constFalse, constFalse, fb => EA.equals(fa, fb), constFalse)), sa => pipe(b, match(constFalse, constFalse, constFalse, sb => EE.equals(sa, sb))))));
}
export const Functor = {
URI,
map: _map,
};
export const Apply = {
URI,
map: _map,
ap: _ap,
};
export const Bifunctor = {
URI,
bimap: _bimap,
mapLeft: _mapLeft,
};
export const Applicative = {
URI,
map: _map,
ap: _ap,
of,
};
export const Monad = {
URI,
map: _map,
ap: _ap,
of,
chain: _chain,
};
// -------------------------------------------------------------------------------------
// combinators
// -------------------------------------------------------------------------------------
export function swap(ma) {
return pipe(ma, match(() => init, () => submitted, a => fail(a), done));
}
export function orElseW(onFail) {
return ma => (isFail(ma) ? onFail(ma.error) : ma);
}
export const orElse = orElseW;
export const filterOrElseW = (predicate, onFalse) => chainW(a => (predicate(a) ? done(a) : fail(onFalse(a))));
export const filterOrElse = filterOrElseW;
// -------------------------------------------------------------------------------------
// type class members
// -------------------------------------------------------------------------------------
export const map = f => fa => isDone(fa) ? done(f(fa.data)) : fa;
export const bimap = (f, g) => (fa) => pipe(fa, match(() => init, () => submitted, flow(g, done), flow(f, fail)));
export const mapLeft = f => fa => isFail(fa) ? fail(f(fa.error)) : fa;
export const apW = fa => fab =>
// eslint-disable-next-line no-nested-ternary
isDone(fab) ? (isDone(fa) ? done(fab.data(fa.data)) : fa) : fab;
export const ap = apW;
export const chainW = (f) => (ma) => isDone(ma) ? f(ma.data) : ma;
export const chain = chainW;