1/**
2 * Reads and updates focused parts of values without mutating the original
3 * value.
4 *
5 * An optic describes where to look inside a value, such as a record field, a
6 * union variant, an optional value, or several values in a collection. Different
7 * optic types describe different kinds of focus: some always find a value,
8 * some may not, and some can find many. This module includes the optic types,
9 * constructors, focusing helpers, and operations for replacing, modifying, or
10 * collecting focused values.
11 *
12 * @since 4.0.0
13 */
14
15import * as Arr from "./Array.ts"
16import { dual, identity } from "./Function.ts"
17import * as InternalRecord from "./internal/record.ts"
18import * as Option from "./Option.ts"
19import * as Predicate from "./Predicate.ts"
20import * as Result from "./Result.ts"
21import type * as Schema from "./Schema.ts"
22import * as SchemaAST from "./SchemaAST.ts"
23import * as SchemaIssue from "./SchemaIssue.ts"
24import * as Struct from "./Struct.ts"
25import type { IsUnion, NoInfer } from "./Types.ts"
26
27/**
28 * A lossless, reversible conversion between types `S` and `A`.
29 *
30 * **When to use**
31 *
32 * Use when you have a pair of functions that convert back and forth without losing
33 * information (e.g. `Record ↔ entries`, `Celsius ↔ Fahrenheit`).
34 * - You want the strongest optic that can be composed with any other.
35 *
36 * **Details**
37 *
38 * - `get(s)` always succeeds and returns an `A`.
39 * - `set(a)` always succeeds and returns an `S`.
40 * - `get(set(a)) === a` and `set(get(s))` equals `s` (round-trip laws).
41 * - Extends both {@link Lens} and {@link Prism}.
42 *
43 * **Example** (Converting between Celsius and Fahrenheit)
44 *
45 * ```ts import.meta.vitest
46 * import { Optic } from "effect"
47 *
48 * const fahrenheit = Optic.makeIso<number, number>(
49 * (c) => c * 9 / 5 + 32,
50 * (f) => (f - 32) * 5 / 9
51 * )
52 *
53 * fahrenheit.get(100) // => 212
54 *
55 * fahrenheit.set(32) // => 0
56 * ```
57 *
58 * @see {@link makeIso} — constructor
59 * @see {@link Lens} — when you only need a one-directional focus into a whole
60 * @see {@link Prism} — when the focus may not be present
61 *
62 * @category models
63 * @since 4.0.0
64 */
65export interface Iso<in out S, in out A> extends Lens<S, A>, Prism<S, A> {}
66
67/**
68 * Creates an {@link Iso} from a pair of conversion functions.
69 *
70 * **When to use**
71 *
72 * Use when you have two pure conversion functions that preserve all information
73 * between `S` and `A`.
74 *
75 * **Details**
76 *
77 * The returned optic can be composed with any other optic.
78 *
79 * **Example** (Wrapping and unwrapping a branded type)
80 *
81 * ```ts import.meta.vitest
82 * import { Optic } from "effect"
83 *
84 * type Meters = { readonly value: number }
85 * const meters = Optic.makeIso<Meters, number>(
86 * (m) => m.value,
87 * (n) => ({ value: n })
88 * )
89 *
90 * meters.get({ value: 100 }) // => 100
91 *
92 * meters.set(42) // => { value: 42 }
93 * ```
94 *
95 * @see {@link Iso} — the type this function returns
96 * @see {@link id} — identity iso (no conversion)
97 *
98 * @category constructors
99 * @since 4.0.0
100 */
101export function makeIso<S, A>(get: (s: S) => A, set: (a: A) => S): Iso<S, A> {
102 return make(primitiveNode("Iso", get, set))
103}
104
105/**
106 * Focuses on exactly one part `A` inside a whole `S`.
107 *
108 * **When to use**
109 *
110 * Use when you always have a value to read and need the original `S` to produce
111 * the updated whole, unlike `Iso`.
112 *
113 * **Details**
114 *
115 * - `get(s)` always succeeds and returns `A`.
116 * - `replace(a, s)` returns a new `S` with the focused part replaced.
117 * - Extends {@link Optional}.
118 * - Composing a Lens with a {@link Prism} or {@link Optional} produces an
119 * {@link Optional}.
120 *
121 * **Example** (Focusing on a struct field)
122 *
123 * ```ts import.meta.vitest
124 * import { Optic } from "effect"
125 *
126 * type Person = { readonly name: string; readonly age: number }
127 *
128 * const _name = Optic.id<Person>().key("name")
129 *
130 * _name.get({ name: "Alice", age: 30 }) // => "Alice"
131 * ```
132 *
133 * @see {@link makeLens} — constructor
134 * @see {@link Iso} — when conversion is lossless in both directions
135 * @see {@link Optional} — when reading can also fail
136 *
137 * @category models
138 * @since 4.0.0
139 */
140export interface Lens<in out S, in out A> extends Optional<S, A> {
141 readonly get: (s: S) => A
142}
143
144/**
145 * Creates a {@link Lens} from a getter and a replacer.
146 *
147 * **When to use**
148 *
149 * Use when you can always extract `A` from `S` and produce a new `S` by
150 * substituting a new `A`.
151 *
152 * **Details**
153 *
154 * - `replace(a, s)` should return a structurally new `S` with `a` in place
155 * of the old focus.
156 *
157 * **Example** (Focusing on the first element of a pair)
158 *
159 * ```ts import.meta.vitest
160 * import { Optic } from "effect"
161 *
162 * const _first = Optic.makeLens<readonly [string, number], string>(
163 * (pair) => pair[0],
164 * (s, pair) => [s, pair[1]]
165 * )
166 *
167 * _first.get(["hello", 42]) // => "hello"
168 *
169 * _first.replace("world", ["hello", 42]) // => ["world", 42]
170 * ```
171 *
172 * @see {@link Lens} — the type this function returns
173 * @see {@link makeIso} — when no original `S` is needed for `set`
174 *
175 * @category constructors
176 * @since 4.0.0
177 */
178export function makeLens<S, A>(get: (s: S) => A, replace: (a: A, s: S) => S): Lens<S, A> {
179 return make(primitiveNode("Lens", get, replace))
180}
181
182/**
183 * Focuses on a part `A` of `S` that may not be present (e.g. a union
184 * variant or a validated subset).
185 *
186 * **When to use**
187 *
188 * Use when the focus is conditional — reading can fail (wrong variant, failed
189 * validation).
190 * - Building a new `S` from `A` does **not** require the original `S`.
191 *
192 * **Details**
193 *
194 * - `getResult(s)` returns `Result.Success<A>` when the focus matches, or
195 * `Result.Failure<SchemaIssue.Issue>` with a structured issue.
196 * - `set(a)` always succeeds and returns a new `S`.
197 * - Extends {@link Optional}.
198 * - Composing two Prisms produces a Prism; composing a Prism with a
199 * {@link Lens} produces an {@link Optional}.
200 *
201 * **Example** (Narrowing a tagged union)
202 *
203 * ```ts import.meta.vitest
204 * import { Optic, Result } from "effect"
205 *
206 * type Shape =
207 * | { readonly _tag: "Circle"; readonly radius: number }
208 * | { readonly _tag: "Rect"; readonly width: number }
209 *
210 * const _circle = Optic.id<Shape>().tag("Circle")
211 *
212 * _circle.getResult({ _tag: "Circle", radius: 5 }) // => Result.succeed({ _tag: "Circle", radius: 5 })
213 *
214 * Result.isFailure(_circle.getResult({ _tag: "Rect", width: 10 })) // => true
215 * ```
216 *
217 * @see {@link makePrism} — constructor
218 * @see {@link fromChecks} — build a Prism from schema checks
219 * @see {@link Lens} — when reading always succeeds
220 *
221 * @category models
222 * @since 4.0.0
223 */
224export interface Prism<in out S, in out A> extends Optional<S, A> {
225 readonly set: (a: A) => S
226}
227
228/**
229 * Creates a {@link Prism} from a fallible getter and an infallible setter.
230 *
231 * **When to use**
232 *
233 * Use when reading can fail (the part may not exist in `S`), but building `S`
234 * from `A` always succeeds.
235 *
236 * **Details**
237 *
238 * - `getResult` should return `Result.fail(issue)` on mismatch.
239 * - Issues are not formatted automatically; callers choose how to render them.
240 *
241 * **Example** (Parsing a string to a number)
242 *
243 * ```ts import.meta.vitest
244 * import { Optic, Result, SchemaIssue } from "effect"
245 *
246 * const numeric = Optic.makePrism<string, number>(
247 * (s) => {
248 * const n = Number(s)
249 * return Number.isNaN(n)
250 * ? Result.fail(new SchemaIssue.InvalidValue({ message: "not a number" }))
251 * : Result.succeed(n)
252 * },
253 * String
254 * )
255 *
256 * numeric.getResult("42") // => Result.succeed(42)
257 *
258 * numeric.set(42) // => "42"
259 * ```
260 *
261 * @see {@link Prism} — the type this function returns
262 * @see {@link fromChecks} — build from `Schema` checks instead
263 *
264 * @category constructors
265 * @since 4.0.0
266 */
267export function makePrism<S, A>(
268 getResult: (s: S) => Result.Result<A, SchemaIssue.Issue>,
269 set: (a: A) => S
270): Prism<S, A> {
271 return make(primitiveNode("Prism", getResult, set))
272}
273
274/**
275 * Creates a {@link Prism} from one or more `Schema` validation checks.
276 *
277 * **When to use**
278 *
279 * Use when you want to narrow `T` to the subset that passes certain validation
280 * rules (e.g. positive integer).
281 * - You already have `Schema.isGreaterThan`, `Schema.isInt`, etc.
282 *
283 * **Details**
284 *
285 * - `getResult` runs all checks and preserves their structured issues when
286 * any check fails.
287 * - `set` is identity — the value passes through unchanged.
288 *
289 * **Example** (Creating a positive integer prism)
290 *
291 * ```ts import.meta.vitest
292 * import { Optic, Result, Schema } from "effect"
293 *
294 * const posInt = Optic.fromChecks<number>(
295 * Schema.isGreaterThan(0),
296 * Schema.isInt()
297 * )
298 *
299 * posInt.getResult(3) // => Result.succeed(3)
300 *
301 * Result.isFailure(posInt.getResult(-1)) // => true
302 * ```
303 *
304 * @see {@link makePrism} — constructor with custom getter/setter
305 * @see {@link Prism} — the type this function returns
306 *
307 * @category constructors
308 * @since 4.0.0
309 */
310export function fromChecks<T>(...checks: readonly [SchemaAST.Check<T>, ...Array<SchemaAST.Check<T>>]): Prism<T, T> {
311 return make([new CheckNode(checks)])
312}
313
314type Kind = "Iso" | "Lens" | "Prism" | "Optional"
315
316type Operation = {
317 readonly kind: Kind
318 readonly get: (s: any) => any
319 readonly set: (a: any, s?: any) => any
320}
321
322type PrimitiveStep = Operation & {
323 readonly _tag: "PrimitiveNode"
324}
325
326type Step = PrimitiveStep | PathNode | CheckNode<any>
327
328type Node = ReadonlyArray<Step>
329
330function primitiveNode(kind: Kind, get: (s: any) => any, set: (a: any, s?: any) => any): Node {
331 return [{ _tag: "PrimitiveNode", kind, get, set }]
332}
333
334const identityOperation: Operation = {
335 kind: "Iso",
336 get: identity,
337 set: identity
338}
339
340class PathNode {
341 readonly _tag = "PathNode"
342 readonly kind = "Lens"
343 readonly path: ReadonlyArray<PropertyKey>
344 readonly get: (s: any) => any
345 readonly set: (a: any, s?: any) => any
346
347 constructor(path: ReadonlyArray<PropertyKey>) {
348 this.path = path
349 this.get = (s) => {
350 let out = s
351 for (let i = 0; i < path.length; i++) {
352 out = out[path[i]]
353 }
354 return out
355 }
356 this.set = (a, s) => {
357 const out = cloneShallow(s)
358 let current = out
359 let i = 0
360 for (; i < path.length - 1; i++) {
361 const key = path[i]
362 InternalRecord.assignProperty(current, key, cloneShallow(current[key]))
363 current = current[key]
364 }
365 InternalRecord.assignProperty(current, path[i], a)
366 return out
367 }
368 }
369}
370
371class CheckNode<T> {
372 readonly _tag = "CheckNode"
373 readonly kind = "Prism"
374 readonly checks: readonly [SchemaAST.Check<T>, ...Array<SchemaAST.Check<T>>]
375 readonly get: (s: T) => Result.Result<T, SchemaIssue.Issue>
376 readonly set = identity
377
378 constructor(checks: readonly [SchemaAST.Check<T>, ...Array<SchemaAST.Check<T>>]) {
379 this.checks = checks
380 this.get = (s) => SchemaAST.runChecks(checks, s)
381 }
382}
383
384function compose(a: Node, b: Node): Node {
385 if (a.length === 0) return b
386 if (b.length === 0) return a
387 const nodes = a.slice()
388 for (let i = 0; i < b.length; i++) {
389 const node = b[i]
390 const last = nodes[nodes.length - 1]
391 if (last._tag === "PathNode" && node._tag === "PathNode") {
392 nodes[nodes.length - 1] = new PathNode([...last.path, ...node.path])
393 } else if (last._tag === "CheckNode" && node._tag === "CheckNode") {
394 nodes[nodes.length - 1] = new CheckNode<any>([...last.checks, ...node.checks])
395 } else {
396 nodes.push(node)
397 }
398 }
399 return nodes
400}
401
402type ForbidUnion<A, Message extends string> = IsUnion<A> extends true ? [Message] : []
403
404/**
405 * The most general optic — both reading and writing can fail.
406 *
407 * **When to use**
408 *
409 * Use when the focus may not exist in `S` and writing a new `A` back may also
410 * fail, for example when the source no longer matches the expected shape. This
411 * is the base type extended by {@link Iso}, {@link Lens}, {@link Prism}, and
412 * {@link Traversal}.
413 *
414 * **Details**
415 *
416 * - `getResult(s)` returns `Result.Success<A>` or `Result.Failure<SchemaIssue.Issue>`.
417 * - `replaceResult(a, s)` returns `Result.Success<S>` or
418 * `Result.Failure<SchemaIssue.Issue>`.
419 * - `replace(a, s)` returns the original `s` on failure (never throws).
420 * - `modify(f)` returns the original `s` on failure (never throws).
421 * - All operations are pure; inputs are never mutated.
422 *
423 * **Example** (Focusing on an optional record key)
424 *
425 * ```ts import.meta.vitest
426 * import { Optic, Result } from "effect"
427 *
428 * type Env = { [key: string]: string }
429 * const _home = Optic.id<Env>().at("HOME")
430 *
431 * _home.getResult({ HOME: "/root" }) // => Result.succeed("/root")
432 *
433 * Result.isFailure(_home.getResult({ PATH: "/bin" })) // => true
434 *
435 * // replace returns original on failure
436 * _home.replace("/new", { PATH: "/bin" }) // => { PATH: "/bin" }
437 * ```
438 *
439 * @see {@link makeOptional} — constructor
440 * @see {@link Lens} — when reading always succeeds
441 * @see {@link Prism} — when writing always succeeds
442 *
443 * @category models
444 * @since 4.0.0
445 */
446export interface Optional<in out S, in out A> {
447 /**
448 * Attempts to read the focus `A` from the whole `S`. Returns
449 * `Result.Success<A>` when the focus exists, or
450 * `Result.Failure<SchemaIssue.Issue>` with a structured issue otherwise.
451 */
452 readonly getResult: (s: S) => Result.Result<A, SchemaIssue.Issue>
453 /**
454 * Replaces the focus in `S` with a new `A`. Returns the original `s`
455 * unchanged when the optic cannot focus (never throws).
456 */
457 readonly replace: (a: A, s: S) => S
458 /**
459 * Like {@link replace}, but returns an explicit `Result` so callers can
460 * detect and handle failure.
461 */
462 readonly replaceResult: (a: A, s: S) => Result.Result<S, SchemaIssue.Issue>
463 /**
464 * Composes this optic with another. The result type is the weakest of
465 * the two: Iso + Iso = Iso, Lens + Prism = Optional, etc.
466 *
467 * **Example** (Composing a lens with a prism)
468 *
469 * ```ts import.meta.vitest
470 * import { Optic, Option, Result } from "effect"
471 *
472 * type State = { value: Option.Option<number> }
473 *
474 * const _inner = Optic.id<State>().key("value").compose(Optic.some())
475 * // _inner is Optional<State, number>
476 * _inner.getResult({ value: Option.some(1) }) // => Result.succeed(1)
477 * ```
478 *
479 * @see {@link id} — start a composition chain
480 */
481 compose<B>(this: Iso<S, A>, that: Iso<A, B>): Iso<S, B>
482 compose<B>(this: Lens<S, A>, that: Lens<A, B>): Lens<S, B>
483 compose<B>(this: Prism<S, A>, that: Prism<A, B>): Prism<S, B>
484 compose<B>(this: Optional<S, A>, that: Optional<A, B>): Optional<S, B>
485
486 /**
487 * Returns a function `(s: S) => S` that applies `f` to the focused value.
488 * If the optic cannot focus, the original `s` is returned unchanged.
489 *
490 * **Example** (Incrementing a nested field)
491 *
492 * ```ts import.meta.vitest
493 * import { Optic } from "effect"
494 *
495 * type S = { readonly a: { readonly b: number } }
496 * const _b = Optic.id<S>().key("a").key("b")
497 *
498 * const inc = _b.modify((n) => n + 1)
499 * inc({ a: { b: 1 } }) // => { a: { b: 2 } }
500 * ```
501 */
502 modify(f: (a: A) => A): (s: S) => S
503
504 /**
505 * Focuses on a property of the current struct/tuple focus.
506 *
507 * **Details**
508 *
509 * - On a {@link Lens}, returns a Lens.
510 * - On an {@link Optional}, returns an Optional.
511 * - Does **not** work on union types (compile error).
512 *
513 * **Example** (Drilling into nested structs)
514 *
515 * ```ts import.meta.vitest
516 * import { Optic } from "effect"
517 *
518 * type S = { readonly a: { readonly b: number } }
519 * const _b = Optic.id<S>().key("a").key("b")
520 *
521 * _b.get({ a: { b: 42 } }) // => 42
522 * ```
523 */
524 key<S, A extends object, Key extends keyof A>(
525 this: Lens<S, A>,
526 key: Key,
527 ..._err: ForbidUnion<A, "cannot use `key` on a union type">
528 ): Lens<S, A[Key]>
529 key<S, A extends object, Key extends keyof A>(
530 this: Optional<S, A>,
531 key: Key,
532 ..._err: ForbidUnion<A, "cannot use `key` on a union type">
533 ): Optional<S, A[Key]>
534
535 /**
536 * Focuses on a key where setting `undefined` **removes** the key from the
537 * struct (or splices the element from an array/tuple).
538 *
539 * **Details**
540 *
541 * - The focus type becomes `A[Key] | undefined`.
542 * - Does **not** work on union types (compile error).
543 *
544 * **Example** (Deleting an optional key)
545 *
546 * ```ts import.meta.vitest
547 * import { Optic } from "effect"
548 *
549 * type S = { readonly a?: number }
550 * const _a = Optic.id<S>().optionalKey("a")
551 *
552 * _a.replace(undefined, { a: 1 }) // => {}
553 *
554 * _a.replace(2, {}) // => { a: 2 }
555 * ```
556 */
557 optionalKey<S, A extends object, Key extends keyof A>(
558 this: Lens<S, A>,
559 key: Key,
560 ..._err: ForbidUnion<A, "cannot use `optionalKey` on a union type">
561 ): Lens<S, A[Key] | undefined>
562 optionalKey<S, A extends object, Key extends keyof A>(
563 this: Optional<S, A>,
564 key: Key,
565 ..._err: ForbidUnion<A, "cannot use `optionalKey` on a union type">
566 ): Optional<S, A[Key] | undefined>
567
568 /**
569 * Adds one or more `Schema` validation checks to the optic chain.
570 * `getResult` fails when any check fails; `set` passes through unchanged.
571 *
572 * **Details**
573 *
574 * - On a {@link Prism}, returns a Prism.
575 * - On an {@link Optional}, returns an Optional.
576 *
577 * **Example** (Focusing only on positive numbers)
578 *
579 * ```ts import.meta.vitest
580 * import { Optic, Result, Schema } from "effect"
581 *
582 * const _pos = Optic.id<number>().check(Schema.isGreaterThan(0))
583 *
584 * _pos.getResult(5) // => Result.succeed(5)
585 *
586 * Result.isFailure(_pos.getResult(-1)) // => true
587 * ```
588 *
589 * @see {@link fromChecks} — standalone prism from checks
590 */
591 check<S, A>(this: Prism<S, A>, ...checks: readonly [SchemaAST.Check<A>, ...Array<SchemaAST.Check<A>>]): Prism<S, A>
592 check<S, A>(
593 this: Optional<S, A>,
594 ...checks: readonly [SchemaAST.Check<A>, ...Array<SchemaAST.Check<A>>]
595 ): Optional<S, A>
596
597 /**
598 * Narrows the focus to a subtype `B` using a type guard.
599 *
600 * **Details**
601 *
602 * - On a {@link Prism}, returns a Prism.
603 * - On an {@link Optional}, returns an Optional.
604 * - Pass optional `annotations` to customize the error message.
605 *
606 * **Example** (Narrowing a union)
607 *
608 * ```ts import.meta.vitest
609 * import { Optic, Result } from "effect"
610 *
611 * type B = { readonly _tag: "b"; readonly b: number }
612 * type S = { readonly _tag: "a"; readonly a: string } | B
613 *
614 * const _b = Optic.id<S>().refine(
615 * (s: S): s is B => s._tag === "b",
616 * { expected: `"b" tag` }
617 * )
618 *
619 * _b.getResult({ _tag: "b", b: 1 }) // => Result.succeed({ _tag: "b", b: 1 })
620 * ```
621 *
622 * @see `.tag()` — shorthand for narrowing by `_tag`
623 */
624 refine<S, A, B extends A>(
625 this: Prism<S, A>,
626 refinement: (a: A) => a is B,
627 annotations?: Schema.Annotations.Filter
628 ): Prism<S, B>
629 refine<S, A, B extends A>(
630 this: Optional<S, A>,
631 refinement: (a: A) => a is B,
632 annotations?: Schema.Annotations.Filter
633 ): Optional<S, B>
634
635 /**
636 * Narrows the focus to the variant of a tagged union with the given
637 * `_tag` value.
638 *
639 * **Details**
640 *
641 * - On a {@link Prism}, returns a Prism.
642 * - On an {@link Optional}, returns an Optional.
643 * - Shorthand for `.refine(s => s._tag === tag)`.
644 * - A non-matching value fails with {@link SchemaIssue.InvalidValue} whose
645 * `expected` annotation is `"<tag> tag"`.
646 *
647 * **Example** (Focusing a tagged variant)
648 *
649 * ```ts import.meta.vitest
650 * import { Optic, Result } from "effect"
651 *
652 * type Shape =
653 * | { readonly _tag: "Circle"; readonly radius: number }
654 * | { readonly _tag: "Rect"; readonly width: number }
655 *
656 * const _radius = Optic.id<Shape>().tag("Circle").key("radius")
657 *
658 * _radius.getResult({ _tag: "Circle", radius: 5 }) // => Result.succeed(5)
659 *
660 * Result.isFailure(_radius.getResult({ _tag: "Rect", width: 10 })) // => true
661 * ```
662 *
663 * @see `.refine()` — for arbitrary type guards
664 */
665 tag<S, A extends { readonly _tag: SchemaAST.LiteralValue }, Tag extends A["_tag"]>(
666 this: Prism<S, A>,
667 tag: Tag
668 ): Prism<S, Extract<A, { readonly _tag: Tag }>>
669 tag<S, A extends { readonly _tag: SchemaAST.LiteralValue }, Tag extends A["_tag"]>(
670 this: Optional<S, A>,
671 tag: Tag
672 ): Optional<S, Extract<A, { readonly _tag: Tag }>>
673
674 /**
675 * Focuses on a key only if it exists (`Object.hasOwn`). Both
676 * `getResult` and `replaceResult` fail when the key is absent.
677 *
678 * **Details**
679 *
680 * Unlike `.key()`, which always succeeds on the read side, `.at()` is
681 * useful for Records or arrays where the key/index may not be present.
682 *
683 * - Always returns an {@link Optional}.
684 * - Does **not** work on union types (compile error).
685 * - A missing key fails with a {@link SchemaIssue.Pointer} at that key whose
686 * inner issue is {@link SchemaIssue.MissingKey}, for both `getResult` and
687 * `replaceResult`.
688 *
689 * **Example** (Accessing records safely)
690 *
691 * ```ts import.meta.vitest
692 * import { Optic, Result } from "effect"
693 *
694 * type Env = { [key: string]: number }
695 * const _x = Optic.id<Env>().at("x")
696 *
697 * _x.getResult({ x: 1 }) // => Result.succeed(1)
698 *
699 * Result.isFailure(_x.getResult({ y: 2 })) // => true
700 * ```
701 *
702 * @see `.key()` — when the key is always present
703 */
704 at<S, A extends object, Key extends keyof A>(
705 this: Optional<S, A>,
706 key: Key,
707 ..._err: ForbidUnion<A, "cannot use `at` on a union type">
708 ): Optional<S, A[Key]>
709
710 /**
711 * Focuses on a subset of keys of the current struct focus.
712 *
713 * **Details**
714 *
715 * - On a {@link Lens}, returns a Lens.
716 * - On an {@link Optional}, returns an Optional.
717 * - Does **not** work on union types (compile error).
718 *
719 * **Example** (Picking keys)
720 *
721 * ```ts import.meta.vitest
722 * import { Optic } from "effect"
723 *
724 * type S = { readonly a: string; readonly b: number; readonly c: boolean }
725 *
726 * const _ac = Optic.id<S>().pick(["a", "c"])
727 *
728 * _ac.get({ a: "hi", b: 1, c: true }) // => { a: "hi", c: true }
729 * ```
730 *
731 * @see `.omit()` — the inverse operation
732 */
733 pick<S, A, Keys extends ReadonlyArray<keyof A>>(
734 this: Lens<S, A>,
735 keys: Keys,
736 ..._err: ForbidUnion<A, "cannot use `pick` on a union type">
737 ): Lens<S, Pick<A, Keys[number]>>
738 pick<S, A, Keys extends ReadonlyArray<keyof A>>(
739 this: Optional<S, A>,
740 keys: Keys,
741 ..._err: ForbidUnion<A, "cannot use `pick` on a union type">
742 ): Optional<S, Pick<A, Keys[number]>>
743
744 /**
745 * Focuses on all keys **except** the specified ones.
746 *
747 * **Details**
748 *
749 * - On a {@link Lens}, returns a Lens.
750 * - On an {@link Optional}, returns an Optional.
751 * - Does **not** work on union types (compile error).
752 *
753 * **Example** (Omitting keys)
754 *
755 * ```ts import.meta.vitest
756 * import { Optic } from "effect"
757 *
758 * type S = { readonly a: string; readonly b: number; readonly c: boolean }
759 *
760 * const _ac = Optic.id<S>().omit(["b"])
761 *
762 * _ac.get({ a: "hi", b: 1, c: true }) // => { a: "hi", c: true }
763 * ```
764 *
765 * @see `.pick()` — the inverse operation
766 *
767 * @since 4.0.0
768 */
769 omit<S, A, Keys extends ReadonlyArray<keyof A>>(
770 this: Lens<S, A>,
771 keys: Keys,
772 ..._err: ForbidUnion<A, "cannot use `omit` on a union type">
773 ): Lens<S, Omit<A, Keys[number]>>
774 omit<S, A, Keys extends ReadonlyArray<keyof A>>(
775 this: Optional<S, A>,
776 keys: Keys,
777 ..._err: ForbidUnion<A, "cannot use `omit` on a union type">
778 ): Optional<S, Omit<A, Keys[number]>>
779
780 /**
781 * Filters out `undefined` from the focus, producing a {@link Prism}.
782 * `getResult` fails when the focus is `undefined`.
783 *
784 * **Example** (Filtering undefined values)
785 *
786 * ```ts import.meta.vitest
787 * import { Optic, Result } from "effect"
788 *
789 * const _defined = Optic.id<number | undefined>().notUndefined()
790 *
791 * _defined.getResult(42) // => Result.succeed(42)
792 *
793 * Result.isFailure(_defined.getResult(undefined)) // => true
794 * ```
795 *
796 * @since 4.0.0
797 */
798 notUndefined<S, A>(this: Prism<S, A>): Prism<S, Exclude<A, undefined>>
799 notUndefined<S, A>(this: Optional<S, A>): Optional<S, Exclude<A, undefined>>
800
801 /**
802 * Focuses **all elements** of an array-like focus and optionally narrows
803 * to a subset using an element-level optic.
804 * Available only on {@link Traversal} (i.e. when `A` is
805 * `ReadonlyArray<Element>`). Returns a new Traversal focused on the
806 * selected elements.
807 *
808 * **Details**
809 *
810 * - **getResult** collects the values focused by `f(id<A>())` for each
811 * element. Non-focusable elements are skipped.
812 * - **replaceResult** expects exactly as many values as were collected by
813 * `getResult` and writes them back in order. Fails with a
814 * {@link SchemaIssue.InvalidValue} if counts differ. If an inner replacement
815 * fails, its issue is wrapped in a {@link SchemaIssue.Pointer} at the element
816 * index.
817 *
818 * **Example** (Incrementing liked posts)
819 *
820 * ```ts import.meta.vitest
821 * import { Optic, Schema } from "effect"
822 *
823 * type Post = { title: string; likes: number }
824 * type S = { user: { posts: ReadonlyArray<Post> } }
825 *
826 * const _likes = Optic.id<S>()
827 * .key("user")
828 * .key("posts")
829 * .forEach((post) => post.key("likes").check(Schema.isGreaterThan(0)))
830 *
831 * const addLike = _likes.modifyAll((n) => n + 1)
832 *
833 * const result = addLike({
834 * user: { posts: [{ title: "a", likes: 0 }, { title: "b", likes: 1 }] }
835 * })
836 * result.user.posts // => [{ title: "a", likes: 0 }, { title: "b", likes: 2 }]
837 * ```
838 *
839 * @see {@link getAll} — extract all focused elements as an array
840 * @see `.modifyAll()` — apply a function to every focused element
841 */
842 forEach<S, A, B>(this: Traversal<S, A>, f: (iso: Iso<A, A>) => Optional<A, B>): Traversal<S, B>
843
844 /**
845 * Applies a function to **every** element focused by the traversal.
846 *
847 * **Details**
848 *
849 * Available only on {@link Traversal}. Returns a function `(s: S) => S`.
850 * If the traversal cannot focus, the original `s` is returned unchanged.
851 *
852 * Unlike `.modify()`, which operates on the whole array, `modifyAll`
853 * maps `f` over each individual element.
854 *
855 * **Example** (Doubling all focused values)
856 *
857 * ```ts import.meta.vitest
858 * import { Optic, Schema } from "effect"
859 *
860 * type S = { readonly items: ReadonlyArray<number> }
861 *
862 * const _positive = Optic.id<S>()
863 * .key("items")
864 * .forEach((n) => n.check(Schema.isGreaterThan(0)))
865 *
866 * const doubled = _positive.modifyAll((n) => n * 2)
867 *
868 * doubled({ items: [1, -2, 3] }) // => { items: [2, -2, 6] }
869 * ```
870 *
871 * @see `.forEach()` — create a sub-traversal
872 * @see {@link getAll} — extract focused elements
873 */
874 modifyAll<S, A>(this: Traversal<S, A>, f: (a: A) => A): (s: S) => S
875}
876
877/**
878 * Creates an {@link Optional} from a fallible getter and a fallible setter.
879 *
880 * **When to use**
881 *
882 * Use when you need an optic for a focus that may be missing on read and may
883 * reject updates on write.
884 *
885 * **Details**
886 *
887 * - `getResult` should return `Result.fail(issue)` on mismatch.
888 * - `set` should return `Result.fail(issue)` when the update cannot be
889 * applied.
890 * - Issues are not formatted automatically; callers choose how to render them.
891 *
892 * **Example** (Accessing record keys safely)
893 *
894 * ```ts import.meta.vitest
895 * import { Optic, Result, SchemaIssue } from "effect"
896 *
897 * const atKey = (key: string) => {
898 * const issue = new SchemaIssue.Pointer([key], new SchemaIssue.MissingKey(undefined))
899 * return Optic.makeOptional<Record<string, number>, number>(
900 * (s) =>
901 * Object.hasOwn(s, key)
902 * ? Result.succeed(s[key])
903 * : Result.fail(issue),
904 * (a, s) =>
905 * Object.hasOwn(s, key)
906 * ? Result.succeed({ ...s, [key]: a })
907 * : Result.fail(issue)
908 * )
909 * }
910 *
911 * atKey("x").getResult({ x: 1 }) // => Result.succeed(1)
912 * ```
913 *
914 * @see {@link Optional} — the type this function returns
915 * @see {@link makeLens} — when reading always succeeds
916 * @see {@link makePrism} — when writing always succeeds
917 *
918 * @category constructors
919 * @since 4.0.0
920 */
921export function makeOptional<S, A>(
922 getResult: (s: S) => Result.Result<A, SchemaIssue.Issue>,
923 set: (a: A, s: S) => Result.Result<S, SchemaIssue.Issue>
924): Optional<S, A> {
925 return make(primitiveNode("Optional", getResult, set))
926}
927
928/**
929 * An optic that focuses on **zero or more** elements of type `A` inside `S`.
930 *
931 * **When to use**
932 *
933 * Use when you want to read/update multiple elements at once (e.g. all items in
934 * an array, or a filtered subset).
935 *
936 * **Details**
937 *
938 * - Technically `Optional<S, ReadonlyArray<A>>` — the focused value is an
939 * array of all matched elements.
940 * - Use `.forEach()` to add per-element sub-optics (filtering, drilling
941 * deeper).
942 * - Use `.modifyAll(f)` to map a function over every focused element.
943 * - Use {@link getAll} to extract all focused elements as a plain array.
944 *
945 * **Example** (Traversing array elements with a filter)
946 *
947 * ```ts import.meta.vitest
948 * import { Optic, Schema } from "effect"
949 *
950 * type S = { readonly items: ReadonlyArray<number> }
951 *
952 * const _positive = Optic.id<S>()
953 * .key("items")
954 * .forEach((n) => n.check(Schema.isGreaterThan(0)))
955 *
956 * const getPositive = Optic.getAll(_positive)
957 *
958 * getPositive({ items: [1, -2, 3] }) // => [1, 3]
959 * ```
960 *
961 * @see {@link getAll} — extract focused elements
962 * @see {@link Optional} — the base type
963 *
964 * @category models
965 * @since 4.0.0
966 */
967export interface Traversal<in out S, in out A> extends Optional<S, ReadonlyArray<A>> {}
968
969class OptionalImpl<S, A> implements Optional<S, A> {
970 /** @internal */
971 readonly node: Node
972 readonly getResult: (s: S) => Result.Result<A, SchemaIssue.Issue>
973 readonly replaceResult: (a: A, s: S) => Result.Result<S, SchemaIssue.Issue>
974 constructor(
975 node: Node,
976 getResult: (s: S) => Result.Result<A, SchemaIssue.Issue>,
977 replaceResult: (a: A, s: S) => Result.Result<S, SchemaIssue.Issue>
978 ) {
979 this.node = node
980 this.getResult = getResult
981 this.replaceResult = replaceResult
982 }
983 replace(a: A, s: S): S {
984 return Result.getOrElse(this.replaceResult(a, s), () => s)
985 }
986 modify(f: (a: A) => A): (s: S) => S {
987 return (s) => Result.getOrElse(Result.flatMap(this.getResult(s), (a) => this.replaceResult(f(a), s)), () => s)
988 }
989 compose(that: any): any {
990 return make(compose(this.node, that.node))
991 }
992 key(key: PropertyKey): any {
993 return make(compose(this.node, [new PathNode([key])]))
994 }
995 optionalKey(key: PropertyKey): any {
996 return make(
997 compose(
998 this.node,
999 primitiveNode(
1000 "Lens",
1001 (s) => s[key],
1002 (a, s) => {
1003 const copy = cloneShallow(s)
1004 if (a === undefined) {
1005 if (
1006 Array.isArray(copy) &&
1007 (typeof key === "number" || (typeof key === "string" && Arr.isCanonicalArrayIndex(key)))
1008 ) {
1009 copy.splice(Number(key), 1)
1010 } else {
1011 delete copy[key]
1012 }
1013 } else {
1014 InternalRecord.assignProperty(copy, key, a)
1015 }
1016 return copy
1017 }
1018 )
1019 )
1020 )
1021 }
1022 check(...checks: readonly [SchemaAST.Check<any>, ...Array<SchemaAST.Check<any>>]): any {
1023 return make(compose(this.node, [new CheckNode(checks)]))
1024 }
1025 refine<B extends A>(refinement: (a: A) => a is B, annotations?: Schema.Annotations.Filter): any {
1026 return make(compose(this.node, [new CheckNode([SchemaAST.makeFilterByGuard(refinement, annotations)])]))
1027 }
1028 tag(tag: string): any {
1029 const err = Result.fail(new SchemaIssue.InvalidValue({ expected: `${JSON.stringify(tag)} tag` }))
1030 return make(
1031 compose(
1032 this.node,
1033 primitiveNode(
1034 "Prism",
1035 (s) => s._tag === tag ? Result.succeed(s) : err,
1036 identity
1037 )
1038 )
1039 )
1040 }
1041 at(key: PropertyKey, ..._rest: Array<any>): any {
1042 const err = Result.fail(
1043 new SchemaIssue.Pointer([key], new SchemaIssue.MissingKey(undefined))
1044 )
1045 return make(
1046 compose(
1047 this.node,
1048 primitiveNode(
1049 "Optional",
1050 (s) => Object.hasOwn(s, key) ? Result.succeed(s[key]) : err,
1051 (a, s) => {
1052 if (Object.hasOwn(s, key)) {
1053 const copy = cloneShallow(s)
1054 InternalRecord.assignProperty(copy, key, a)
1055 return Result.succeed(copy)
1056 } else {
1057 return err
1058 }
1059 }
1060 )
1061 )
1062 )
1063 }
1064 pick(keys: any) {
1065 return this.compose(makeLens(Struct.pick(keys), (p, a) => ({ ...Struct.omit(a, keys), ...p })))
1066 }
1067 omit(keys: any) {
1068 return this.compose(makeLens(Struct.omit(keys), (o, a) => ({ ...Struct.pick(a, keys), ...o })))
1069 }
1070 notUndefined(): any {
1071 return this.refine(Predicate.isNotUndefined, { expected: "a value other than `undefined`" })
1072 }
1073 forEach<S, A, B>(this: Traversal<S, A>, f: (iso: Iso<A, A>) => Optional<A, B>): Traversal<S, B> {
1074 const inner = f(id<A>())
1075 return makeOptional<S, ReadonlyArray<B>>(
1076 // GET: collect focused Bs
1077 (s) =>
1078 Result.map(this.getResult(s), (as) => {
1079 const bs: Array<B> = []
1080 for (let i = 0; i < as.length; i++) {
1081 const r = inner.getResult(as[i])
1082 if (Result.isSuccess(r)) bs.push(r.success)
1083 }
1084 return bs
1085 }),
1086 // SET: bs must match the number of focusable elements
1087 (bs, s) =>
1088 Result.flatMap(this.getResult(s), (as) => {
1089 // 1) collect focusable indices
1090 const idxs: Array<number> = []
1091 for (let i = 0; i < as.length; i++) {
1092 if (Result.isSuccess(inner.getResult(as[i]))) idxs.push(i)
1093 }
1094
1095 // 2) arity check
1096 if (bs.length !== idxs.length) {
1097 return Result.fail(
1098 new SchemaIssue.InvalidValue({
1099 message: `each: replacement length mismatch: ${bs.length} !== ${idxs.length}`
1100 })
1101 )
1102 }
1103
1104 // 3) update those indices
1105 const out: Array<A> = as.slice()
1106 for (let k = 0; k < idxs.length; k++) {
1107 const i = idxs[k]
1108 const r = inner.replaceResult(bs[k], as[i])
1109 if (Result.isFailure(r)) {
1110 return Result.fail(new SchemaIssue.Pointer([i], r.failure))
1111 }
1112 out[i] = r.success
1113 }
1114 return this.replaceResult(out, s)
1115 })
1116 )
1117 }
1118 modifyAll<S, A>(this: Traversal<S, A>, f: (a: A) => A): (s: S) => S {
1119 return (s) =>
1120 Result.getOrElse(
1121 Result.flatMap(this.getResult(s), (as) => this.replaceResult(as.map(f), s)),
1122 () => s
1123 )
1124 }
1125}
1126
1127class IsoImpl<S, A> extends OptionalImpl<S, A> implements Iso<S, A> {
1128 readonly get: (s: S) => A
1129 readonly set: (a: A) => S
1130 constructor(node: Node, get: (s: S) => A, set: (a: A) => S) {
1131 super(node, (s) => Result.succeed(get(s)), (a) => Result.succeed(set(a)))
1132 this.get = get
1133 this.set = set
1134 }
1135 override replace(a: A, _: S): S {
1136 return this.set(a)
1137 }
1138 override modify(f: (a: A) => A): (s: S) => S {
1139 return (s) => this.set(f(this.get(s)))
1140 }
1141}
1142
1143class LensImpl<S, A> extends OptionalImpl<S, A> implements Lens<S, A> {
1144 readonly get: (s: S) => A
1145 constructor(node: Node, get: (s: S) => A, replace: (a: A, s: S) => S) {
1146 super(node, (s) => Result.succeed(get(s)), (a, s) => Result.succeed(replace(a, s)))
1147 this.get = get
1148 this.replace = replace
1149 }
1150 override modify(f: (a: A) => A): (s: S) => S {
1151 return (s) => this.replace(f(this.get(s)), s)
1152 }
1153}
1154
1155class PrismImpl<S, A> extends OptionalImpl<S, A> implements Prism<S, A> {
1156 readonly set: (a: A) => S
1157 constructor(node: Node, getResult: (s: S) => Result.Result<A, SchemaIssue.Issue>, set: (a: A) => S) {
1158 super(node, getResult, (a, _) => Result.succeed(set(a)))
1159 this.set = set
1160 }
1161 override replace(a: A, _: S): S {
1162 return this.set(a)
1163 }
1164 override modify(f: (a: A) => A): (s: S) => S {
1165 return (s) => Result.getOrElse(Result.map(this.getResult(s), (a) => this.set(f(a))), () => s)
1166 }
1167}
1168
1169function make(node: Node): any {
1170 let op: Operation = node[0] ?? identityOperation
1171 if (node.length > 1) {
1172 const kind = node.reduce<Kind>((kind, step) => composeKind(kind, step.kind), "Iso")
1173 op = {
1174 kind,
1175 get: compileGet(node, kind),
1176 set: compileSet(node, kind)
1177 }
1178 }
1179 switch (op.kind) {
1180 case "Iso":
1181 return new IsoImpl(node, op.get, op.set)
1182 case "Lens":
1183 return new LensImpl(node, op.get, op.set)
1184 case "Prism":
1185 return new PrismImpl(node, op.get, op.set)
1186 case "Optional":
1187 return new OptionalImpl(node, op.get, op.set)
1188 }
1189}
1190
1191function cloneShallow<T>(pojo: T): T {
1192 if (Array.isArray(pojo)) return pojo.slice() as T
1193 if (typeof pojo === "object" && pojo !== null) {
1194 const proto = Object.getPrototypeOf(pojo)
1195 if (proto !== Object.prototype && proto !== null) {
1196 throw new Error("Cannot clone object with non-Object constructor or null prototype")
1197 }
1198 return { ...pojo } as T
1199 }
1200 return pojo
1201}
1202
1203function compileGet(nodes: Node, kind: Kind): (s: any) => any {
1204 return (s) => {
1205 for (let i = 0; i < nodes.length; i++) {
1206 const op = nodes[i]
1207 const result = op.get(s)
1208 if (hasFailingGet(op.kind)) {
1209 if (Result.isFailure(result)) {
1210 return result
1211 }
1212 s = result.success
1213 } else {
1214 s = result
1215 }
1216 }
1217 return hasFailingGet(kind) ? Result.succeed(s) : s
1218 }
1219}
1220
1221function compileSet(nodes: Node, kind: Kind): (a: any, s: any) => any {
1222 if (hasSourceFreeSet(kind)) {
1223 return (a) => {
1224 for (let i = nodes.length - 1; i >= 0; i--) {
1225 a = nodes[i].set(a)
1226 }
1227 return a
1228 }
1229 }
1230 return (a, s) => {
1231 const len = nodes.length
1232 const sources = new Array(len)
1233 for (let i = 0; i < len; i++) {
1234 sources[i] = s
1235 const op = nodes[i]
1236 if (hasFailingGet(op.kind)) {
1237 const result = op.get(s)
1238 if (Result.isFailure(result)) {
1239 return result
1240 }
1241 s = result.success
1242 } else {
1243 s = op.get(s)
1244 }
1245 }
1246 for (let i = len - 1; i >= 0; i--) {
1247 const op = nodes[i]
1248 if (hasSourceFreeSet(op.kind)) {
1249 a = op.set(a)
1250 } else if (op.kind === "Lens") {
1251 a = op.set(a, sources[i])
1252 } else {
1253 const result = op.set(a, sources[i])
1254 if (Result.isFailure(result)) {
1255 return result
1256 }
1257 a = result.success
1258 }
1259 }
1260 return kind === "Optional" ? Result.succeed(a) : a
1261 }
1262}
1263
1264function hasFailingGet(kind: Kind): boolean {
1265 return kind === "Prism" || kind === "Optional"
1266}
1267
1268function hasSourceFreeSet(kind: Kind): boolean {
1269 return kind === "Iso" || kind === "Prism"
1270}
1271
1272function composeKind(a: Kind, b: Kind): Kind {
1273 if (a === "Iso") return b
1274 if (b === "Iso" || a === b) return a
1275 return "Optional"
1276}
1277// ---------------------------------------------
1278// Derived APIs
1279// ---------------------------------------------
1280
1281/**
1282 * Reads the focused value from a `Lens`.
1283 *
1284 * **When to use**
1285 *
1286 * Use when the optic always focuses exactly one value.
1287 *
1288 * **Details**
1289 *
1290 * Supports both data-first and data-last forms.
1291 *
1292 * @see {@link getResult} for optics whose focus may be absent
1293 *
1294 * @category getters
1295 * @since 4.0.0
1296 */
1297export const get: {
1298 <S, A>(optic: Lens<S, A>): (self: NoInfer<S>) => A
1299 <S, A>(self: NoInfer<S>, optic: Lens<S, A>): A
1300} = dual<
1301 <S, A>(optic: Lens<S, A>) => (self: NoInfer<S>) => A,
1302 <S, A>(self: NoInfer<S>, optic: Lens<S, A>) => A
1303>(2, (self, optic) => optic.get(self))
1304
1305/**
1306 * Attempts to read the focused value from an `Optional`.
1307 *
1308 * **When to use**
1309 *
1310 * Use when the optic may not focus and you need the failure as a `Result`.
1311 *
1312 * **Details**
1313 *
1314 * Supports both data-first and data-last forms.
1315 *
1316 * @see {@link get} for optics that always focus
1317 *
1318 * @category getters
1319 * @since 4.0.0
1320 */
1321export const getResult: {
1322 <S, A>(optic: Optional<S, A>): (self: NoInfer<S>) => Result.Result<A, SchemaIssue.Issue>
1323 <S, A>(self: NoInfer<S>, optic: Optional<S, A>): Result.Result<A, SchemaIssue.Issue>
1324} = dual<
1325 <S, A>(optic: Optional<S, A>) => (self: NoInfer<S>) => Result.Result<A, SchemaIssue.Issue>,
1326 <S, A>(self: NoInfer<S>, optic: Optional<S, A>) => Result.Result<A, SchemaIssue.Issue>
1327>(2, (self, optic) => optic.getResult(self))
1328
1329/**
1330 * Builds a source value from a focused value using a `Prism`.
1331 *
1332 * **When to use**
1333 *
1334 * Use when the optic can construct the source without an existing source value.
1335 *
1336 * **Details**
1337 *
1338 * Supports both data-first and data-last forms. The focused value is `self`.
1339 *
1340 * @see {@link replace} for updates that use an existing source
1341 *
1342 * @category transforming
1343 * @since 4.0.0
1344 */
1345export const set: {
1346 <S, A>(optic: Prism<S, A>): (self: NoInfer<A>) => S
1347 <S, A>(self: NoInfer<A>, optic: Prism<S, A>): S
1348} = dual<
1349 <S, A>(optic: Prism<S, A>) => (self: NoInfer<A>) => S,
1350 <S, A>(self: NoInfer<A>, optic: Prism<S, A>) => S
1351>(2, (self, optic) => optic.set(self))
1352
1353/**
1354 * Replaces the focused value in a source.
1355 *
1356 * **When to use**
1357 *
1358 * Use when a failed focus should leave the source unchanged.
1359 *
1360 * **Details**
1361 *
1362 * Supports both data-first and data-last forms.
1363 *
1364 * @see {@link replaceResult} for an explicit replacement failure
1365 *
1366 * @category transforming
1367 * @since 4.0.0
1368 */
1369export const replace: {
1370 <S, A>(optic: Optional<S, A>, value: NoInfer<A>): (self: NoInfer<S>) => S
1371 <S, A>(self: NoInfer<S>, optic: Optional<S, A>, value: NoInfer<A>): S
1372} = dual<
1373 <S, A>(optic: Optional<S, A>, value: NoInfer<A>) => (self: NoInfer<S>) => S,
1374 <S, A>(self: NoInfer<S>, optic: Optional<S, A>, value: NoInfer<A>) => S
1375>(3, (self, optic, value) => optic.replace(value, self))
1376
1377/**
1378 * Attempts to replace the focused value in a source.
1379 *
1380 * **When to use**
1381 *
1382 * Use when you need an explicit `Result` for a replacement failure.
1383 *
1384 * **Details**
1385 *
1386 * Supports both data-first and data-last forms.
1387 *
1388 * @see {@link replace} for returning the original source on failure
1389 *
1390 * @category transforming
1391 * @since 4.0.0
1392 */
1393export const replaceResult: {
1394 <S, A>(
1395 optic: Optional<S, A>,
1396 value: NoInfer<A>
1397 ): (self: NoInfer<S>) => Result.Result<S, SchemaIssue.Issue>
1398 <S, A>(
1399 self: NoInfer<S>,
1400 optic: Optional<S, A>,
1401 value: NoInfer<A>
1402 ): Result.Result<S, SchemaIssue.Issue>
1403} = dual<
1404 <S, A>(
1405 optic: Optional<S, A>,
1406 value: NoInfer<A>
1407 ) => (self: NoInfer<S>) => Result.Result<S, SchemaIssue.Issue>,
1408 <S, A>(
1409 self: NoInfer<S>,
1410 optic: Optional<S, A>,
1411 value: NoInfer<A>
1412 ) => Result.Result<S, SchemaIssue.Issue>
1413>(3, (self, optic, value) => optic.replaceResult(value, self))
1414
1415/**
1416 * Transforms the focused value in a source.
1417 *
1418 * **When to use**
1419 *
1420 * Use when you want to update a focus with a function.
1421 *
1422 * **Details**
1423 *
1424 * Supports both data-first and data-last forms. A failed focus leaves the
1425 * source unchanged.
1426 *
1427 * @see {@link modifyAll} for transforming every value in a traversal
1428 *
1429 * @category transforming
1430 * @since 4.0.0
1431 */
1432export const modify: {
1433 <S, A>(
1434 optic: Optional<S, A>,
1435 f: (value: NoInfer<A>) => NoInfer<A>
1436 ): (self: NoInfer<S>) => S
1437 <S, A>(
1438 self: NoInfer<S>,
1439 optic: Optional<S, A>,
1440 f: (value: NoInfer<A>) => NoInfer<A>
1441 ): S
1442} = dual<
1443 <S, A>(
1444 optic: Optional<S, A>,
1445 f: (value: NoInfer<A>) => NoInfer<A>
1446 ) => (self: NoInfer<S>) => S,
1447 <S, A>(
1448 self: NoInfer<S>,
1449 optic: Optional<S, A>,
1450 f: (value: NoInfer<A>) => NoInfer<A>
1451 ) => S
1452>(3, (self, optic, f) => optic.modify(f)(self))
1453
1454/**
1455 * Extracts all values focused by a `Traversal` as a plain mutable array.
1456 *
1457 * **When to use**
1458 *
1459 * Use when you need the focused values as a simple `Array<A>` for further
1460 * processing.
1461 *
1462 * **Details**
1463 *
1464 * - Returns an empty array when the traversal cannot focus.
1465 * - Always returns a fresh array (safe to mutate).
1466 * - Supports both data-first and data-last forms.
1467 *
1468 * **Example** (Collecting positive numbers)
1469 *
1470 * ```ts import.meta.vitest
1471 * import { Optic, Schema } from "effect"
1472 *
1473 * type S = { readonly values: ReadonlyArray<number> }
1474 *
1475 * const _pos = Optic.id<S>()
1476 * .key("values")
1477 * .forEach((n) => n.check(Schema.isGreaterThan(0)))
1478 *
1479 * const getPositive = Optic.getAll(_pos)
1480 *
1481 * getPositive({ values: [3, -1, 5] }) // => [3, 5]
1482 *
1483 * getPositive({ values: [-1, -2] }) // => []
1484 * ```
1485 *
1486 * @see {@link Traversal} — the optic type this operates on
1487 *
1488 * @category getters
1489 * @since 4.0.0
1490 */
1491export const getAll: {
1492 <S, A>(traversal: Traversal<S, A>): (self: NoInfer<S>) => Array<A>
1493 <S, A>(self: NoInfer<S>, traversal: Traversal<S, A>): Array<A>
1494} = dual<
1495 <S, A>(traversal: Traversal<S, A>) => (self: NoInfer<S>) => Array<A>,
1496 <S, A>(self: NoInfer<S>, traversal: Traversal<S, A>) => Array<A>
1497>(2, (self, traversal) =>
1498 Result.match(traversal.getResult(self), {
1499 onFailure: () => [],
1500 onSuccess: (as) => [...as]
1501 }))
1502
1503/**
1504 * Transforms every value focused by a `Traversal`.
1505 *
1506 * **When to use**
1507 *
1508 * Use when you want to update each value selected by a traversal.
1509 *
1510 * **Details**
1511 *
1512 * Supports both data-first and data-last forms. A failed traversal leaves the
1513 * source unchanged.
1514 *
1515 * @see {@link modify} for transforming the focus as a whole
1516 * @see {@link getAll} for reading every focused value
1517 *
1518 * @category transforming
1519 * @since 4.0.0
1520 */
1521export const modifyAll: {
1522 <S, A>(
1523 traversal: Traversal<S, A>,
1524 f: (value: NoInfer<A>) => NoInfer<A>
1525 ): (self: NoInfer<S>) => S
1526 <S, A>(
1527 self: NoInfer<S>,
1528 traversal: Traversal<S, A>,
1529 f: (value: NoInfer<A>) => NoInfer<A>
1530 ): S
1531} = dual<
1532 <S, A>(
1533 traversal: Traversal<S, A>,
1534 f: (value: NoInfer<A>) => NoInfer<A>
1535 ) => (self: NoInfer<S>) => S,
1536 <S, A>(
1537 self: NoInfer<S>,
1538 traversal: Traversal<S, A>,
1539 f: (value: NoInfer<A>) => NoInfer<A>
1540 ) => S
1541>(3, (self, traversal, f) => traversal.modifyAll(f)(self))
1542
1543// ---------------------------------------------
1544// Built-in Optics
1545// ---------------------------------------------
1546
1547const identityIso = make([])
1548
1549/**
1550 * Iso that focuses on the whole value unchanged.
1551 *
1552 * **When to use**
1553 *
1554 * Use when you need to start an optic chain with a focus on the whole value.
1555 *
1556 * **Details**
1557 *
1558 * - `get(s)` returns `s`.
1559 * - `set(a)` returns `a`.
1560 * - Singleton — every call returns the same instance.
1561 *
1562 * **Example** (Starting an optic chain)
1563 *
1564 * ```ts import.meta.vitest
1565 * import { Optic } from "effect"
1566 *
1567 * type S = { readonly x: number }
1568 *
1569 * const _x = Optic.id<S>().key("x")
1570 *
1571 * _x.get({ x: 42 }) // => 42
1572 * ```
1573 *
1574 * @see {@link Iso} — the type this function returns
1575 *
1576 * @category constructors
1577 * @since 4.0.0
1578 */
1579export function id<S>(): Iso<S, S> {
1580 return identityIso
1581}
1582
1583/**
1584 * Iso that converts a `Record<string, A>` to an array of
1585 * `[key, value]` entries and back.
1586 *
1587 * **When to use**
1588 *
1589 * Use when you want to traverse or manipulate record entries as an array (e.g.
1590 * with `.forEach()`).
1591 *
1592 * **Details**
1593 *
1594 * - `get` uses `Object.entries`.
1595 * - `set` uses `Object.fromEntries`.
1596 * - Round-trip is lossless for `Record<string, A>`.
1597 *
1598 * **Example** (Traversing record values)
1599 *
1600 * ```ts import.meta.vitest
1601 * import { Optic, Schema } from "effect"
1602 *
1603 * const _positiveValues = Optic.entries<number>()
1604 * .forEach((entry) => entry.key(1).check(Schema.isGreaterThan(0)))
1605 *
1606 * const inc = _positiveValues.modifyAll((n) => n + 1)
1607 *
1608 * inc({ a: 0, b: 3, c: -1 }) // => { a: 0, b: 4, c: -1 }
1609 * ```
1610 *
1611 * @see {@link Iso} — the type this function returns
1612 * @see {@link id} — identity iso
1613 *
1614 * @category constructors
1615 * @since 4.0.0
1616 */
1617export function entries<A>(): Iso<Record<string, A>, ReadonlyArray<readonly [string, A]>> {
1618 return make(primitiveNode("Iso", Object.entries, Object.fromEntries))
1619}
1620
1621/**
1622 * Prism that focuses on the value inside `Option.Some`.
1623 *
1624 * **When to use**
1625 *
1626 * Use when you have an `Option<A>` and want to read/update the inner value only
1627 * when it is `Some`.
1628 *
1629 * **Details**
1630 *
1631 * - `getResult` fails with a structured issue when the option is `None`.
1632 * - `set(a)` wraps `a` in `Option.some(a)`.
1633 *
1634 * **Example** (Accessing Some value)
1635 *
1636 * ```ts import.meta.vitest
1637 * import { Optic, Option, Result } from "effect"
1638 *
1639 * const _some = Optic.id<Option.Option<number>>().compose(Optic.some())
1640 *
1641 * _some.getResult(Option.some(42)) // => Result.succeed(42)
1642 *
1643 * Result.isFailure(_some.getResult(Option.none())) // => true
1644 *
1645 * _some.set(10) // => Option.some(10)
1646 * ```
1647 *
1648 * @see {@link none} — focuses on `None` instead
1649 * @see {@link Prism} — the type this function returns
1650 *
1651 * @category constructors
1652 * @since 4.0.0
1653 */
1654export function some<A>(): Prism<Option.Option<A>, A> {
1655 const run = runRefinement(Option.isSome, { expected: "a Some value" })
1656 return makePrism(
1657 (s) => Result.map(run(s), (s) => s.value),
1658 Option.some
1659 )
1660}
1661
1662/**
1663 * Prism that focuses on `Option.None`, exposing `undefined`.
1664 *
1665 * **When to use**
1666 *
1667 * Use when you want to match or construct `None` values within an optic chain.
1668 *
1669 * **Details**
1670 *
1671 * - `getResult` succeeds with `undefined` when the option is `None`.
1672 * - `getResult` fails when the option is `Some`.
1673 * - `set(undefined)` produces `Option.none()`.
1674 *
1675 * **Example** (Matching None)
1676 *
1677 * ```ts import.meta.vitest
1678 * import { Optic, Option, Result } from "effect"
1679 *
1680 * const _none = Optic.id<Option.Option<number>>().compose(Optic.none())
1681 *
1682 * _none.getResult(Option.none()) // => Result.succeed(undefined)
1683 *
1684 * Result.isFailure(_none.getResult(Option.some(1))) // => true
1685 * ```
1686 *
1687 * @see {@link some} — focuses on `Some` instead
1688 * @see {@link Prism} — the type this function returns
1689 *
1690 * @category constructors
1691 * @since 4.0.0
1692 */
1693export function none<A>(): Prism<Option.Option<A>, undefined> {
1694 const run = runRefinement(Option.isNone, { expected: "a None value" })
1695 return makePrism(
1696 (s) => Result.map(run(s), () => undefined),
1697 () => Option.none()
1698 )
1699}
1700
1701/**
1702 * Prism that focuses on the success value of a `Result`.
1703 *
1704 * **When to use**
1705 *
1706 * Use when you have a `Result<A, E>` and want to read/update `A` only when it
1707 * is a `Success`.
1708 *
1709 * **Details**
1710 *
1711 * - `getResult` fails when the result is a `Failure`.
1712 * - `set(a)` produces `Result.succeed(a)`.
1713 *
1714 * **Example** (Accessing success)
1715 *
1716 * ```ts import.meta.vitest
1717 * import { Optic, Result } from "effect"
1718 *
1719 * const _ok = Optic.id<Result.Result<number, string>>().compose(Optic.success())
1720 *
1721 * _ok.getResult(Result.succeed(42)) // => Result.succeed(42)
1722 *
1723 * Result.isFailure(_ok.getResult(Result.fail("err"))) // => true
1724 * ```
1725 *
1726 * @see {@link failure} — focuses on the failure side
1727 * @see {@link Prism} — the type this function returns
1728 *
1729 * @category constructors
1730 * @since 4.0.0
1731 */
1732export function success<A, E>(): Prism<Result.Result<A, E>, A> {
1733 const run = runRefinement(Result.isSuccess, { expected: "a Result.Success value" })
1734 return makePrism(
1735 (s) => Result.map(run(s), (s) => s.success),
1736 Result.succeed
1737 )
1738}
1739
1740/**
1741 * Prism that focuses on the failure value of a `Result`.
1742 *
1743 * **When to use**
1744 *
1745 * Use when you have a `Result<A, E>` and want to read/update `E` only when it
1746 * is a `Failure`.
1747 *
1748 * **Details**
1749 *
1750 * - `getResult` fails when the result is a `Success`.
1751 * - `set(e)` produces `Result.fail(e)`.
1752 *
1753 * **Example** (Accessing failure)
1754 *
1755 * ```ts import.meta.vitest
1756 * import { Optic, Result } from "effect"
1757 *
1758 * const _err = Optic.id<Result.Result<number, string>>().compose(Optic.failure())
1759 *
1760 * _err.getResult(Result.fail("oops")) // => Result.succeed("oops")
1761 *
1762 * Result.isFailure(_err.getResult(Result.succeed(42))) // => true
1763 * ```
1764 *
1765 * @see {@link success} — focuses on the success side
1766 * @see {@link Prism} — the type this function returns
1767 *
1768 * @category constructors
1769 * @since 4.0.0
1770 */
1771export function failure<A, E>(): Prism<Result.Result<A, E>, E> {
1772 const run = runRefinement(Result.isFailure, { expected: "a Result.Failure value" })
1773 return makePrism(
1774 (s) => Result.map(run(s), (s) => s.failure),
1775 Result.fail
1776 )
1777}
1778
1779function runRefinement<T extends E, E>(
1780 refinement: (e: E) => e is T,
1781 annotations?: Schema.Annotations.Filter
1782): (e: E) => Result.Result<T, SchemaIssue.Issue> {
1783 return (e) => SchemaAST.runChecks([SchemaAST.makeFilterByGuard(refinement, annotations)], e) as any
1784}