Optic.ts
Optic.ts overview
Section titled “Optic.ts overview”Reads and updates focused parts of values without mutating the original value.
An optic describes where to look inside a value, such as a record field, a union variant, an optional value, or several values in a collection. Different optic types describe different kinds of focus: some always find a value, some may not, and some can find many. This module includes the optic types, constructors, focusing helpers, and operations for replacing, modifying, or collecting focused values.
Since v4.0.0
Exports Grouped by Category
Section titled “Exports Grouped by Category”Iso (interface)
Section titled “Iso (interface)”A lossless, reversible conversion between types S and A.
When to use
Use when you have a pair of functions that convert back and forth without losing
information (e.g. Record ↔ entries, Celsius ↔ Fahrenheit).
- You want the strongest optic that can be composed with any other.
Details
get(s)always succeeds and returns anA.set(a)always succeeds and returns anS.get(set(a)) === aandset(get(s))equalss(round-trip laws).- Extends both
LensandPrism.
Example (Converting between Celsius and Fahrenheit)
import { Optic } from "effect"
const fahrenheit = Optic.makeIso<number, number>( (c) => (c * 9) / 5 + 32, (f) => ((f - 32) * 5) / 9)
console.log(fahrenheit.get(100))// Output: 212
console.log(fahrenheit.set(32))// Output: 0See
makeIso— constructorLens— when you only need a one-directional focus into a wholePrism— when the focus may not be present
Signature
export interface Iso<in out S, in out A> extends Lens<S, A>, Prism<S, A> {}Since v4.0.0
entries
Section titled “entries”Iso that converts a Record<string, A> to an array of
[key, value] entries and back.
When to use
Use when you want to traverse or manipulate record entries as an array (e.g.
with .forEach()).
Details
getusesObject.entries.setusesObject.fromEntries.- Round-trip is lossless for
Record<string, A>.
Example (Traversing record values)
import { Optic, Schema } from "effect"
const _positiveValues = Optic.entries<number>().forEach((entry) => entry.key(1).check(Schema.isGreaterThan(0)))
const inc = _positiveValues.modifyAll((n) => n + 1)
console.log(inc({ a: 0, b: 3, c: -1 }))// Output: { a: 0, b: 4, c: -1 }See
Iso— the type this function returnsid— identity iso
Signature
declare const entries: <A>() => Iso<Record<string, A>, ReadonlyArray<readonly [string, A]>>Since v4.0.0
Iso that focuses on the whole value unchanged.
When to use
Use when you need to start an optic chain with a focus on the whole value.
Details
get(s)returnss.set(a)returnsa.- Singleton — every call returns the same instance.
Example (Starting an optic chain)
import { Optic } from "effect"
type S = { readonly x: number }
const _x = Optic.id<S>().key("x")
console.log(_x.get({ x: 42 }))// Output: 42See
Iso— the type this function returns
Signature
declare const id: <S>() => Iso<S, S>Since v4.0.0
Lens (interface)
Section titled “Lens (interface)”Focuses on exactly one part A inside a whole S.
When to use
Use when you always have a value to read and need the original S to produce
the updated whole, unlike Iso.
Details
get(s)always succeeds and returnsA.replace(a, s)returns a newSwith the focused part replaced.- Extends
Optional. - Composing a Lens with a
PrismorOptionalproduces anOptional.
Example (Focusing on a struct field)
import { Optic } from "effect"
type Person = { readonly name: string; readonly age: number }
const _name = Optic.id<Person>().key("name")
console.log(_name.get({ name: "Alice", age: 30 }))// Output: "Alice"See
makeLens— constructorIso— when conversion is lossless in both directionsOptional— when reading can also fail
Signature
export interface Lens<in out S, in out A> extends Optional<S, A> { readonly get: (s: S) => A}Since v4.0.0
Optional
Section titled “Optional”Optional (interface)
Section titled “Optional (interface)”The most general optic — both reading and writing can fail.
When to use
Use when the focus may not exist in S and writing a new A back may also
fail, for example when the source no longer matches the expected shape. This
is the base type extended by Iso, Lens, Prism, and
Traversal.
Details
getResult(s)returnsResult.Success<A>orResult.Failure<string>.replaceResult(a, s)returnsResult.Success<S>orResult.Failure<string>.replace(a, s)returns the originalson failure (never throws).modify(f)returns the originalson failure (never throws).- All operations are pure; inputs are never mutated.
Example (Focusing on an optional record key)
import { Optic, Result } from "effect"
type Env = { [key: string]: string }const _home = Optic.id<Env>().at("HOME")
console.log(Result.isSuccess(_home.getResult({ HOME: "/root" })))// Output: true
console.log(Result.isFailure(_home.getResult({ PATH: "/bin" })))// Output: true
// replace returns original on failureconsole.log(_home.replace("/new", { PATH: "/bin" }))// Output: { PATH: "/bin" }See
makeOptional— constructorLens— when reading always succeedsPrism— when writing always succeeds
Signature
export interface Optional<in out S, in out A> { readonly node: Node /** * Attempts to read the focus `A` from the whole `S`. Returns * `Result.Success<A>` when the focus exists, or * `Result.Failure<string>` with a descriptive error otherwise. */ readonly getResult: (s: S) => Result.Result<A, string> /** * Replaces the focus in `S` with a new `A`. Returns the original `s` * unchanged when the optic cannot focus (never throws). */ readonly replace: (a: A, s: S) => S /** * Like {@link replace}, but returns an explicit `Result` so callers can * detect and handle failure. */ readonly replaceResult: (a: A, s: S) => Result.Result<S, string> /** * Composes this optic with another. The result type is the weakest of * the two: Iso + Iso = Iso, Lens + Prism = Optional, etc. * * **Example** (Composing a lens with a prism) * * ```ts * import { Optic, Option } from "effect" * * type State = { value: Option.Option<number> } * * const _inner = Optic.id<State>().key("value").compose(Optic.some()) * // _inner is Optional<State, number> * ``` * * @see {@link id} — start a composition chain */ compose<B>(this: Iso<S, A>, that: Iso<A, B>): Iso<S, B> compose<B>(this: Lens<S, A>, that: Lens<A, B>): Lens<S, B> compose<B>(this: Prism<S, A>, that: Prism<A, B>): Prism<S, B> compose<B>(this: Optional<S, A>, that: Optional<A, B>): Optional<S, B>
/** * Returns a function `(s: S) => S` that applies `f` to the focused value. * If the optic cannot focus, the original `s` is returned unchanged. * * **Example** (Incrementing a nested field) * * ```ts * import { Optic } from "effect" * * type S = { readonly a: { readonly b: number } } * const _b = Optic.id<S>().key("a").key("b") * * const inc = _b.modify((n) => n + 1) * console.log(inc({ a: { b: 1 } })) * // Output: { a: { b: 2 } } * ``` */ modify(f: (a: A) => A): (s: S) => S
/** * Focuses on a property of the current struct/tuple focus. * * **Details** * * - On a {@link Lens}, returns a Lens. * - On an {@link Optional}, returns an Optional. * - Does **not** work on union types (compile error). * * **Example** (Drilling into nested structs) * * ```ts * import { Optic } from "effect" * * type S = { readonly a: { readonly b: number } } * const _b = Optic.id<S>().key("a").key("b") * * console.log(_b.get({ a: { b: 42 } })) * // Output: 42 * ``` */ key<S, A extends object, Key extends keyof A>( this: Lens<S, A>, key: Key, ..._err: ForbidUnion<A, "cannot use `key` on a union type"> ): Lens<S, A[Key]> key<S, A extends object, Key extends keyof A>( this: Optional<S, A>, key: Key, ..._err: ForbidUnion<A, "cannot use `key` on a union type"> ): Optional<S, A[Key]>
/** * Focuses on a key where setting `undefined` **removes** the key from the * struct (or splices the element from an array/tuple). * * **Details** * * - The focus type becomes `A[Key] | undefined`. * - Does **not** work on union types (compile error). * * **Example** (Deleting an optional key) * * ```ts * import { Optic } from "effect" * * type S = { readonly a?: number } * const _a = Optic.id<S>().optionalKey("a") * * console.log(_a.replace(undefined, { a: 1 })) * // Output: {} * * console.log(_a.replace(2, {})) * // Output: { a: 2 } * ``` */ optionalKey<S, A extends object, Key extends keyof A>( this: Lens<S, A>, key: Key, ..._err: ForbidUnion<A, "cannot use `optionalKey` on a union type"> ): Lens<S, A[Key] | undefined> optionalKey<S, A extends object, Key extends keyof A>( this: Optional<S, A>, key: Key, ..._err: ForbidUnion<A, "cannot use `optionalKey` on a union type"> ): Optional<S, A[Key] | undefined>
/** * Adds one or more `Schema` validation checks to the optic chain. * `getResult` fails when any check fails; `set` passes through unchanged. * * **Details** * * - On a {@link Prism}, returns a Prism. * - On an {@link Optional}, returns an Optional. * * **Example** (Focusing only on positive numbers) * * ```ts * import { Optic, Result, Schema } from "effect" * * const _pos = Optic.id<number>().check(Schema.isGreaterThan(0)) * * console.log(Result.isSuccess(_pos.getResult(5))) * // Output: true * * console.log(Result.isFailure(_pos.getResult(-1))) * // Output: true * ``` * * @see {@link fromChecks} — standalone prism from checks */ check<S, A>(this: Prism<S, A>, ...checks: readonly [SchemaAST.Check<A>, ...Array<SchemaAST.Check<A>>]): Prism<S, A> check<S, A>( this: Optional<S, A>, ...checks: readonly [SchemaAST.Check<A>, ...Array<SchemaAST.Check<A>>] ): Optional<S, A>
/** * Narrows the focus to a subtype `B` using a type guard. * * **Details** * * - On a {@link Prism}, returns a Prism. * - On an {@link Optional}, returns an Optional. * - Pass optional `annotations` to customize the error message. * * **Example** (Narrowing a union) * * ```ts * import { Optic, Result } from "effect" * * type B = { readonly _tag: "b"; readonly b: number } * type S = { readonly _tag: "a"; readonly a: string } | B * * const _b = Optic.id<S>().refine( * (s: S): s is B => s._tag === "b", * { expected: `"b" tag` } * ) * * console.log(Result.isSuccess(_b.getResult({ _tag: "b", b: 1 }))) * // Output: true * ``` * * @see `.tag()` — shorthand for narrowing by `_tag` */ refine<S, A, B extends A>( this: Prism<S, A>, refinement: (a: A) => a is B, annotations?: Schema.Annotations.Filter ): Prism<S, B> refine<S, A, B extends A>( this: Optional<S, A>, refinement: (a: A) => a is B, annotations?: Schema.Annotations.Filter ): Optional<S, B>
/** * Narrows the focus to the variant of a tagged union with the given * `_tag` value. * * **Details** * * - On a {@link Prism}, returns a Prism. * - On an {@link Optional}, returns an Optional. * - Shorthand for `.refine(s => s._tag === tag)`. * * **Example** (Focusing a tagged variant) * * ```ts * import { Optic, Result } from "effect" * * type Shape = * | { readonly _tag: "Circle"; readonly radius: number } * | { readonly _tag: "Rect"; readonly width: number } * * const _radius = Optic.id<Shape>().tag("Circle").key("radius") * * console.log(Result.isSuccess(_radius.getResult({ _tag: "Circle", radius: 5 }))) * // Output: true * * console.log(Result.isFailure(_radius.getResult({ _tag: "Rect", width: 10 }))) * // Output: true * ``` * * @see `.refine()` — for arbitrary type guards */ tag<S, A extends { readonly _tag: SchemaAST.LiteralValue }, Tag extends A["_tag"]>( this: Prism<S, A>, tag: Tag ): Prism<S, Extract<A, { readonly _tag: Tag }>> tag<S, A extends { readonly _tag: SchemaAST.LiteralValue }, Tag extends A["_tag"]>( this: Optional<S, A>, tag: Tag ): Optional<S, Extract<A, { readonly _tag: Tag }>>
/** * Focuses on a key only if it exists (`Object.hasOwn`). Both * `getResult` and `replaceResult` fail when the key is absent. * * **Details** * * Unlike `.key()`, which always succeeds on the read side, `.at()` is * useful for Records or arrays where the key/index may not be present. * * - Always returns an {@link Optional}. * - Does **not** work on union types (compile error). * * **Example** (Accessing records safely) * * ```ts * import { Optic, Result } from "effect" * * type Env = { [key: string]: number } * const _x = Optic.id<Env>().at("x") * * console.log(Result.isSuccess(_x.getResult({ x: 1 }))) * // Output: true * * console.log(Result.isFailure(_x.getResult({ y: 2 }))) * // Output: true * ``` * * @see `.key()` — when the key is always present */ at<S, A extends object, Key extends keyof A>( this: Optional<S, A>, key: Key, ..._err: ForbidUnion<A, "cannot use `at` on a union type"> ): Optional<S, A[Key]>
/** * Focuses on a subset of keys of the current struct focus. * * **Details** * * - On a {@link Lens}, returns a Lens. * - On an {@link Optional}, returns an Optional. * - Does **not** work on union types (compile error). * * **Example** (Picking keys) * * ```ts * import { Optic } from "effect" * * type S = { readonly a: string; readonly b: number; readonly c: boolean } * * const _ac = Optic.id<S>().pick(["a", "c"]) * * console.log(_ac.get({ a: "hi", b: 1, c: true })) * // Output: { a: "hi", c: true } * ``` * * @see `.omit()` — the inverse operation */ pick<S, A, Keys extends ReadonlyArray<keyof A>>( this: Lens<S, A>, keys: Keys, ..._err: ForbidUnion<A, "cannot use `pick` on a union type"> ): Lens<S, Pick<A, Keys[number]>> pick<S, A, Keys extends ReadonlyArray<keyof A>>( this: Optional<S, A>, keys: Keys, ..._err: ForbidUnion<A, "cannot use `pick` on a union type"> ): Optional<S, Pick<A, Keys[number]>>
/** * Focuses on all keys **except** the specified ones. * * **Details** * * - On a {@link Lens}, returns a Lens. * - On an {@link Optional}, returns an Optional. * - Does **not** work on union types (compile error). * * **Example** (Omitting keys) * * ```ts * import { Optic } from "effect" * * type S = { readonly a: string; readonly b: number; readonly c: boolean } * * const _ac = Optic.id<S>().omit(["b"]) * * console.log(_ac.get({ a: "hi", b: 1, c: true })) * // Output: { a: "hi", c: true } * ``` * * @see `.pick()` — the inverse operation * * @since 4.0.0 */ omit<S, A, Keys extends ReadonlyArray<keyof A>>( this: Lens<S, A>, keys: Keys, ..._err: ForbidUnion<A, "cannot use `omit` on a union type"> ): Lens<S, Omit<A, Keys[number]>> omit<S, A, Keys extends ReadonlyArray<keyof A>>( this: Optional<S, A>, keys: Keys, ..._err: ForbidUnion<A, "cannot use `omit` on a union type"> ): Optional<S, Omit<A, Keys[number]>>
/** * Filters out `undefined` from the focus, producing a {@link Prism}. * `getResult` fails when the focus is `undefined`. * * **Example** (Filtering undefined values) * * ```ts * import { Optic, Result } from "effect" * * const _defined = Optic.id<number | undefined>().notUndefined() * * console.log(Result.isSuccess(_defined.getResult(42))) * // Output: true * * console.log(Result.isFailure(_defined.getResult(undefined))) * // Output: true * ``` * * @since 4.0.0 */ notUndefined(): Prism<S, Exclude<A, undefined>> notUndefined(): Optional<S, Exclude<A, undefined>>
/** * Focuses **all elements** of an array-like focus and optionally narrows * to a subset using an element-level optic. * Available only on {@link Traversal} (i.e. when `A` is * `ReadonlyArray<Element>`). Returns a new Traversal focused on the * selected elements. * * **Details** * * - **getResult** collects the values focused by `f(id<A>())` for each * element. Non-focusable elements are skipped. * - **replaceResult** expects exactly as many values as were collected by * `getResult` and writes them back in order. Fails with a * length-mismatch error if counts differ. * * **Example** (Incrementing liked posts) * * ```ts * import { Optic, Schema } from "effect" * * type Post = { title: string; likes: number } * type S = { user: { posts: ReadonlyArray<Post> } } * * const _likes = Optic.id<S>() * .key("user") * .key("posts") * .forEach((post) => post.key("likes").check(Schema.isGreaterThan(0))) * * const addLike = _likes.modifyAll((n) => n + 1) * * console.log( * addLike({ * user: { posts: [{ title: "a", likes: 0 }, { title: "b", likes: 1 }] } * }) * ) * // Output: { user: { posts: [{ title: "a", likes: 0 }, { title: "b", likes: 2 }] } } * ``` * * @see {@link getAll} — extract all focused elements as an array * @see `.modifyAll()` — apply a function to every focused element */ forEach<S, A, B>(this: Traversal<S, A>, f: (iso: Iso<A, A>) => Optional<A, B>): Traversal<S, B>
/** * Applies a function to **every** element focused by the traversal. * * **Details** * * Available only on {@link Traversal}. Returns a function `(s: S) => S`. * If the traversal cannot focus, the original `s` is returned unchanged. * * Unlike `.modify()`, which operates on the whole array, `modifyAll` * maps `f` over each individual element. * * **Example** (Doubling all focused values) * * ```ts * import { Optic, Schema } from "effect" * * type S = { readonly items: ReadonlyArray<number> } * * const _positive = Optic.id<S>() * .key("items") * .forEach((n) => n.check(Schema.isGreaterThan(0))) * * const doubled = _positive.modifyAll((n) => n * 2) * * console.log(doubled({ items: [1, -2, 3] })) * // Output: { items: [2, -2, 6] } * ``` * * @see `.forEach()` — create a sub-traversal * @see {@link getAll} — extract focused elements */ modifyAll<S, A>(this: Traversal<S, A>, f: (a: A) => A): (s: S) => S}Since v4.0.0
Prism (interface)
Section titled “Prism (interface)”Focuses on a part A of S that may not be present (e.g. a union
variant or a validated subset).
When to use
Use when the focus is conditional — reading can fail (wrong variant, failed validation).
- Building a new
SfromAdoes not require the originalS.
Details
getResult(s)returnsResult.Success<A>when the focus matches, orResult.Failure<string>with an error message.set(a)always succeeds and returns a newS.- Extends
Optional. - Composing two Prisms produces a Prism; composing a Prism with a
Lensproduces anOptional.
Example (Narrowing a tagged union)
import { Optic, Result } from "effect"
type Shape = { readonly _tag: "Circle"; readonly radius: number } | { readonly _tag: "Rect"; readonly width: number }
const _circle = Optic.id<Shape>().tag("Circle")
console.log(Result.isSuccess(_circle.getResult({ _tag: "Circle", radius: 5 })))// Output: true
console.log(Result.isFailure(_circle.getResult({ _tag: "Rect", width: 10 })))// Output: trueSee
makePrism— constructorfromChecks— build a Prism from schema checksLens— when reading always succeeds
Signature
export interface Prism<in out S, in out A> extends Optional<S, A> { readonly set: (a: A) => S}Since v4.0.0
failure
Section titled “failure”Prism that focuses on the failure value of a Result.
When to use
Use when you have a Result<A, E> and want to read/update E only when it
is a Failure.
Details
getResultfails when the result is aSuccess.set(e)producesResult.fail(e).
Example (Accessing failure)
import { Optic, Result } from "effect"
const _err = Optic.id<Result.Result<number, string>>().compose(Optic.failure())
console.log(Result.isSuccess(_err.getResult(Result.fail("oops"))))// Output: true
console.log(Result.isFailure(_err.getResult(Result.succeed(42))))// Output: trueSee
success— focuses on the success sidePrism— the type this function returns
Signature
declare const failure: <A, E>() => Prism<Result.Result<A, E>, E>Since v4.0.0
Prism that focuses on Option.None, exposing undefined.
When to use
Use when you want to match or construct None values within an optic chain.
Details
getResultsucceeds withundefinedwhen the option isNone.getResultfails when the option isSome.set(undefined)producesOption.none().
Example (Matching None)
import { Optic, Option, Result } from "effect"
const _none = Optic.id<Option.Option<number>>().compose(Optic.none())
console.log(Result.isSuccess(_none.getResult(Option.none())))// Output: true
console.log(Result.isFailure(_none.getResult(Option.some(1))))// Output: trueSee
some— focuses onSomeinsteadPrism— the type this function returns
Signature
declare const none: <A>() => Prism<Option.Option<A>, undefined>Since v4.0.0
Prism that focuses on the value inside Option.Some.
When to use
Use when you have an Option<A> and want to read/update the inner value only
when it is Some.
Details
getResultfails with an error message when the option isNone.set(a)wrapsainOption.some(a).
Example (Accessing Some value)
import { Optic, Option, Result } from "effect"
const _some = Optic.id<Option.Option<number>>().compose(Optic.some())
console.log(Result.isSuccess(_some.getResult(Option.some(42))))// Output: true
console.log(Result.isFailure(_some.getResult(Option.none())))// Output: true
console.log(_some.set(10))// Output: { _tag: "Some", value: 10 }See
none— focuses onNoneinsteadPrism— the type this function returns
Signature
declare const some: <A>() => Prism<Option.Option<A>, A>Since v4.0.0
success
Section titled “success”Prism that focuses on the success value of a Result.
When to use
Use when you have a Result<A, E> and want to read/update A only when it
is a Success.
Details
getResultfails when the result is aFailure.set(a)producesResult.succeed(a).
Example (Accessing success)
import { Optic, Result } from "effect"
const _ok = Optic.id<Result.Result<number, string>>().compose(Optic.success())
console.log(Result.isSuccess(_ok.getResult(Result.succeed(42))))// Output: true
console.log(Result.isFailure(_ok.getResult(Result.fail("err"))))// Output: trueSee
failure— focuses on the failure sidePrism— the type this function returns
Signature
declare const success: <A, E>() => Prism<Result.Result<A, E>, A>Since v4.0.0
Traversal
Section titled “Traversal”Traversal (interface)
Section titled “Traversal (interface)”An optic that focuses on zero or more elements of type A inside S.
When to use
Use when you want to read/update multiple elements at once (e.g. all items in an array, or a filtered subset).
Details
- Technically
Optional<S, ReadonlyArray<A>>— the focused value is an array of all matched elements. - Use
.forEach()to add per-element sub-optics (filtering, drilling deeper). - Use
.modifyAll(f)to map a function over every focused element. - Use
getAllto extract all focused elements as a plain array.
Example (Traversing array elements with a filter)
import { Optic, Schema } from "effect"
type S = { readonly items: ReadonlyArray<number> }
const _positive = Optic.id<S>() .key("items") .forEach((n) => n.check(Schema.isGreaterThan(0)))
const getPositive = Optic.getAll(_positive)
console.log(getPositive({ items: [1, -2, 3] }))// Output: [1, 3]See
getAll— extract focused elementsOptional— the base type
Signature
export interface Traversal<in out S, in out A> extends Optional<S, ReadonlyArray<A>> {}Since v4.0.0
getAll
Section titled “getAll”Returns a function that extracts all elements focused by a
Traversal as a plain mutable array.
When to use
Use when you need the focused values as a simple Array<A> for further
processing.
Details
- Returns an empty array when the traversal cannot focus.
- Always returns a fresh array (safe to mutate).
Example (Collecting positive numbers)
import { Optic, Schema } from "effect"
type S = { readonly values: ReadonlyArray<number> }
const _pos = Optic.id<S>() .key("values") .forEach((n) => n.check(Schema.isGreaterThan(0)))
const getPositive = Optic.getAll(_pos)
console.log(getPositive({ values: [3, -1, 5] }))// Output: [3, 5]
console.log(getPositive({ values: [-1, -2] }))// Output: []See
Traversal— the optic type this operates on
Signature
declare const getAll: <S, A>(traversal: Traversal<S, A>) => (s: S) => Array<A>Since v4.0.0
constructors
Section titled “constructors”fromChecks
Section titled “fromChecks”Creates a Prism from one or more Schema validation checks.
When to use
Use when you want to narrow T to the subset that passes certain validation
rules (e.g. positive integer).
- You already have
Schema.isGreaterThan,Schema.isInt, etc.
Details
getResultruns all checks; fails with a combined error message when any check fails.setis identity — the value passes through unchanged.
Example (Creating a positive integer prism)
import { Optic, Result, Schema } from "effect"
const posInt = Optic.fromChecks<number>(Schema.isGreaterThan(0), Schema.isInt())
console.log(Result.isSuccess(posInt.getResult(3)))// Output: true
console.log(Result.isFailure(posInt.getResult(-1)))// Output: trueSee
makePrism— constructor with custom getter/setterPrism— the type this function returns
Signature
declare const fromChecks: <T>(checks_0: SchemaAST.Check<T>, ...checks: Array<SchemaAST.Check<T>>) => Prism<T, T>Since v4.0.0
makeIso
Section titled “makeIso”Creates an Iso from a pair of conversion functions.
When to use
Use when you have two pure conversion functions that preserve all information
between S and A.
Details
The returned optic can be composed with any other optic.
Example (Wrapping and unwrapping a branded type)
import { Optic } from "effect"
type Meters = { readonly value: number }const meters = Optic.makeIso<Meters, number>( (m) => m.value, (n) => ({ value: n }))
console.log(meters.get({ value: 100 }))// Output: 100
console.log(meters.set(42))// Output: { value: 42 }See
Iso— the type this function returnsid— identity iso (no conversion)
Signature
declare const makeIso: <S, A>(get: (s: S) => A, set: (a: A) => S) => Iso<S, A>Since v4.0.0
makeLens
Section titled “makeLens”Creates a Lens from a getter and a replacer.
When to use
Use when you can always extract A from S and produce a new S by
substituting a new A.
Details
replace(a, s)should return a structurally newSwithain place of the old focus.
Example (Focusing on the first element of a pair)
import { Optic } from "effect"
const _first = Optic.makeLens<readonly [string, number], string>( (pair) => pair[0], (s, pair) => [s, pair[1]])
console.log(_first.get(["hello", 42]))// Output: "hello"
console.log(_first.replace("world", ["hello", 42]))// Output: ["world", 42]See
Lens— the type this function returnsmakeIso— when no originalSis needed forset
Signature
declare const makeLens: <S, A>(get: (s: S) => A, replace: (a: A, s: S) => S) => Lens<S, A>Since v4.0.0
makeOptional
Section titled “makeOptional”Creates an Optional from a fallible getter and a fallible setter.
When to use
Use when you need an optic for a focus that may be missing on read and may reject updates on write.
Details
getResultshould returnResult.fail(message)on mismatch.setshould returnResult.fail(message)when the update cannot be applied.
Example (Accessing record keys safely)
import { Optic, Result } from "effect"
const atKey = (key: string) => Optic.makeOptional<Record<string, number>, number>( (s) => (Object.hasOwn(s, key) ? Result.succeed(s[key]) : Result.fail(`Key "${key}" not found`)), (a, s) => (Object.hasOwn(s, key) ? Result.succeed({ ...s, [key]: a }) : Result.fail(`Key "${key}" not found`)) )
console.log(Result.isSuccess(atKey("x").getResult({ x: 1 })))// Output: trueSee
Optional— the type this function returnsmakeLens— when reading always succeedsmakePrism— when writing always succeeds
Signature
declare const makeOptional: <S, A>( getResult: (s: S) => Result.Result<A, string>, set: (a: A, s: S) => Result.Result<S, string>) => Optional<S, A>Since v4.0.0
makePrism
Section titled “makePrism”Creates a Prism from a fallible getter and an infallible setter.
When to use
Use when reading can fail (the part may not exist in S), but building S
from A always succeeds.
Details
getResultshould returnResult.fail(message)on mismatch.
Example (Parsing a string to a number)
import { Optic, Result } from "effect"
const numeric = Optic.makePrism<string, number>((s) => { const n = Number(s) return Number.isNaN(n) ? Result.fail("not a number") : Result.succeed(n)}, String)
console.log(Result.isSuccess(numeric.getResult("42")))// Output: true
console.log(numeric.set(42))// Output: "42"See
Prism— the type this function returnsfromChecks— build fromSchemachecks instead
Signature
declare const makePrism: <S, A>(getResult: (s: S) => Result.Result<A, string>, set: (a: A) => S) => Prism<S, A>Since v4.0.0