@typed/fp
Version:
Data Structures and Resources for fp-ts
453 lines • 10.8 kB
JavaScript
/**
* Data is an ADT which allows you to represent all the states involved in loading a
* piece of data asynchronously.
*
* @since 0.9.2
*/
import * as AD from 'fp-ts/Alt';
import * as App from 'fp-ts/Applicative';
import * as Ap from 'fp-ts/Apply';
import * as Ch from 'fp-ts/Chain';
import * as Ei from 'fp-ts/Either';
import { constant, constFalse, flow, identity, pipe } from 'fp-ts/function';
import * as F from 'fp-ts/Functor';
import * as O from 'fp-ts/Option';
import * as P from './Progress';
/**
* @since 0.9.2
* @category Refinement
*/
export const isNoData = (data) => data._tag === 'NoData';
/**
* @since 0.9.2
* @category Refinement
*/
export const isLoading = (data) => data._tag === 'Loading';
/**
* @since 0.9.2
* @category Refinement
*/
export const isRefresh = (data) => data._tag === 'Refresh';
/**
* @since 0.9.2
* @category Refinement
*/
export const isReplete = (data) => data._tag === 'Replete';
/**
* @since 0.9.2
* @category Refinement
*/
export const hasValue = (data) => isRefresh(data) || isReplete(data);
/**
* @since 0.9.2
* @category Constructor
*/
export const noData = { _tag: 'NoData' };
/**
* @since 0.9.2
* @category Constructor
*/
export const loading = {
_tag: 'Loading',
progress: O.none,
};
/**
* @since 0.9.2
* @category Constructor
*/
export const fromProgress = (progress) => ({
_tag: 'Loading',
progress: O.some(progress),
});
/**
* @since 0.9.2
* @category Constructor
*/
export const refresh = (value, progress = O.none) => ({
_tag: 'Refresh',
value,
progress,
});
/**
* @since 0.9.2
* @category Constructor
*/
export const replete = (value) => ({ _tag: 'Replete', value });
/**
* @since 0.9.2
* @category Deconstructor
*/
export const matchW = (onNoData, onLoading, onRefresh, onReplete) => (data) => {
switch (data._tag) {
case 'NoData':
return onNoData();
case 'Loading':
return onLoading(data.progress);
case 'Refresh':
return onRefresh(data.value, data.progress);
case 'Replete':
return onReplete(data.value);
}
};
/**
* @since 0.9.2
* @category Deconstructor
*/
export const match3W = (onNoData, onLoading, onRefreshOrReplete) => (data) => {
switch (data._tag) {
case 'NoData':
return onNoData();
case 'Loading':
return onLoading(data.progress);
case 'Refresh':
case 'Replete':
return onRefreshOrReplete(data.value);
}
};
/**
* @since 0.9.2
* @category Deconstructor
*/
export const match = matchW;
/**
* @since 0.9.2
* @category Combinator
*/
export const toLoading = (data) => pipe(data, matchW(() => loading, flow(O.map(fromProgress), O.getOrElse(() => loading)), refresh, refresh));
/**
* @since 0.9.2
* @category Constructor
*/
export const fromNullable = (a) => a === null || a === undefined ? noData : replete(a);
/**
* @since 0.9.2
* @category Typeclass Constructor
*/
export const getShow = (S) => ({
show: matchW(constant('NoData'), O.matchW(() => `Loading`, (p) => `Loading(${P.Show.show(p)})`), (v, p) => pipe(p, O.matchW(() => `Refresh(${S.show(v)})`, (p) => `Refresh(${S.show(v)}, ${P.Show.show(p)})`)), (v) => `Replete(${S.show(v)})`),
});
/**
* @since 0.9.2
* @category Typeclass Constructor
*/
export const getEq = (S) => {
const OptionProgressEq = O.getEq(P.Eq);
return {
equals: (a) => (b) => pipe(a, matchW(() => isNoData(b), (p) => isLoading(b) && OptionProgressEq.equals(p)(b.progress), (a, p) => isRefresh(b) && S.equals(a)(b.value) && OptionProgressEq.equals(p)(b.progress), (a) => isReplete(b) && S.equals(a)(b.value))),
};
};
/**
* @since 0.9.2
* @category Typeclass Constructor
*/
export const getSemigroup = (S) => {
const OptionProgressSemigroup = O.getMonoid(P.Semigroup);
return {
concat: (secondD) => (firstD) => pipe(firstD, matchW(constant(secondD), // Empty value
(fp) => pipe(secondD, matchW(constant(firstD), constant(firstD), (second, sp) => refresh(second, OptionProgressSemigroup.concat(sp)(fp)), refresh)), (first, fp) => pipe(secondD, matchW(constant(firstD), constant(firstD), (second, sp) => refresh(pipe(first, S.concat(second)), OptionProgressSemigroup.concat(sp)(fp)), (second) => refresh(pipe(first, S.concat(second)), fp))), (first) => pipe(secondD, matchW(constant(firstD), (sp) => refresh(first, sp), (second, sp) => refresh(pipe(first, S.concat(second)), sp), (second) => replete(pipe(first, S.concat(second))))))),
};
};
/**
* @since 0.9.2
* @category Typeclass Constructor
*/
export const getMonoid = (S) => ({
...getSemigroup(S),
empty: noData,
});
/**
* @since 0.9.2
* @category Combinator
*/
export const getOrElse = (onInitial, onLoading) => (ma) => match3W(onInitial, onLoading, identity)(ma);
/**
* @since 0.9.2
* @category Combinator
*/
export const getOrElseW = (onInitial, onLoading) => (ma) => match3W(onInitial, onLoading, identity)(ma);
/**
* @since 0.9.2
* @category Combinator
*/
export const elem = (E) => (a) => (ma) => match3W(constFalse, constFalse, E.equals(a))(ma);
/**
* @since 0.9.2
* @category Combinator
*/
export const exists = (predicate) => (ma) => pipe(ma, match3W(constFalse, constFalse, predicate));
/**
* @since 0.9.2
* @category Constructor
*/
export const of = (value) => replete(value);
/**
* @since 0.9.2
* @category Combinator
*/
export const map = (f) => (data) => pipe(data, matchW(constant(noData), flow(O.map(fromProgress), O.getOrElse(() => loading)), (a, p) => refresh(f(a), p), flow(f, replete)));
/**
* @since 0.9.2
* @category Combinator
*/
export const chain = (f) => (data) => pipe(data, match3W(constant(noData), flow(O.map(fromProgress), O.getOrElse(() => loading)), f));
/**
* @since 0.9.2
* @category URI
*/
export const URI = '@typed/fp/Data';
/**
* @since 0.9.2
* @category Instance
*/
export const Pointed = {
of,
};
/**
* @since 0.9.2
* @category Instance
*/
export const Functor = {
map,
};
/**
* @since 0.9.2
* @category Combinator
*/
export const bindTo = F.bindTo(Functor);
/**
* @since 0.9.2
* @category Combinator
*/
export const flap = F.flap(Functor);
/**
* @since 0.9.2
* @category Combinator
*/
export const tupled = F.tupled(Functor);
/**
* @since 0.9.2
* @category Instance
*/
export const Chain = {
...Functor,
chain,
};
/**
* @since 0.9.2
* @category Combinator
*/
export const ap = Ch.ap(Chain);
/**
* @since 0.9.2
* @category Combinator
*/
export const bind = Ch.bind(Chain);
/**
* @since 0.9.2
* @category Combinator
*/
export const chainFirst = Ch.chainFirst(Chain);
/**
* @since 0.9.2
* @category Instance
*/
export const Apply = {
...Functor,
ap,
};
/**
* @since 0.9.2
* @category Combinator
*/
export const apFirst = Ap.apFirst(Apply);
/**
* @since 0.9.2
* @category Combinator
*/
export const apS = Ap.apS(Apply);
/**
* @since 0.9.2
* @category Combinator
*/
export const apSecond = Ap.apSecond(Apply);
/**
* @since 0.9.2
* @category Combinator
*/
export const apT = Ap.apT(Apply);
/**
* @since 0.9.2
* @category Typeclass Constructor
*/
export const getApplySemigroup = Ap.getApplySemigroup(Apply);
/**
* @since 0.9.2
* @category Instance
*/
export const Applicative = {
...Apply,
...Pointed,
};
/**
* @since 0.9.2
* @category Typeclass Constructor
*/
export const getApplicativeMonoid = App.getApplicativeMonoid(Applicative);
/**
* @since 0.9.2
* @category Instance
*/
export const Monad = {
...Chain,
...Pointed,
};
/**
* @since 0.9.2
* @category Combinator
*/
export const chainRec = (f) => (value) => {
let data = f(value);
while (!isNoData(data) && !isLoading(data)) {
if (Ei.isRight(data.value)) {
return replete(data.value.right);
}
data = f(data.value.left);
}
return data;
};
/**
* @since 0.9.2
* @category Combinator
*/
export const alt = (f) => (b) => pipe(b, matchW(f, f, refresh, replete));
/**
* @since 0.9.2
* @category Instance
*/
export const Alt = {
...Functor,
alt,
};
/**
* @since 0.9.2
* @category Combinator
*/
export const altAll = AD.altAll(Alt);
/**
* @since 0.9.2
* @category Constructor
*/
export const zero = () => noData;
/**
* @since 0.9.2
* @category Instance
*/
export const Alternative = {
...Alt,
zero,
};
/**
* @since 0.9.2
* @category Natural Transformation
*/
export const fromOption = (option) => pipe(option, O.matchW(() => noData, replete));
/**
* @since 0.9.2
* @category Natural Transformation
*/
export const toOption = (data) => pipe(data, matchW(() => O.none, () => O.none, O.some, O.some));
/**
* @since 0.9.2
* @category Combinator
*/
export function foldMap(M) {
return (f) => (fa) => pipe(fa, match3W(() => M.empty, () => M.empty, f));
}
/**
* @since 0.9.2
* @category Deconstructor
*/
export const reduce = (seed, f) => (data) => pipe(data, match3W(constant(seed), constant(seed), (b) => f(seed, b)));
/**
* @since 0.9.2
* @category Deconstructor
*/
export const reduceRight = (seed, f) => (data) => pipe(data, match3W(constant(seed), constant(seed), (b) => f(b, seed)));
/**
* @since 0.9.2
* @category Instance
*/
export const Foldable = {
reduce,
foldMap,
reduceRight,
};
/**
* @since 0.9.2
* @category Combinator
*/
export function traverse(F) {
return (f) => (data) => pipe(data, matchW(() => F.of(noData), O.matchW(() => F.of(loading), (p) => F.of(fromProgress(p))), (a, progress) => pipe(a, f, F.map((b) => refresh(b, progress))), flow(f, F.map(replete))));
}
/**
* @since 0.9.2
* @category Instance
*/
export const Traversable = {
...Functor,
traverse,
};
/**
* @since 0.9.2
* @category Combinator
*/
export const compact = (dataOption) => pipe(dataOption, chain(O.matchW(() => noData, replete)));
/**
* @since 0.9.2
* @category Combinator
*/
export const separate = (dataEither) => {
return {
left: pipe(dataEither, chain(Ei.matchW(replete, () => noData))),
right: pipe(dataEither, chain(Ei.matchW(() => noData, replete))),
};
};
/**
* @since 0.9.2
* @category Instance
*/
export const Compactable = {
compact,
separate,
};
/**
* @since 0.9.2
* @category Combinator
*/
export const partitionMap = (f) => (fa) => pipe(fa, map(f), separate);
/**
* @since 0.9.2
* @category Combinator
*/
export const partition = flow(Ei.fromPredicate, partitionMap);
/**
* @since 0.9.2
* @category Combinator
*/
export const filterMap = (f) => (fa) => pipe(fa, map(f), compact);
/**
* @since 0.9.2
* @category Combinator
*/
export const filter = flow(O.fromPredicate, filterMap);
/**
* @since 0.9.2
* @category Instance
*/
export const Filterable = {
partitionMap,
partition,
filterMap,
filter,
};
//# sourceMappingURL=Data.js.map