monocle-ts-esm
Version:
A porting of scala monocle library to TypeScript
218 lines (217 loc) • 8.96 kB
JavaScript
import * as RA from 'fp-ts-esm/ReadonlyArray';
import * as RR from 'fp-ts-esm/ReadonlyRecord';
import { constant, flow, identity } from 'fp-ts-esm/function';
import * as O from 'fp-ts-esm/Option';
import * as E from 'fp-ts-esm/Either';
import { pipe } from 'fp-ts-esm/pipeable';
// -------------------------------------------------------------------------------------
// Iso
// -------------------------------------------------------------------------------------
/** @internal */
export const iso = (get, reverseGet) => ({
get,
reverseGet
});
/** @internal */
export const isoAsLens = (sa) => lens(sa.get, flow(sa.reverseGet, constant));
/** @internal */
export const isoAsPrism = (sa) => prism(flow(sa.get, O.some), sa.reverseGet);
/** @internal */
export const isoAsOptional = (sa) => optional(flow(sa.get, O.some), flow(sa.reverseGet, constant));
/** @internal */
export const isoAsTraversal = (sa) => traversal((F) => (f) => (s) =>
// @ts-expect-error
F.map(f(sa.get(s)), (a) => sa.reverseGet(a)));
// -------------------------------------------------------------------------------------
// Lens
// -------------------------------------------------------------------------------------
/** @internal */
export const lens = (get, set) => ({ get, set });
/** @internal */
export const lensAsOptional = (sa) => optional(flow(sa.get, O.some), sa.set);
/** @internal */
export const lensAsTraversal = (sa) =>
// @ts-expect-error
traversal((F) => (f) => (s) => F.map(f(sa.get(s)), (a) => sa.set(a)(s)));
/** @internal */
export const lensComposeLens = (ab) => (sa) => lens((s) => ab.get(sa.get(s)), (b) => (s) => sa.set(ab.set(b)(sa.get(s)))(s));
/** @internal */
export const prismComposePrism = (ab) => (sa) => prism(flow(sa.getOption, O.chain(ab.getOption)), flow(ab.reverseGet, sa.reverseGet));
/** @internal */
export const lensComposePrism = (ab) => (sa) => optionalComposeOptional(prismAsOptional(ab))(lensAsOptional(sa));
/** @internal */
export const lensId = () => lens(identity, constant);
/** @internal */
export const lensProp = (prop) => (sa) => lens((s) => sa.get(s)[prop], (ap) => (s) => {
const oa = sa.get(s);
if (ap === oa[prop]) {
return s;
}
return sa.set(Object.assign({}, oa, { [prop]: ap }))(s);
});
/** @internal */
export const lensProps = (...props) => (sa) => lens((s) => {
const a = sa.get(s);
const r = {};
for (const k of props) {
r[k] = a[k];
}
return r;
}, (a) => (s) => {
const oa = sa.get(s);
for (const k of props) {
if (a[k] !== oa[k]) {
return sa.set(Object.assign({}, oa, a))(s);
}
}
return s;
});
/** @internal */
export const lensComponent = (prop) => (sa) => lens((s) => sa.get(s)[prop], (ap) => (s) => {
const oa = sa.get(s);
if (ap === oa[prop]) {
return s;
}
const copy = oa.slice();
copy[prop] = ap;
return sa.set(copy)(s);
});
/** @internal */
export const lensAtKey = (key) => (sa) => pipe(sa, lensComposeLens(atReadonlyRecord().at(key)));
// -------------------------------------------------------------------------------------
// Prism
// -------------------------------------------------------------------------------------
/** @internal */
export const prism = (getOption, reverseGet) => ({ getOption, reverseGet });
/** @internal */
export const prismAsOptional = (sa) => optional(sa.getOption, (a) => prismSet(a)(sa));
/** @internal */
export const prismAsTraversal = (sa) => traversal((F) => (f) => (s) => pipe(sa.getOption(s), O.fold(() => F.of(s),
// @ts-expect-error
(a) => F.map(f(a), (a) => prismSet(a)(sa)(s)))));
/** @internal */
export const prismModifyOption = (f) => (sa) => (s) => pipe(sa.getOption(s), O.map((o) => {
const n = f(o);
return n === o ? s : sa.reverseGet(n);
}));
/** @internal */
export const prismModify = (f) => (sa) => {
const g = prismModifyOption(f)(sa);
return (s) => pipe(g(s), O.getOrElse(() => s));
};
/** @internal */
export const prismSet = (a) => prismModify(() => a);
/** @internal */
export const prismComposeLens = (ab) => (sa) => optionalComposeOptional(lensAsOptional(ab))(prismAsOptional(sa));
/** @internal */
export const prismFromNullable = () => prism(O.fromNullable, identity);
/** @internal */
export const prismFromPredicate = (predicate) => prism(O.fromPredicate(predicate), identity);
/** @internal */
export const prismSome = () => prism(identity, O.some);
/** @internal */
export const prismRight = () => prism(O.fromEither, E.right);
/** @internal */
export const prismLeft = () => prism((s) => (E.isLeft(s) ? O.some(s.left) : O.none), // TODO: replace with E.getLeft in v3
E.left);
// -------------------------------------------------------------------------------------
// Optional
// -------------------------------------------------------------------------------------
/** @internal */
export const optional = (getOption, set) => ({
getOption,
set
});
/** @internal */
export const optionalAsTraversal = (sa) => traversal((F) => (f) => (s) => pipe(sa.getOption(s), O.fold(() => F.of(s), (a) => F.map(f(a), (a) => sa.set(a)(s)))));
/** @internal */
export const optionalModifyOption = (f) => (optional) => (s) => pipe(optional.getOption(s), O.map((a) => {
const n = f(a);
return n === a ? s : optional.set(n)(s);
}));
/** @internal */
export const optionalModify = (f) => (optional) => {
const g = optionalModifyOption(f)(optional);
return (s) => pipe(g(s), O.getOrElse(() => s));
};
/** @internal */
export const optionalComposeOptional = (ab) => (sa) => optional(flow(sa.getOption, O.chain(ab.getOption)), (b) => optionalModify(ab.set(b))(sa));
/** @internal */
export const optionalIndex = (i) => (sa) => pipe(sa, optionalComposeOptional(indexReadonlyArray().index(i)));
/** @internal */
export const optionalIndexNonEmpty = (i) => (sa) => pipe(sa, optionalComposeOptional(indexReadonlyNonEmptyArray().index(i)));
/** @internal */
export const optionalKey = (key) => (sa) => pipe(sa, optionalComposeOptional(indexReadonlyRecord().index(key)));
/** @internal */
export const optionalFindFirst = (predicate) => optional(RA.findFirst(predicate), (a) => (s) => pipe(RA.findIndex(predicate)(s), O.fold(() => s, (i) => RA.unsafeUpdateAt(i, a, s))));
const unsafeUpdateAt = (i, a, as) => {
if (as[i] === a) {
return as;
}
else {
const xs = [as[0], ...as.slice(1)];
xs[i] = a;
return xs;
}
};
/** @internal */
export const optionalFindFirstNonEmpty = (predicate) => optional(RA.findFirst(predicate), (a) => (as) => pipe(RA.findIndex(predicate)(as), O.fold(() => as, (i) => unsafeUpdateAt(i, a, as))));
// -------------------------------------------------------------------------------------
// Traversal
// -------------------------------------------------------------------------------------
/** @internal */
export const traversal = (modifyF) => ({
modifyF
});
/** @internal */
export function traversalComposeTraversal(ab) {
return (sa) => traversal((F) => (f) => sa.modifyF(F)(ab.modifyF(F)(f)));
}
/** @internal */
export const ApplicativeIdentity = {
URI: 'Identity',
map: (fa, f) => f(fa),
of: identity,
ap:
/* istanbul ignore next */
(fab, fa) => fab(fa)
};
const isIdentity = (F) => F.URI === 'Identity';
export function fromTraversable(T) {
return () => traversal((F) => {
// if `F` is `Identity` then `traverseF = map`
const traverseF = isIdentity(F)
? T.map
: T.traverse(F);
return (f) => (s) => traverseF(s, f);
});
}
/** @internal */
export function traversalTraverse(T) {
return traversalComposeTraversal(fromTraversable(T)());
}
// -------------------------------------------------------------------------------------
// Ix
// -------------------------------------------------------------------------------------
/** @internal */
export const index = (index) => ({ index });
/** @internal */
export const indexReadonlyArray = () => index((i) => optional((as) => RA.lookup(i, as), (a) => (as) => pipe(RA.lookup(i, as), O.fold(() => as, () => RA.unsafeUpdateAt(i, a, as)))));
/** @internal */
export const indexReadonlyNonEmptyArray = () => index((i) => optional((as) => RA.lookup(i, as), (a) => (as) => pipe(RA.lookup(i, as), O.fold(() => as, () => unsafeUpdateAt(i, a, as)))));
/** @internal */
export const indexReadonlyRecord = () => index((k) => optional((r) => RR.lookup(k, r), (a) => (r) => {
if (r[k] === a || O.isNone(RR.lookup(k, r))) {
return r;
}
return RR.insertAt(k, a)(r);
}));
// -------------------------------------------------------------------------------------
// At
// -------------------------------------------------------------------------------------
/** @internal */
export const at = (at) => ({ at });
/** @internal */
export function atReadonlyRecord() {
return at((key) => lens((r) => RR.lookup(key, r), O.fold(() => RR.deleteAt(key), (a) => RR.insertAt(key, a))));
}