UNPKG

@typed/fp

Version:

Data Structures and Resources for fp-ts

453 lines 10.8 kB
/** * 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