// ets_tracing: off import { Tagged } from "@effect-ts/core/Case" import type { Array } from "@effect-ts/core/Collections/Immutable/Array" import * as A from "@effect-ts/core/Collections/Immutable/Array" import * as R from "@effect-ts/core/Collections/Immutable/Dictionary" import * as E from "@effect-ts/core/Either" import type { Predicate } from "@effect-ts/core/Function" import { constant, flow, identity, pipe } from "@effect-ts/core/Function" import * as O from "@effect-ts/core/Option" import type * as P from "@effect-ts/core/Prelude" import * as DSL from "@effect-ts/core/Prelude/DSL" import * as HM from "@effect-ts/system/Collections/Immutable/HashMap" import { matchTag_ } from "@effect-ts/system/Utils" import type { At } from "../At/index.js" import type { Index } from "../Ix/index.js" export class Lens extends Tagged("Lens")<{ readonly get: (s: S) => A readonly set: (a: A) => (s: S) => S }> { readonly [">>>"]: { (ab: Iso): Lens (ab: Lens): Lens (ab: Prism): Optional (ab: Optional): Optional (ab: Traversal): Traversal } = (ab) => // @ts-expect-error matchTag_(ab, { Iso: (ab) => lensComposeLens(isoAsLens(ab))(this), Lens: (ab) => lensComposeLens(ab)(this), Optional: (ab) => optionalComposeOptional(ab)(lensAsOptional(this)), Prism: (ab) => lensComposePrism(ab)(this), Traversal: (ab) => traversalComposeTraversal(ab)(lensAsTraversal(this)) }) } export class Prism extends Tagged("Prism")<{ readonly getOption: (s: S) => O.Option readonly reverseGet: (a: A) => S }> { readonly [">>>"]: { (ab: Iso): Prism (ab: Lens): Optional (ab: Prism): Prism (ab: Optional): Optional (ab: Traversal): Traversal } = (ab) => //@ts-expect-error matchTag_(ab, { Iso: (ab) => composePrism(isoAsPrism(ab))(this), Lens: (ab) => prismComposeLens(ab)(this), Optional: (ab) => optionalComposeOptional(ab)(prismAsOptional(this)), Prism: (ab) => composePrism(ab)(this), Traversal: (ab) => traversalComposeTraversal(ab)(prismAsTraversal(this)) }) } export class Optional extends Tagged("Optional")<{ readonly getOption: (s: S) => O.Option readonly set: (a: A) => (s: S) => S }> { readonly [">>>"]: { (ab: Iso): Optional (ab: Lens): Optional (ab: Prism): Optional (ab: Optional): Optional (ab: Traversal): Traversal } = (ab) => // @ts-expect-error matchTag_(ab, { Iso: (ab) => optionalComposeOptional(isoAsOptional(ab))(this), Lens: (ab) => optionalComposeOptional(lensAsOptional(ab))(this), Optional: (ab) => optionalComposeOptional(ab)(this), Prism: (ab) => optionalComposeOptional(prismAsOptional(ab))(this), Traversal: (ab) => traversalComposeTraversal(ab)(optionalAsTraversal(this)) }) } export class Iso extends Tagged("Iso")<{ readonly get: (s: S) => A readonly reverseGet: (a: A) => S }> { readonly [">>>"]: { (ab: Iso): Iso (ab: Lens): Lens (ab: Prism): Prism (ab: Optional): Optional (ab: Traversal): Traversal } = (ab) => // @ts-expect-error matchTag_(ab, { Iso: (ab) => isoComposeIso(ab)(this), Lens: (ab) => lensComposeLens(ab)(isoAsLens(this)), Optional: (ab) => optionalComposeOptional(ab)(isoAsOptional(this)), Prism: (ab) => composePrism(ab)(isoAsPrism(this)), Traversal: (ab) => traversalComposeTraversal(ab)(isoAsTraversal(this)) }) } export interface ModifyF { (F: P.Applicative): < FK, FQ, FW, FX, FI, FS, FR, FE >( f: (a: A) => P.Kind ) => (s: S) => P.Kind } export class Traversal extends Tagged("Traversal")<{ readonly modifyF: ModifyF }> { readonly [">>>"]: { (ab: Iso): Traversal (ab: Lens): Traversal (ab: Prism): Traversal (ab: Optional): Traversal (ab: Traversal): Traversal } = (ab) => matchTag_(ab, { Iso: (ab) => traversalComposeTraversal(isoAsTraversal(ab))(this), Lens: (ab) => traversalComposeTraversal(lensAsTraversal(ab))(this), Optional: (ab) => traversalComposeTraversal(optionalAsTraversal(ab))(this), Prism: (ab) => traversalComposeTraversal(prismAsTraversal(ab))(this), Traversal: (ab) => traversalComposeTraversal(ab)(this) }) } // ------------------------------------------------------------------------------------- // Iso // ------------------------------------------------------------------------------------- export const isoAsLens = (sa: Iso): Lens => new Lens({ get: sa.get, set: flow(sa.reverseGet, constant) }) export const isoAsOptional = (sa: Iso): Optional => new Optional({ getOption: flow(sa.get, O.some), set: flow(sa.reverseGet, constant) }) /** * Compose an `Iso` with an `Iso` */ export const isoComposeIso = (ab: Iso) => (sa: Iso): Iso => new Iso({ get: flow(sa.get, ab.get), reverseGet: flow(ab.reverseGet, sa.reverseGet) }) /** * View an `Iso` as a `Prism` */ export const isoAsPrism = (sa: Iso): Prism => new Prism({ getOption: flow(sa.get, O.some), reverseGet: sa.reverseGet }) export const isoAsTraversal = (sa: Iso): Traversal => new Traversal({ modifyF: (F: P.Applicative) => ( f: (a: A) => P.Kind ) => (s: S): P.Kind => pipe( f(sa.get(s)), F.map((a) => sa.reverseGet(a)) ) }) // ------------------------------------------------------------------------------------- // Lens // ------------------------------------------------------------------------------------- export const lensAsOptional = (sa: Lens): Optional => new Optional({ getOption: flow(sa.get, O.some), set: sa.set }) export const lensAsTraversal = (sa: Lens): Traversal => new Traversal({ modifyF: (F: P.Applicative) => ( f: (a: A) => P.Kind ) => (s: S): P.Kind => pipe( f(sa.get(s)), F.map((a) => sa.set(a)(s)) ) }) export const lensComposeLens = (ab: Lens) => (sa: Lens): Lens => new Lens({ get: (s) => ab.get(sa.get(s)), set: (b) => (s) => sa.set(ab.set(b)(sa.get(s)))(s) }) export const lensComposePrism = (ab: Prism) => (sa: Lens): Optional => optionalComposeOptional(prismAsOptional(ab))(lensAsOptional(sa)) export const lensId = (): Lens => new Lens({ get: identity, set: constant }) export const lensProp = (prop: P) => (lens: Lens): Lens => new Lens({ get: (s) => lens.get(s)[prop], set: (ap) => (s) => { const oa = lens.get(s) if (ap === oa[prop]) { return s } return lens.set(Object.assign({}, oa, { [prop]: ap }))(s) } }) export const lensProps = (...props: [P, P, ...Array

]) => (lens: Lens): Lens => new Lens({ get: (s) => { const a = lens.get(s) const r: { [K in P]?: A[K] } = {} for (const k of props) { r[k] = a[k] } return r as any }, set: (a) => (s) => { const oa = lens.get(s) const b: any = {} let mod = false for (const k of props) { if (a[k] !== oa[k]) { mod = true b[k] = a[k] } } if (mod) { return lens.set(Object.assign({}, oa, b))(s) } return s } }) export const lensComponent = , P extends keyof A>(prop: P) => (lens: Lens): Lens => new Lens({ get: (s) => lens.get(s)[prop], set: (ap) => (s) => { const oa = lens.get(s) if (ap === oa[prop]) { return s } const copy: A = oa.slice() as any copy[prop] = ap return lens.set(copy)(s) } }) // ------------------------------------------------------------------------------------- // Prism // ------------------------------------------------------------------------------------- export const prismAsOptional = (sa: Prism): Optional => new Optional({ getOption: sa.getOption, set: (a) => prismSet(a)(sa) }) export const prismAsTraversal = (sa: Prism): Traversal => new Traversal({ modifyF: ( F: P.Applicative ): (( f: (a: A) => P.Kind ) => (s: S) => P.Kind) => { const succeed = DSL.succeedF(F) return (f) => (s) => O.fold_( sa.getOption(s), () => succeed(s), (a) => F.map((a) => prismSet(a)(sa)(s))(f(a)) ) } }) export const prismModifyOption = (f: (a: A) => A) => (sa: Prism) => (s: S): O.Option => O.map_(sa.getOption(s), (o) => { const n = f(o) return n === o ? s : sa.reverseGet(n) }) export const prismModify = (f: (a: A) => A) => (sa: Prism): ((s: S) => S) => { const g = prismModifyOption(f)(sa) return (s) => O.getOrElse_(g(s), () => s) } export const prismSet = (a: A): ((sa: Prism) => (s: S) => S) => prismModify(() => a) export const prismComposeLens = (ab: Lens) => (sa: Prism): Optional => optionalComposeOptional(lensAsOptional(ab))(prismAsOptional(sa)) export const prismFromNullable = (): Prism> => new Prism({ getOption: O.fromNullable, reverseGet: identity }) export function prismFromPredicate(predicate: Predicate): Prism { return new Prism({ getOption: O.fromPredicate(predicate), reverseGet: identity }) } export const prismSome = (): Prism, A> => new Prism({ getOption: identity, reverseGet: O.some }) export const prismRight = (): Prism, A> => new Prism({ getOption: O.fromEither, reverseGet: E.right }) export const prismLeft = (): Prism, E> => new Prism({ getOption: (s) => (E.isLeft(s) ? O.some(s.left) : O.none), // TODO: replace with E.getLeft in v3 reverseGet: E.left }) // ------------------------------------------------------------------------------------- // Optional // ------------------------------------------------------------------------------------- /** * Compose a `Prism` with a `Prism` */ export const composePrism = (ab: Prism) => (sa: Prism): Prism => new Prism({ getOption: flow(sa.getOption, O.chain(ab.getOption)), reverseGet: flow(ab.reverseGet, sa.reverseGet) }) export const optionalAsTraversal = (sa: Optional): Traversal => new Traversal({ modifyF: (F: P.Applicative) => ( f: (a: A) => P.Kind ): ((s: S) => P.Kind) => { const succeed = DSL.succeedF(F) return (s) => O.fold_( sa.getOption(s), () => succeed(s), (a) => F.map((a: A) => sa.set(a)(s))(f(a)) ) } }) export const optionalModifyOption = (f: (a: A) => A) => (optional: Optional) => (s: S): O.Option => O.map_(optional.getOption(s), (a) => { const n = f(a) return n === a ? s : optional.set(n)(s) }) export const optionalModify = (f: (a: A) => A) => (optional: Optional): ((s: S) => S) => { const g = optionalModifyOption(f)(optional) return (s) => O.getOrElse_(g(s), () => s) } export const optionalComposeOptional = (ab: Optional) => (sa: Optional): Optional => new Optional({ getOption: flow(sa.getOption, O.chain(ab.getOption)), set: (b) => optionalModify(ab.set(b))(sa) }) const findMutable = (predicate: Predicate): Optional, A> => new Optional({ getOption: A.find(predicate), set: (a) => (s) => O.fold_( A.findIndex(predicate)(s), () => s, (i) => A.unsafeUpdateAt(i, a)(s) ) }) export const find: (predicate: Predicate) => Optional, A> = findMutable as any // ------------------------------------------------------------------------------------- // Traversal // ------------------------------------------------------------------------------------- export function traversalComposeTraversal( ab: Traversal ): (sa: Traversal) => Traversal { return (sa: Traversal) => new Traversal({ modifyF: (F: P.Applicative) => ( f: (a: B) => P.Kind ): ((s: S) => P.Kind) => sa.modifyF(F)(ab.modifyF(F)(f)) }) } export function fromForEach( T: P.ForEach ): < A, K = P.Initial, Q = P.Initial, W = P.Initial, X = P.Initial, I = P.Initial, S = P.Initial, R = P.Initial, E = P.Initial >() => Traversal, A> export function fromForEach( T: P.ForEach> ): () => Traversal, A> { return (): Traversal, A> => new Traversal, A>({ modifyF: T.forEachF }) } // ------------------------------------------------------------------------------------- // Ix // ------------------------------------------------------------------------------------- function indexMutableArray(): Index, number, A> { return { index: (i) => new Optional({ getOption: A.get(i), set: (a) => (as) => O.getOrElse_(A.updateAt(i, a)(as), () => as) }) } } export const indexArray: () => Index, number, A> = indexMutableArray as any export function indexRecord(): Index< Readonly>, string, A > { return { index: (k) => new Optional({ getOption: R.lookup(k), set: (a) => (r) => { if (r[k] === a || O.isNone(R.lookup(k)(r))) { return r } return R.insertAt(k, a)(r) } }) } } export function indexHashMap(): Index< Readonly>, K, A > { return { index: (k) => new Optional({ getOption: HM.get(k), set: (a) => (m) => { const x = HM.get_(m, k) if ((x._tag === "Some" && x.value === a) || !HM.has_(m, k)) { return m } return HM.set_(m, k, a) } }) } } // ------------------------------------------------------------------------------------- // At // ------------------------------------------------------------------------------------- export function atRecord(): At< Readonly>, string, O.Option > { return { at: (key) => new Lens({ get: R.lookup(key), set: O.fold( () => R.deleteAt(key), (a) => R.insertAt(key, a) ) }) } } export function atHashMap(): At< Readonly>, K, O.Option > { return { at: (key) => new Lens({ get: HM.get(key), set: O.fold( () => HM.remove(key), (a) => HM.set(key, a) ) }) } }