UNPKG

monocle-ts-esm

Version:

A porting of scala monocle library to TypeScript

1,286 lines (1,285 loc) 27.9 kB
/** * @since 1.0.0 */ import * as A from 'fp-ts-esm/Array'; import { getApplicative, make } from 'fp-ts-esm/Const'; import { constant, identity } from 'fp-ts-esm/function'; import { monoidAll, monoidAny } from 'fp-ts-esm/Monoid'; import { fromNullable, fromPredicate, getFirstMonoid, isNone, option, some } from 'fp-ts-esm/Option'; import { pipe } from 'fp-ts-esm/pipeable'; import * as at from './At.js'; import * as iso from './Iso.js'; import * as index from './Ix.js'; import * as lens from './Lens.js'; import * as optional from './Optional.js'; import * as prism from './Prism.js'; import * as traversal from './Traversal.js'; export { /** * @since 2.3.0 */ at, /** * @since 2.3.0 */ iso, /** * @since 2.3.0 */ index, /** * @since 2.3.0 */ lens, /** * @since 2.3.0 */ prism, /** * @since 2.3.0 */ optional, /** * @since 2.3.0 */ traversal }; // // compat // const fromIso = (iso) => new Iso(iso.get, iso.reverseGet); const fromLens = (lens) => new Lens(lens.get, lens.set); const fromPrism = (prism) => new Prism(prism.getOption, prism.reverseGet); const fromOptional = (optional) => new Optional(optional.getOption, optional.set); const fromTraversal = (traversal) => new Traversal(traversal.modifyF); const fromAt = (at) => new At((i) => fromLens(at.at(i))); const fromIndex = (ix) => new Index((i) => fromOptional(ix.index(i))); // // old APIs // const update = (o, k, a) => { return a === o[k] ? o : Object.assign({}, o, { [k]: a }); }; /** * Laws: * 1. `reverseGet(get(s)) = s` * 2. `get(reversetGet(a)) = a` * * @category constructor * @since 1.0.0 */ export class Iso { constructor(get, reverseGet) { this.get = get; this.reverseGet = reverseGet; /** * @since 1.0.0 */ this._tag = 'Iso'; /** * @since 1.0.0 */ this.unwrap = this.get; /** * @since 1.0.0 */ this.to = this.get; /** * @since 1.0.0 */ this.wrap = this.reverseGet; /** * @since 1.0.0 */ this.from = this.reverseGet; } /** * reverse the `Iso`: the source becomes the target and the target becomes the source * @since 1.0.0 */ reverse() { return fromIso(iso.reverse(this)); } /** * @since 1.0.0 */ modify(f) { return iso.modify(f)(this); } /** * view an `Iso` as a `Lens` * * @since 1.0.0 */ asLens() { return fromLens(iso.asLens(this)); } /** * view an `Iso` as a `Prism` * * @since 1.0.0 */ asPrism() { return fromPrism(iso.asPrism(this)); } /** * view an `Iso` as a `Optional` * * @since 1.0.0 */ asOptional() { return fromOptional(iso.asOptional(this)); } /** * view an `Iso` as a `Traversal` * * @since 1.0.0 */ asTraversal() { return fromTraversal(iso.asTraversal(this)); } /** * view an `Iso` as a `Fold` * * @since 1.0.0 */ asFold() { return new Fold(() => (f) => (s) => f(this.get(s))); } /** * view an `Iso` as a `Getter` * * @since 1.0.0 */ asGetter() { return new Getter((s) => this.get(s)); } /** * view an `Iso` as a `Setter` * * @since 1.0.0 */ asSetter() { return new Setter((f) => this.modify(f)); } /** * compose an `Iso` with an `Iso` * * @since 1.0.0 */ compose(ab) { return fromIso(iso.compose(ab)(this)); } /** * Alias of `compose` * * @since 1.0.0 */ composeIso(ab) { return this.compose(ab); } /** * compose an `Iso` with a `Lens ` * * @since 1.0.0 */ composeLens(ab) { return fromLens(pipe(this, iso.asLens, lens.compose(ab))); } /** * compose an `Iso` with a `Prism` * * @since 1.0.0 */ composePrism(ab) { return fromPrism(pipe(this, iso.asPrism, prism.compose(ab))); } /** * compose an `Iso` with an `Optional` * * @since 1.0.0 */ composeOptional(ab) { return fromOptional(pipe(this, iso.asOptional, optional.compose(ab))); } /** * compose an `Iso` with a `Traversal` * * @since 1.0.0 */ composeTraversal(ab) { return fromTraversal(pipe(this, iso.asTraversal, traversal.compose(ab))); } /** * compose an `Iso` with a `Fold` * * @since 1.0.0 */ composeFold(ab) { return this.asFold().compose(ab); } /** * compose an `Iso` with a `Getter` * * @since 1.0.0 */ composeGetter(ab) { return this.asGetter().compose(ab); } /** * compose an `Iso` with a `Setter` * * @since 1.0.0 */ composeSetter(ab) { return this.asSetter().compose(ab); } } /** * Laws: * 1. `get(set(a)(s)) = a` * 2. `set(get(s))(s) = s` * 3. `set(a)(set(a)(s)) = set(a)(s)` * * @category constructor * @since 1.0.0 */ export class Lens { constructor(get, set) { this.get = get; this.set = set; /** * @since 1.0.0 */ this._tag = 'Lens'; } /** * @example * import { Lens } from 'monocle-ts' * * type Person = { * name: string * age: number * address: { * city: string * } * } * * const city = Lens.fromPath<Person>()(['address', 'city']) * * const person: Person = { name: 'Giulio', age: 43, address: { city: 'Milan' } } * * assert.strictEqual(city.get(person), 'Milan') * assert.deepStrictEqual(city.set('London')(person), { name: 'Giulio', age: 43, address: { city: 'London' } }) * * @since 1.0.0 */ static fromPath() { const fromProp = Lens.fromProp(); return (path) => { const lens = fromProp(path[0]); return path.slice(1).reduce((acc, prop) => acc.compose(fromProp(prop)), lens); }; } /** * Returns a `Lens` from a type and a prop * * @example * import { Lens } from 'monocle-ts' * * type Person = { * name: string * age: number * } * * const age = Lens.fromProp<Person>()('age') * * const person: Person = { name: 'Giulio', age: 43 } * * assert.strictEqual(age.get(person), 43) * assert.deepStrictEqual(age.set(44)(person), { name: 'Giulio', age: 44 }) * * @since 1.0.0 */ static fromProp() { return (prop) => fromLens(pipe(lens.id(), lens.prop(prop))); } static fromProps() { return (props) => fromLens(pipe(lens.id(), lens.props(...props))); } /** * Returns a `Lens` from a nullable (`A | null | undefined`) prop * * @example * import { Lens } from 'monocle-ts' * * interface Outer { * inner?: Inner * } * * interface Inner { * value: number * foo: string * } * * const inner = Lens.fromNullableProp<Outer>()('inner', { value: 0, foo: 'foo' }) * const value = Lens.fromProp<Inner>()('value') * const lens = inner.compose(value) * * assert.deepStrictEqual(lens.set(1)({}), { inner: { value: 1, foo: 'foo' } }) * assert.strictEqual(lens.get({}), 0) * assert.deepStrictEqual(lens.set(1)({ inner: { value: 1, foo: 'bar' } }), { inner: { value: 1, foo: 'bar' } }) * assert.strictEqual(lens.get({ inner: { value: 1, foo: 'bar' } }), 1) * * @since 1.0.0 */ static fromNullableProp() { return (k, defaultValue) => new Lens((s) => { const osk = fromNullable(s[k]); if (isNone(osk)) { return defaultValue; } else { return osk.value; } }, (a) => (s) => update(s, k, a)); } /** * @since 1.0.0 */ modify(f) { return lens.modify(f)(this); } /** * view a `Lens` as a Optional * * @since 1.0.0 */ asOptional() { return fromOptional(lens.asOptional(this)); } /** * view a `Lens` as a `Traversal` * * @since 1.0.0 */ asTraversal() { return fromTraversal(lens.asTraversal(this)); } /** * view a `Lens` as a `Setter` * * @since 1.0.0 */ asSetter() { return new Setter((f) => this.modify(f)); } /** * view a `Lens` as a `Getter` * * @since 1.0.0 */ asGetter() { return new Getter((s) => this.get(s)); } /** * view a `Lens` as a `Fold` * * @since 1.0.0 */ asFold() { return new Fold(() => (f) => (s) => f(this.get(s))); } /** * compose a `Lens` with a `Lens` * * @since 1.0.0 */ compose(ab) { return fromLens(lens.compose(ab)(this)); } /** * Alias of `compose` * * @since 1.0.0 */ composeLens(ab) { return this.compose(ab); } /** * compose a `Lens` with a `Getter` * * @since 1.0.0 */ composeGetter(ab) { return this.asGetter().compose(ab); } /** * compose a `Lens` with a `Fold` * * @since 1.0.0 */ composeFold(ab) { return this.asFold().compose(ab); } /** * compose a `Lens` with an `Optional` * * @since 1.0.0 */ composeOptional(ab) { return fromOptional(pipe(this, lens.asOptional, optional.compose(ab))); } /** * compose a `Lens` with an `Traversal` * * @since 1.0.0 */ composeTraversal(ab) { return fromTraversal(pipe(this, lens.asTraversal, traversal.compose(ab))); } /** * compose a `Lens` with an `Setter` * * @since 1.0.0 */ composeSetter(ab) { return this.asSetter().compose(ab); } /** * compose a `Lens` with an `Iso` * * @since 1.0.0 */ composeIso(ab) { return fromLens(pipe(this, lens.compose(pipe(ab, iso.asLens)))); } /** * compose a `Lens` with a `Prism` * * @since 1.0.0 */ composePrism(ab) { return fromOptional(lens.composePrism(ab)(this)); } } /** * Laws: * 1. `pipe(getOption(s), fold(() => s, reverseGet)) = s` * 2. `getOption(reverseGet(a)) = some(a)` * * @category constructor * @since 1.0.0 */ export class Prism { constructor(getOption, reverseGet) { this.getOption = getOption; this.reverseGet = reverseGet; /** * @since 1.0.0 */ this._tag = 'Prism'; } static fromPredicate(predicate) { return fromPrism(prism.fromPredicate(predicate)); } /** * @since 1.0.0 */ static some() { return somePrism; } /** * @since 1.0.0 */ modify(f) { return (s) => { const os = this.modifyOption(f)(s); if (isNone(os)) { return s; } else { return os.value; } }; } /** * @since 1.0.0 */ modifyOption(f) { return (s) => option.map(this.getOption(s), (v) => { // @ts-expect-error const n = f(v); return n === v ? s : this.reverseGet(n); }); } /** * set the target of a `Prism` with a value * * @since 1.0.0 */ set(a) { return this.modify(() => a); } /** * view a `Prism` as a `Optional` * * @since 1.0.0 */ asOptional() { return fromOptional(prism.asOptional(this)); } /** * view a `Prism` as a `Traversal` * * @since 1.0.0 */ asTraversal() { return fromTraversal(prism.asTraversal(this)); } /** * view a `Prism` as a `Setter` * * @since 1.0.0 */ asSetter() { return new Setter((f) => this.modify(f)); } /** * view a `Prism` as a `Fold` * * @since 1.0.0 */ asFold() { return new Fold((M) => (f) => (s) => { const oa = this.getOption(s); return isNone(oa) ? M.empty : f(oa.value); }); } /** * compose a `Prism` with a `Prism` * * @since 1.0.0 */ compose(ab) { return fromPrism(prism.compose(ab)(this)); } /** * Alias of `compose` * * @since 1.0.0 */ composePrism(ab) { return this.compose(ab); } /** * compose a `Prism` with a `Optional` * * @since 1.0.0 */ composeOptional(ab) { return fromOptional(pipe(this, prism.asOptional, optional.compose(ab))); } /** * compose a `Prism` with a `Traversal` * * @since 1.0.0 */ composeTraversal(ab) { return fromTraversal(pipe(this, prism.asTraversal, traversal.compose(ab))); } /** * compose a `Prism` with a `Fold` * * @since 1.0.0 */ composeFold(ab) { return this.asFold().compose(ab); } /** * compose a `Prism` with a `Setter` * * @since 1.0.0 */ composeSetter(ab) { return this.asSetter().compose(ab); } /** * compose a `Prism` with a `Iso` * * @since 1.0.0 */ composeIso(ab) { return fromPrism(pipe(this, prism.compose(pipe(ab, iso.asPrism)))); } /** * compose a `Prism` with a `Lens` * * @since 1.0.0 */ composeLens(ab) { return fromOptional(prism.composeLens(ab)(this)); } /** * compose a `Prism` with a `Getter` * * @since 1.0.0 */ composeGetter(ab) { return this.asFold().compose(ab.asFold()); } } const somePrism = /*#__PURE__*/ new Prism(identity, some); /** * Laws: * 1. `pipe(getOption(s), fold(() => s, a => set(a)(s))) = s` * 2. `getOption(set(a)(s)) = pipe(getOption(s), map(_ => a))` * 3. `set(a)(set(a)(s)) = set(a)(s)` * * @category constructor * @since 1.0.0 */ export class Optional { constructor(getOption, set) { this.getOption = getOption; this.set = set; /** * @since 1.0.0 */ this._tag = 'Optional'; } /** * Returns an `Optional` from a nullable (`A | null | undefined`) prop * * @example * import { Optional } from 'monocle-ts' * * interface Phone { * number: string * } * interface Employment { * phone?: Phone * } * interface Info { * employment?: Employment * } * interface Response { * info?: Info * } * * const numberFromResponse = Optional.fromPath<Response>()(['info', 'employment', 'phone', 'number']) * * const response1: Response = { * info: { * employment: { * phone: { * number: '555-1234' * } * } * } * } * const response2: Response = { * info: { * employment: {} * } * } * * numberFromResponse.getOption(response1) // some('555-1234') * numberFromResponse.getOption(response2) // none * * @since 2.1.0 */ static fromPath() { const fromNullableProp = Optional.fromNullableProp(); return (path) => { const optional = fromNullableProp(path[0]); return path.slice(1).reduce((acc, prop) => acc.compose(fromNullableProp(prop)), optional); }; } /** * @example * import { Optional } from 'monocle-ts' * * interface S { * a: number | undefined | null * } * * const optional = Optional.fromNullableProp<S>()('a') * * const s1: S = { a: undefined } * const s2: S = { a: null } * const s3: S = { a: 1 } * * assert.deepStrictEqual(optional.set(2)(s1), s1) * assert.deepStrictEqual(optional.set(2)(s2), s2) * assert.deepStrictEqual(optional.set(2)(s3), { a: 2 }) * * @since 1.0.0 */ static fromNullableProp() { return (k) => new Optional((s) => fromNullable(s[k]), (a) => (s) => (s[k] == null ? s : update(s, k, a))); } /** * Returns an `Optional` from an option (`Option<A>`) prop * * @example * import { Optional } from 'monocle-ts' * import * as O from 'fp-ts/Option' * * interface S { * a: O.Option<number> * } * * const optional = Optional.fromOptionProp<S>()('a') * const s1: S = { a: O.none } * const s2: S = { a: O.some(1) } * assert.deepStrictEqual(optional.set(2)(s1), s1) * assert.deepStrictEqual(optional.set(2)(s2), { a: O.some(2) }) * * @since 1.0.0 */ static fromOptionProp() { const formProp = Lens.fromProp(); return (prop) => formProp(prop).composePrism(somePrism); } /** * @since 1.0.0 */ modify(f) { return optional.modify(f)(this); } /** * @since 1.0.0 */ modifyOption(f) { return optional.modifyOption(f)(this); } /** * view a `Optional` as a `Traversal` * * @since 1.0.0 */ asTraversal() { return fromTraversal(optional.asTraversal(this)); } /** * view an `Optional` as a `Fold` * * @since 1.0.0 */ asFold() { return new Fold((M) => (f) => (s) => { const oa = this.getOption(s); return isNone(oa) ? M.empty : f(oa.value); }); } /** * view an `Optional` as a `Setter` * * @since 1.0.0 */ asSetter() { return new Setter((f) => this.modify(f)); } /** * compose a `Optional` with a `Optional` * * @since 1.0.0 */ compose(ab) { return fromOptional(optional.compose(ab)(this)); } /** * Alias of `compose` * * @since 1.0.0 */ composeOptional(ab) { return this.compose(ab); } /** * compose an `Optional` with a `Traversal` * * @since 1.0.0 */ composeTraversal(ab) { return fromTraversal(pipe(this, optional.asTraversal, traversal.compose(ab))); } /** * compose an `Optional` with a `Fold` * * @since 1.0.0 */ composeFold(ab) { return this.asFold().compose(ab); } /** * compose an `Optional` with a `Setter` * * @since 1.0.0 */ composeSetter(ab) { return this.asSetter().compose(ab); } /** * compose an `Optional` with a `Lens` * * @since 1.0.0 */ composeLens(ab) { return fromOptional(pipe(this, optional.compose(pipe(ab, lens.asOptional)))); } /** * compose an `Optional` with a `Prism` * * @since 1.0.0 */ composePrism(ab) { return fromOptional(pipe(this, optional.compose(pipe(ab, prism.asOptional)))); } /** * compose an `Optional` with a `Iso` * * @since 1.0.0 */ composeIso(ab) { return fromOptional(pipe(this, optional.compose(pipe(ab, iso.asOptional)))); } /** * compose an `Optional` with a `Getter` * * @since 1.0.0 */ composeGetter(ab) { return this.asFold().compose(ab.asFold()); } } /** * @category constructor * @since 1.0.0 */ export class Traversal { constructor( // Van Laarhoven representation modifyF) { this.modifyF = modifyF; /** * @since 1.0.0 */ this._tag = 'Traversal'; } /** * @since 1.0.0 */ modify(f) { return traversal.modify(f)(this); } /** * @since 1.0.0 */ set(a) { return traversal.set(a)(this); } filter(predicate) { return fromTraversal(traversal.filter(predicate)(this)); } /** * view a `Traversal` as a `Fold` * * @since 1.0.0 */ asFold() { return new Fold((M) => (f) => this.modifyF(getApplicative(M))((a) => make(f(a)))); } /** * view a `Traversal` as a `Setter` * * @since 1.0.0 */ asSetter() { return new Setter((f) => this.modify(f)); } /** * compose a `Traversal` with a `Traversal` * * @since 1.0.0 */ compose(ab) { return fromTraversal(traversal.compose(ab)(this)); } /** * Alias of `compose` * * @since 1.0.0 */ composeTraversal(ab) { return this.compose(ab); } /** * compose a `Traversal` with a `Fold` * * @since 1.0.0 */ composeFold(ab) { return this.asFold().compose(ab); } /** * compose a `Traversal` with a `Setter` * * @since 1.0.0 */ composeSetter(ab) { return this.asSetter().compose(ab); } /** * compose a `Traversal` with a `Optional` * * @since 1.0.0 */ composeOptional(ab) { return this.compose(ab.asTraversal()); } /** * compose a `Traversal` with a `Lens` * * @since 1.0.0 */ composeLens(ab) { return fromTraversal(pipe(this, traversal.compose(pipe(ab, lens.asTraversal)))); } /** * compose a `Traversal` with a `Prism` * * @since 1.0.0 */ composePrism(ab) { return fromTraversal(pipe(this, traversal.compose(pipe(ab, prism.asTraversal)))); } /** * compose a `Traversal` with a `Iso` * * @since 1.0.0 */ composeIso(ab) { return fromTraversal(pipe(this, traversal.compose(pipe(ab, iso.asTraversal)))); } /** * compose a `Traversal` with a `Getter` * * @since 1.0.0 */ composeGetter(ab) { return this.asFold().compose(ab.asFold()); } } /** * @category constructor * @since 1.2.0 */ export class At { constructor(at) { this.at = at; /** * @since 1.0.0 */ this._tag = 'At'; } /** * lift an instance of `At` using an `Iso` * * @since 1.2.0 */ fromIso(iso) { return fromAt(at.fromIso(iso)(this)); } } /** * @category constructor * @since 1.2.0 */ export class Index { constructor(index) { this.index = index; /** * @since 1.0.0 */ this._tag = 'Index'; } /** * @since 1.2.0 */ static fromAt(at) { return fromIndex(index.fromAt(at)); } /** * lift an instance of `Index` using an `Iso` * * @since 1.2.0 */ fromIso(iso) { return fromIndex(index.fromIso(iso)(this)); } } /** * @category constructor * @since 1.0.0 */ export class Getter { constructor(get) { this.get = get; /** * @since 1.0.0 */ this._tag = 'Getter'; } /** * view a `Getter` as a `Fold` * * @since 1.0.0 */ asFold() { return new Fold(() => (f) => (s) => f(this.get(s))); } /** * compose a `Getter` with a `Getter` * * @since 1.0.0 */ compose(ab) { return new Getter((s) => ab.get(this.get(s))); } /** * Alias of `compose` * * @since 1.0.0 */ composeGetter(ab) { return this.compose(ab); } /** * compose a `Getter` with a `Fold` * * @since 1.0.0 */ composeFold(ab) { return this.asFold().compose(ab); } /** * compose a `Getter` with a `Lens` * * @since 1.0.0 */ composeLens(ab) { return this.compose(ab.asGetter()); } /** * compose a `Getter` with a `Iso` * * @since 1.0.0 */ composeIso(ab) { return this.compose(ab.asGetter()); } /** * compose a `Getter` with a `Optional` * * @since 1.0.0 */ composeTraversal(ab) { return this.asFold().compose(ab.asFold()); } /** * compose a `Getter` with a `Optional` * * @since 1.0.0 */ composeOptional(ab) { return this.asFold().compose(ab.asFold()); } /** * compose a `Getter` with a `Prism` * * @since 1.0.0 */ composePrism(ab) { return this.asFold().compose(ab.asFold()); } } /** * @category constructor * @since 1.0.0 */ export class Fold { constructor(foldMap) { this.foldMap = foldMap; /** * @since 1.0.0 */ this._tag = 'Fold'; this.getAll = foldMap(A.getMonoid())(A.of); this.exist = foldMap(monoidAny); this.all = foldMap(monoidAll); this.foldMapFirst = foldMap(getFirstMonoid()); } /** * compose a `Fold` with a `Fold` * * @since 1.0.0 */ compose(ab) { return new Fold((M) => (f) => this.foldMap(M)(ab.foldMap(M)(f))); } /** * Alias of `compose` * * @since 1.0.0 */ composeFold(ab) { return this.compose(ab); } /** * compose a `Fold` with a `Getter` * * @since 1.0.0 */ composeGetter(ab) { return this.compose(ab.asFold()); } /** * compose a `Fold` with a `Traversal` * * @since 1.0.0 */ composeTraversal(ab) { return this.compose(ab.asFold()); } /** * compose a `Fold` with a `Optional` * * @since 1.0.0 */ composeOptional(ab) { return this.compose(ab.asFold()); } /** * compose a `Fold` with a `Lens` * * @since 1.0.0 */ composeLens(ab) { return this.compose(ab.asFold()); } /** * compose a `Fold` with a `Prism` * * @since 1.0.0 */ composePrism(ab) { return this.compose(ab.asFold()); } /** * compose a `Fold` with a `Iso` * * @since 1.0.0 */ composeIso(ab) { return this.compose(ab.asFold()); } find(p) { return this.foldMapFirst(fromPredicate(p)); } /** * get the first target of a `Fold` * * @since 1.0.0 */ headOption(s) { return this.find(() => true)(s); } } /** * @category constructor * @since 1.0.0 */ export class Setter { constructor(modify) { this.modify = modify; /** * @since 1.0.0 */ this._tag = 'Setter'; } /** * @since 1.0.0 */ set(a) { return this.modify(constant(a)); } /** * compose a `Setter` with a `Setter` * * @since 1.0.0 */ compose(ab) { return new Setter((f) => this.modify(ab.modify(f))); } /** * Alias of `compose` * * @since 1.0.0 */ composeSetter(ab) { return this.compose(ab); } /** * compose a `Setter` with a `Traversal` * * @since 1.0.0 */ composeTraversal(ab) { return this.compose(ab.asSetter()); } /** * compose a `Setter` with a `Optional` * * @since 1.0.0 */ composeOptional(ab) { return this.compose(ab.asSetter()); } /** * compose a `Setter` with a `Lens` * * @since 1.0.0 */ composeLens(ab) { return this.compose(ab.asSetter()); } /** * compose a `Setter` with a `Prism` * * @since 1.0.0 */ composePrism(ab) { return this.compose(ab.asSetter()); } /** * compose a `Setter` with a `Iso` * * @since 1.0.0 */ composeIso(ab) { return this.compose(ab.asSetter()); } } export function fromTraversable(T) { const f = traversal.fromTraversable(T); return () => fromTraversal(f()); } export function fromFoldable(F) { return () => new Fold((M) => { const foldMapFM = F.foldMap(M); return (f) => (s) => foldMapFM(s, f); }); }