dev.constructive.eo.optics

The concrete optic zoo: Optic (the one existential base trait) and its named shapes — Lens, Prism, Iso, Optional, Traversal, Getter, Fold, AffineFold, Review, Modify, Unfold, Plated — each a thin class over a carrier from data with fused andThen overloads so hot compositions skip the generic seam. Construct and compose here; consume through the capability traits in dev.constructive.eo.

Attributes

Members list

Grouped members

Optics

object At

Monocle's At as a plain constructor object — NOT a typeclass. At(k) builds an ordinary total Lens from a Map[K, V] to the Option[V] at key k: replacing with Some(v) inserts or updates, replacing with None deletes the key. The Lens laws hold for every key (get-replace, replace-get, replace-replace). When you only want to touch an EXISTING value — never insert — use Index, whose focus is the bare V.

Monocle's At as a plain constructor object — NOT a typeclass. At(k) builds an ordinary total Lens from a Map[K, V] to the Option[V] at key k: replacing with Some(v) inserts or updates, replacing with None deletes the key. The Lens laws hold for every key (get-replace, replace-get, replace-replace). When you only want to touch an EXISTING value — never insert — use Index, whose focus is the bare V.

Attributes

Source
At.scala
Supertypes
class Object
trait Matchable
class Any
Self type
At.type
object Each

Monocle's Each as a plain constructor object — NOT a typeclass. Each[F, A] is exactly Traversal.each: a Traversal over every element of any cats.Traverse container, provided under the name Monocle users grep for. There is no Each[S, A] typeclass to instance and nothing to derive — the result is an ordinary optic that composes through .andThen and serves capability evidence (CanFold, CanModify) like any other.

Monocle's Each as a plain constructor object — NOT a typeclass. Each[F, A] is exactly Traversal.each: a Traversal over every element of any cats.Traverse container, provided under the name Monocle users grep for. There is no Each[S, A] typeclass to instance and nothing to derive — the result is an ordinary optic that composes through .andThen and serves capability evidence (CanFold, CanModify) like any other.

Attributes

Source
Each.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Each.type
object Index

Monocle's Index as a plain constructor object — NOT a typeclass. Index(i) / Index(k) builds an ordinary Optional focusing one positional or keyed slot; there is no Index[S, I, A] typeclass to instance. Monocle index semantics hold: the focus is the slot's CURRENT value, so a write on an absent slot (index out of bounds, key missing) is a silent pass-through — Index never inserts. For insert-or-update-or-delete on a Map, use At, whose focus is the Option[V].

Monocle's Index as a plain constructor object — NOT a typeclass. Index(i) / Index(k) builds an ordinary Optional focusing one positional or keyed slot; there is no Index[S, I, A] typeclass to instance. Monocle index semantics hold: the focus is the slot's CURRENT value, so a write on an absent slot (index out of bounds, key missing) is a silent pass-through — Index never inserts. For insert-or-update-or-delete on a Map, use At, whose focus is the Option[V].

Bare Index(0) cannot pick between the sequence and map overloads on its own — supply the type arguments (Index[Vector, Int](0), Index[String, Long]("k")) or let .andThen composition pin the source type.

Attributes

Source
Index.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Index.type

Type members

Classlikes

object AffineFold

Constructors for AffineFold.

Constructors for AffineFold.

Attributes

Source
AffineFold.scala
Supertypes
class Object
trait Matchable
class Any
Self type
AffineFold.type
final class BijectionIso[S, T, A, B](read: S => A, build: B => T) extends Optic[S, T, A, B, Direct], CanGet[S, A], CanReverseGet[T, B], CanModifyP[S, T, A, B], CanFold[S, A]

Concrete Optic subclass for an isomorphism. Stores get / reverseGet directly so the hot path skips the Accessor[Direct] / ReverseAccessor[Direct] typeclass dispatches the generic extensions would perform — same storage shape as Monocle's Iso. Returned by Iso.apply so hand-written isos pick up the fused path automatically.

Concrete Optic subclass for an isomorphism. Stores get / reverseGet directly so the hot path skips the Accessor[Direct] / ReverseAccessor[Direct] typeclass dispatches the generic extensions would perform — same storage shape as Monocle's Iso. Returned by Iso.apply so hand-written isos pick up the fused path automatically.

Attributes

Source
Iso.scala
Supertypes
trait CanFold[S, A]
trait CanModifyP[S, T, A, B]
trait CanReverseGet[T, B]
trait CanGet[S, A]
trait Optic[S, T, A, B, Direct]
class Object
trait Matchable
class Any
Show all
final class ComposedTraversal[S, T, A, B, C, D, Xo, Xi](outer: Optic[S, T, A, B, MultiFocus[PSVec]] { type X = Xo; }, inner: Optic[A, B, C, D, MultiFocus[PSVec]] { type X = Xi; }) extends Traversal[S, T, C, D]

The concrete class behind composed — see its scaladoc for why the composition re-homes under Traversal.

The concrete class behind composed — see its scaladoc for why the composition re-homes under Traversal.

NO foldMap override — composed folds keep the base class's materialize-then-fold walk, BY MEASUREMENT (2026-07-22, -prof gc, List[LineItem] fixtures). Streaming was tried in three shapes and lost to the flat composeTo array walk in every regime that matters: per-element dispatch 2x'd B/op on lens∘each∘lens at n ≥ 32 (megamorphic loop body — nothing inlines, the singleton bridge's pair + boxes reach the heap); a pair-free f ∘ singletonFocus variant with the inner resolved once per call still lost ~35% B/op at n ≥ 256 (won only below ~32 foci); and even the each∘each shape — where inner dispatch amortizes over whole sub-containers — measured +37% B/op streamed (TraversalBench.eoFoldNested vs its Optic-ascribed materialized twin). The tight @tailrec walk over one flat array is what the JIT rewards; only the LEAF constructors (pEach / selfChildren) stream. Do not re-stream composed folds without re-measuring.

Attributes

Source
Traversal.scala
Supertypes
class Traversal[S, T, C, D]
trait Optic[S, T, C, D, MultiFocus[PSVec]]
class Object
trait Matchable
class Any
object Fold

Constructors for Fold — read-only multi-focus optic, backed by Forget[F] (Forget[F][X, A] = F[A]). T = Unit rules out the write path; .foldMap is the consumption surface. Fold.select(p) narrows to a one-element Option stream. The build-only dual on the same carrier — assemble a T from an F-layer — is Unfold.

Constructors for Fold — read-only multi-focus optic, backed by Forget[F] (Forget[F][X, A] = F[A]). T = Unit rules out the write path; .foldMap is the consumption surface. Fold.select(p) narrows to a one-element Option stream. The build-only dual on the same carrier — assemble a T from an F-layer — is Unfold.

Both constructors return the concrete ForgetFold subclass so a hand-written Fold picks up its eager, carrier-free foldMap member (see ForgetFold.foldMap).

Attributes

Source
Fold.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Fold.type
final class ForgetFold[S, F[_], A](val read: S => F[A])(using FF: Foldable[F]) extends Optic[S, Unit, A, Unit, Forget[F]], CanFold[S, A]

Concrete Optic subclass for Fold, storing the source projection to and the underlying Foldable[F] directly. This lets the terminal foldMap fold the focus eagerly through the captured Foldable[F], skipping both the per-call ForgetfulFold[Forget[F]] summon and the intermediate S => M closure the generic Optic.foldMap extension builds — the same specialisation GetReplaceLens / Modify / MultiFocusSingleton apply to their hot paths.

Concrete Optic subclass for Fold, storing the source projection to and the underlying Foldable[F] directly. This lets the terminal foldMap fold the focus eagerly through the captured Foldable[F], skipping both the per-call ForgetfulFold[Forget[F]] summon and the intermediate S => M closure the generic Optic.foldMap extension builds — the same specialisation GetReplaceLens / Modify / MultiFocusSingleton apply to their hot paths.

Returned by every Fold.* constructor so hand-written folds pick up the fast path automatically. A composed Fold (the result of .andThen) surfaces as the erased Optic[…, Forget[F]] and keeps the generic extension — the same trade-off GetReplaceLens accepts.

Attributes

Source
Fold.scala
Supertypes
trait CanFold[S, A]
trait Optic[S, Unit, A, Unit, Forget[F]]
class Object
trait Matchable
class Any
class GetReplaceLens[S, T, A, B](read: S => A, val enplace: (S, B) => T) extends Optic[S, T, A, B, Tuple2], CanGet[S, A], CanModifyP[S, T, A, B], CanFold[S, A]

Concrete Optic subclass storing get and enplace directly, enabling the fused-andThen overloads below to bypass the Tuple2 carrier entirely. Returned by every Lens.* constructor so hand-written lenses pick up the fused hot path automatically.

Concrete Optic subclass storing get and enplace directly, enabling the fused-andThen overloads below to bypass the Tuple2 carrier entirely. Returned by every Lens.* constructor so hand-written lenses pick up the fused hot path automatically.

Attributes

Source
Lens.scala
Supertypes
trait CanFold[S, A]
trait CanModifyP[S, T, A, B]
trait CanGet[S, A]
trait Optic[S, T, A, B, Tuple2]
class Object
trait Matchable
class Any
Show all
final class Getter[S, A](read: S => A) extends Optic[S, Unit, A, Unit, Direct], CanGet[S, A], CanFold[S, A]

Concrete Optic subclass for a read-only getter — the one-way view of an S whose focus A can only be read. Both the leftover T and the back-focus B are Unit, making the read-only-ness honest in the type (there is no B to put back, so .modify / .replace never exist). Implements CanGet and CanFold directly, so a Getter can be passed wherever a consuming signature demands CanGet[S, A].

Concrete Optic subclass for a read-only getter — the one-way view of an S whose focus A can only be read. Both the leftover T and the back-focus B are Unit, making the read-only-ness honest in the type (there is no B to put back, so .modify / .replace never exist). Implements CanGet and CanFold directly, so a Getter can be passed wherever a consuming signature demands CanGet[S, A].

Implementation notes: a final class storing get directly — NOT an abstract class with an abstract get: the CI A/B showed composed-getter dispatch through the abstract-class form costs ~1.8x (eoGet_3 5.1ns final vs 11.5ns abstract) even with a fused andThen, while every fused path that stayed a concrete class (GetReplaceLens lens-reuse) was flat. The hot path skips the Accessor[Direct] dispatch the generic extension would perform — the same shape as BijectionIso / GetReplaceLens. Returned by Getter.apply so hand-written getters pick up the fused path automatically.

Attributes

Companion
object
Source
Getter.scala
Supertypes
trait CanFold[S, A]
trait CanGet[S, A]
trait Optic[S, Unit, A, Unit, Direct]
class Object
trait Matchable
class Any
Show all
object Getter

Constructor for Getter — read-only single-focus optic, backed by Direct with T = B = Unit. .get(s) is the only meaningful operation; the write path is vestigial.

Constructor for Getter — read-only single-focus optic, backed by Direct with T = B = Unit. .get(s) is the only meaningful operation; the write path is vestigial.

Both the leftover T and the back-focus B are Unit, which makes the read-only-ness explicit in the type (there is no B to put back). Getter.apply returns a concrete Getter, so a Getter composes with another Getter through the ordinary andThen (the fused Getter.andThen) — g1.andThen(g2) reads s => g2.get(g1.get(s)) — exactly as Iso / Lens compose via their own fused subclasses.

Attributes

Companion
class
Source
Getter.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Getter.type
object Iso

Constructor for Iso — a bijective single-focus optic, backed by Direct. The Iso type alias above names the concrete BijectionIso for the monomorphic case, so ascribing it is safe; Iso.apply returns that class carrying the fused compose overloads and capability mixins. Consuming signatures should still demand a capability (CanGet[S, A], CanReverseGet[S, A], …) rather than name Iso. An iso encodes a data-shape bijection. Direct[X, A] = A carries no leftover, so every Iso operation reduces to plain function application.

Constructor for Iso — a bijective single-focus optic, backed by Direct. The Iso type alias above names the concrete BijectionIso for the monomorphic case, so ascribing it is safe; Iso.apply returns that class carrying the fused compose overloads and capability mixins. Consuming signatures should still demand a capability (CanGet[S, A], CanReverseGet[S, A], …) rather than name Iso. An iso encodes a data-shape bijection. Direct[X, A] = A carries no leftover, so every Iso operation reduces to plain function application.

Attributes

Source
Iso.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Iso.type
object Lens

Constructors for Lens — the always-present single-focus optic, backed by Tuple2. "A Lens[S, A]" is prose shorthand for Optic[S, S, A, A, Tuple2]: there is deliberately NO type Lens[S, A] alias to ascribe (Lens is only this constructor object). The reason is keeping code on the hot, fused paths: TWO concrete classes back this family — GetReplaceLens here, SimpleLens from first / second and the eo-generics macro — so an alias could only name the generic Optic[S, S, A, A, Tuple2], and ascribing that erases the concrete class, dropping its fused compose overloads and capability mixins in favour of generic typeclass dispatch. (Contrast Iso, whose single concrete class does get an alias.) So leave vals un-ascribed and let consuming signatures demand a capability (CanGet[S, A], CanModify[S, A], …) instead of a concrete optic type. A lens reads a field via get(s) and rewrites it via modify / replace. The eo-generics module's lens[S](_.field) macro derives both.

Constructors for Lens — the always-present single-focus optic, backed by Tuple2. "A Lens[S, A]" is prose shorthand for Optic[S, S, A, A, Tuple2]: there is deliberately NO type Lens[S, A] alias to ascribe (Lens is only this constructor object). The reason is keeping code on the hot, fused paths: TWO concrete classes back this family — GetReplaceLens here, SimpleLens from first / second and the eo-generics macro — so an alias could only name the generic Optic[S, S, A, A, Tuple2], and ascribing that erases the concrete class, dropping its fused compose overloads and capability mixins in favour of generic typeclass dispatch. (Contrast Iso, whose single concrete class does get an alias.) So leave vals un-ascribed and let consuming signatures demand a capability (CanGet[S, A], CanModify[S, A], …) instead of a concrete optic type. A lens reads a field via get(s) and rewrites it via modify / replace. The eo-generics module's lens[S](_.field) macro derives both.

Attributes

Source
Lens.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Lens.type
final class MendTearPrism[S, T, A, B](val tear: S => Either[T, A], val mend: B => T) extends Optic[S, T, A, B, Either], CanGetOption[S, A], CanReverseGet[T, B], CanModifyP[S, T, A, B], CanFold[S, A]

Concrete Optic subclass storing tear (getOrModify) and mend (reverseGet) directly. The fused-andThen overloads pattern-match once and skip the generic AssociativeFunctor[Either] round-trip. All Prism.* constructors return this type.

Concrete Optic subclass storing tear (getOrModify) and mend (reverseGet) directly. The fused-andThen overloads pattern-match once and skip the generic AssociativeFunctor[Either] round-trip. All Prism.* constructors return this type.

Attributes

Source
Prism.scala
Supertypes
trait CanFold[S, A]
trait CanModifyP[S, T, A, B]
trait CanReverseGet[T, B]
trait CanGetOption[S, A]
trait Optic[S, T, A, B, Either]
class Object
trait Matchable
class Any
Show all
object Modify

Constructor for Modify — write-only single-focus optic, backed by ModifyF. The caller applies a function at the focus but cannot read it back; useful when observation would leak information or when the focus is genuinely unreadable (e.g. inside a closure).

Constructor for Modify — write-only single-focus optic, backed by ModifyF. The caller applies a function at the focus but cannot read it back; useful when observation would leak information or when the focus is genuinely unreadable (e.g. inside a closure).

'''This is Monocle's / Haskell lens's Setter, renamed.''' The mapping is Setter.modifyModify.modify and Setter.setModify.replace — do not look for a Setter type here.

Modify.apply returns a concrete Modify, so a Modify composes with another Modify through the ordinary andThen (the fused Modify.andThen) — s1.andThen(s2).modify(f) == s1.modify(s2.modify(f)) — exactly as Iso / Lens / Getter compose via their own fused subclasses, bypassing the generic AssociativeFunctor[ModifyF] round-trip.

Attributes

Companion
class
Source
Modify.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Modify.type
final class Modify[S, T, A, B](val modifyFn: (A => B) => S => T) extends Optic[S, T, A, B, ModifyF], CanModifyP[S, T, A, B]

Concrete Optic subclass for a write-only modifier. Stores the writer modifyFn directly (so the hot path skips the ModifyF carrier round-trip the generic extension performs) and carries a fused andThen for modify∘modify composition — the same shape as GetReplaceLens / Getter. Returned by Modify.apply so hand-written modifiers pick up the fused path automatically.

Concrete Optic subclass for a write-only modifier. Stores the writer modifyFn directly (so the hot path skips the ModifyF carrier round-trip the generic extension performs) and carries a fused andThen for modify∘modify composition — the same shape as GetReplaceLens / Getter. Returned by Modify.apply so hand-written modifiers pick up the fused path automatically.

Attributes

Companion
object
Source
Modify.scala
Supertypes
trait CanModifyP[S, T, A, B]
trait Optic[S, T, A, B, ModifyF]
class Object
trait Matchable
class Any
object Optic

Companion for Optic. Hosts the profunctor instances and the capability-gated extension catalogue — .get, .modify, .replace, .foldMap, .modifyA, .all, .reverseGet, .getOption, .put, .transform, .place, .transfer, .andThen (carrier-morphing plus the read-only / AffineFold / Modify / Review / Unfold collapses), .readOnly, .cross, .morph, .headOption, .length, .exists. Adding a new carrier means supplying the typeclass instances of the operations it should support.

Companion for Optic. Hosts the profunctor instances and the capability-gated extension catalogue — .get, .modify, .replace, .foldMap, .modifyA, .all, .reverseGet, .getOption, .put, .transform, .place, .transfer, .andThen (carrier-morphing plus the read-only / AffineFold / Modify / Review / Unfold collapses), .readOnly, .cross, .morph, .headOption, .length, .exists. Adding a new carrier means supplying the typeclass instances of the operations it should support.

'''What composes.''' Of the 121 (outer family ∘ inner family) pairs across the 11 optic families, 87 compose import-free and without a type ascription — same-carrier pairs via compose.AssociativeFunctor, cross-carrier pairs via a summoned compose.Morph (e.g. Lens ∘ Prism auto-morphs to Optional), and read-side collapses via compose.ReadCompose. The remaining 34 cells are void by design and do not compile: building through a non-invertible optic, writing through a read-only one, reading through a write-only one (plus the mirror cells). So an .andThen that fails to compile outside those cells is a typing problem at the call site — most often an Optic[…] ascription that dropped the concrete class's fused overloads, or a Morph-ambiguous chain needing the explicit Optic.morph form — never a missing feature. CompositionMatrixSpec pins the full matrix.

Attributes

Companion
trait
Source
Optic.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Optic.type
trait Optic[S, T, A, B, F[_, _]]

Existential encoding of a profunctor optic — the single trait behind every optic family in cats-eo. The optic is a pair of functions (S => F[X, A], F[X, B] => T) over a carrier F[_, _] and an existential X that threads the leftover information needed to rebuild T. Each family picks a different carrier (Tuple2 for Lens, Either for Prism, Affine for Optional, …). Operations live in the companion as capability-gated extensions; each extension's (using …) clause names the typeclass on F that unlocks it.

Existential encoding of a profunctor optic — the single trait behind every optic family in cats-eo. The optic is a pair of functions (S => F[X, A], F[X, B] => T) over a carrier F[_, _] and an existential X that threads the leftover information needed to rebuild T. Each family picks a different carrier (Tuple2 for Lens, Either for Prism, Affine for Optional, …). Operations live in the companion as capability-gated extensions; each extension's (using …) clause names the typeclass on F that unlocks it.

'''Usage frame: construct via optic, consume via capability.''' Concrete optic types (Lens, Prism, Getter, …) belong where optics are built and composed; a signature that uses an optic should leave the subject generic and demand the weakest capability trait it needs (CanGet, CanGetOption, CanModify, CanFold, …):

def validateId[T](idCarrier: T)(using CanGetOption[T, Id]): Boolean

Keep ONE optic given per (S, A) pair in scope — capabilities are keyed by their type parameters only, so two same-typed foci need newtypes or explicit (using myLens) passing.

Type parameters

A

focus read out of S

B

focus written back to produce T (often = A)

F

two-argument carrier; capabilities scale with the typeclasses F admits.

S

source type being observed / modified

T

result type after modification (often = S)

Attributes

See also
Companion
object
Source
Optic.scala
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class AvroBridge[A, B]
class AvroPrism[A]
class AvroRecordPrism[A]
class JsonPrism[A]
class JsoniterPrism[A]
class BijectionIso[S, T, A, B]
class ForgetFold[S, F, A]
class GetReplaceLens[S, T, A, B]
class Getter[S, A]
class MendTearPrism[S, T, A, B]
class Modify[S, T, A, B]
class Optional[S, T, A, B]
class PickFold[S, A]
class PickMendPrism[S, A, B]
class Review[T, B]
class SplitCombineLens[S, T, A, B, XA]
class SimpleLens[S, A, XA]
class Traversal[S, T, A, B]
class AvroTraversal[A]
class ComposedTraversal[S, T, A, B, C, D, Xo, Xi]
class TraverseTraversal[T, A, B]
class Unfold[T, B, F]
Show all
Self type
Optic[S, T, A, B, F]
object Optional

Constructor for Optional — the conditionally-present single-focus optic, backed by Affine. "An Optional[S, A]" is prose shorthand for Optic[S, S, A, A, Affine] — there is NO two-parameter alias to ascribe; the concrete Optional class below always takes all four parameters (Optional[S, S, A, A] for the monomorphic case). Ascribing the concrete class is fine — that keeps code on the hot, fused paths; what would knock it off is ascribing the generic Optic[…], which drops the fused compose overloads and capability mixins for generic dispatch. Optional.apply returns that concrete class, so leave vals un-ascribed (or name the four-parameter class) and let consuming signatures demand a capability (CanGetOption[S, A], CanModify[S, A], …) instead. An optional encodes a field that may or may not be there. Composes freely with Lens via cross-carrier .andThen (auto-morphs through Composer[Tuple2, Affine]).

Constructor for Optional — the conditionally-present single-focus optic, backed by Affine. "An Optional[S, A]" is prose shorthand for Optic[S, S, A, A, Affine] — there is NO two-parameter alias to ascribe; the concrete Optional class below always takes all four parameters (Optional[S, S, A, A] for the monomorphic case). Ascribing the concrete class is fine — that keeps code on the hot, fused paths; what would knock it off is ascribing the generic Optic[…], which drops the fused compose overloads and capability mixins for generic dispatch. Optional.apply returns that concrete class, so leave vals un-ascribed (or name the four-parameter class) and let consuming signatures demand a capability (CanGetOption[S, A], CanModify[S, A], …) instead. An optional encodes a field that may or may not be there. Composes freely with Lens via cross-carrier .andThen (auto-morphs through Composer[Tuple2, Affine]).

'''Miss semantics (normative).''' A write through a missed optional is a silent identity pass-through: modify(f)(s) and replace(b)(s) return s unchanged (f is never invoked), and foldMap folds zero foci (returns Monoid.empty). No error, no signal. When hit-ness matters, probe with .getOption first.

Attributes

Companion
class
Source
Optional.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Optional.type
final class Optional[S, T, A, B](val getOrModify: S => Either[T, A], val reverseGet: (S, B) => T) extends Optic[S, T, A, B, Affine], CanGetOption[S, A], CanModifyP[S, T, A, B], CanFold[S, A]

Concrete Optic subclass for Optional.apply — stores getOrModify / reverseGet directly so the fused .andThen overloads can skip the generic AssociativeFunctor[Affine] round-trip whenever the other side is also a known subclass.

Concrete Optic subclass for Optional.apply — stores getOrModify / reverseGet directly so the fused .andThen overloads can skip the generic AssociativeFunctor[Affine] round-trip whenever the other side is also a known subclass.

Attributes

Companion
object
Source
Optional.scala
Supertypes
trait CanFold[S, A]
trait CanModifyP[S, T, A, B]
trait CanGetOption[S, A]
trait Optic[S, T, A, B, Affine]
class Object
trait Matchable
class Any
Show all
final class PickFold[S, A](val pick: S => Option[A]) extends Optic[S, Unit, A, Unit, Affine], CanGetOption[S, A], CanFold[S, A]

Concrete Optic subclass for the AffineFold family — the read-only 0-or-1-focus optic, Optic[S, Unit, A, Unit, Affine]. Both T and the write-focus B are Unit (honestly one-way, like Getter / Fold): there is no value to put back, so .modify / .replace don't apply. The surface is .getOption and .foldMap. Composes with Lens / Prism via the same Morph bridges full Optional uses (they key off the carrier, not the T slot).

Concrete Optic subclass for the AffineFold family — the read-only 0-or-1-focus optic, Optic[S, Unit, A, Unit, Affine]. Both T and the write-focus B are Unit (honestly one-way, like Getter / Fold): there is no value to put back, so .modify / .replace don't apply. The surface is .getOption and .foldMap. Composes with Lens / Prism via the same Morph bridges full Optional uses (they key off the carrier, not the T slot).

"An AffineFold[S, A]" is prose shorthand — there is deliberately NO type alias (one existed and was removed): an alias could only name the generic Optic[S, Unit, A, Unit, Affine] — composed read-collapse results are generic, not PickFold — and ascribing that knocks a CONSTRUCTED fold off the hot, fused paths below (getOption / andThen here on the concrete class). Ascribe PickFold[S, A] when you hold a constructed one; spell the full Optic[…] only for composed results — or, per the doctrine, leave vals un-ascribed and demand CanGetOption[S, A] / CanFold[S, A] in consuming signatures.

A final class storing pick directly, NOT a shared anonymous wrapper: a single anon class would host one pick.apply bytecode site for every AffineFold instance, the megamorphic-dispatch trap PrintInlining exposed on the abstract-class Getter (the JIT profiles that one site over all instances' lambdas and gives up inlining). Same storage shape as PickMendPrism (pick) and ForgetFold (read). Specialised existential X = (Unit, Unit): Affine.Hit stores snd: Unit + b: A — one reference slot less than the (S, A) payload of a full Optional, since from throws its input away anyway. Returned by AffineFold.apply / AffineFold.select so hand-written affine folds pick up the fused members automatically.

Attributes

Source
AffineFold.scala
Supertypes
trait CanFold[S, A]
trait CanGetOption[S, A]
trait Optic[S, Unit, A, Unit, Affine]
class Object
trait Matchable
class Any
Show all
final class PickMendPrism[S, A, B](val pick: S => Option[A], val mend: B => S) extends Optic[S, S, A, B, Either], CanGetOption[S, A], CanReverseGet[S, B], CanModifyP[S, S, A, B], CanFold[S, A]

Concrete Optic subclass for the Option-shaped Prism (Prism.optional / Prism.pOptional). Stores pick and mend directly; the fused extensions pattern-match on Option so the hot path never builds the intermediate Either[S, A] the generic MendTearPrism would.

Concrete Optic subclass for the Option-shaped Prism (Prism.optional / Prism.pOptional). Stores pick and mend directly; the fused extensions pattern-match on Option so the hot path never builds the intermediate Either[S, A] the generic MendTearPrism would.

Attributes

Source
Prism.scala
Supertypes
trait CanFold[S, A]
trait CanModifyP[S, S, A, B]
trait CanReverseGet[S, B]
trait CanGetOption[S, A]
trait Optic[S, S, A, B, Either]
class Object
trait Matchable
class Any
Show all
trait Plated[S]

A self-similar structure: a value of S whose immediate sub-terms are themselves S. The single member plate is a Traversal[S, S] focusing those immediate children — the cats-eo analogue of Haskell lens's Plated class (plate :: Traversal' a a).

A self-similar structure: a value of S whose immediate sub-terms are themselves S. The single member plate is a Traversal[S, S] focusing those immediate children — the cats-eo analogue of Haskell lens's Plated class (plate :: Traversal' a a).

The recursion combinators in the companion (Plated.transform, Plated.rewrite, Plated.children, Plated.universe) build on plate to walk the whole tree. They are stack-safe on deep trees: transform recurses on the call stack while shallow and falls back to a heap-stack machine past a depth bound, rewrite trampolines through cats.Eval, and universe / children use an explicit worklist — a degenerate 100k-deep tree is fine.

The children are carried as a PSVec (the MultiFocus[PSVec] carrier's focus vector), so the read path and the write path share one representation with no List round-trips — see childrenVec.

Get an instance by hand via Plated.fromChildren / Plated.fromChildrenVec, by deriving one with dev.constructive.eo.generics.plate[S], or — when you have already built the self-traversal as an optic — by calling .asPlated on it (see the extension methods below).

Attributes

See also

Traversal.selfChildren for the underlying carrier.

Companion
object
Source
Plated.scala
Supertypes
class Object
trait Matchable
class Any
object Plated

Combinators over Plated — recursion schemes faithful to Control.Lens.Plated, all stack-safe. Each is offered both as a using Plated[S] method here and as an extension on any self-traversal optic you build directly (so myPlate.transformAll(f)(s) works without a typeclass).

Combinators over Plated — recursion schemes faithful to Control.Lens.Plated, all stack-safe. Each is offered both as a using Plated[S] method here and as an extension on any self-traversal optic you build directly (so myPlate.transformAll(f)(s) works without a typeclass).

Attributes

Companion
trait
Source
Plated.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Plated.type
object Prism

Constructors for Prism — the partial single-focus optic, backed by Either. "A Prism[S, A]" is prose shorthand for Optic[S, S, A, A, Either]: there is deliberately NO type Prism[S, A] alias to ascribe (Prism is only this constructor object) — TWO concrete classes back the family (MendTearPrism, and PickMendPrism from optional / pOptional), so an alias could only name the generic Optic, and ascribing that would knock code off the hot, fused paths (the fused compose overloads and capability mixins live on the concrete classes; contrast Iso, whose single concrete class does get an alias). Leave vals un-ascribed and let consuming signatures demand a capability (CanGetOption[S, A], CanReverseGet[S, A], …) instead. A prism encodes a branch of a sum type: getOption(s) succeeds when s matches, reverseGet(a) lifts back. The eo-generics module's prism[S, A] macro derives prisms on enums / sealed traits / union types.

Constructors for Prism — the partial single-focus optic, backed by Either. "A Prism[S, A]" is prose shorthand for Optic[S, S, A, A, Either]: there is deliberately NO type Prism[S, A] alias to ascribe (Prism is only this constructor object) — TWO concrete classes back the family (MendTearPrism, and PickMendPrism from optional / pOptional), so an alias could only name the generic Optic, and ascribing that would knock code off the hot, fused paths (the fused compose overloads and capability mixins live on the concrete classes; contrast Iso, whose single concrete class does get an alias). Leave vals un-ascribed and let consuming signatures demand a capability (CanGetOption[S, A], CanReverseGet[S, A], …) instead. A prism encodes a branch of a sum type: getOption(s) succeeds when s matches, reverseGet(a) lifts back. The eo-generics module's prism[S, A] macro derives prisms on enums / sealed traits / union types.

'''Miss semantics (normative).''' A write through a missed prism is a silent identity pass-through: modify(f)(s) and replace(b)(s) return s unchanged (f is never invoked), and foldMap folds zero foci (returns Monoid.empty). No error, no signal. When hit-ness matters, probe with .getOption first.

Attributes

Source
Prism.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Prism.type
final class Review[T, B](build: B => T) extends Optic[Unit, T, Unit, B, Direct], CanReverseGet[T, B]

Reverse-only counterpart to Getter — wraps reverseGet: A => S and is the exact mirror of Getter. Getter is Optic[S, Unit, A, Unit, Direct] (a real to reading S => A, vestigial from); Review is the dual Optic[Unit, S, Unit, A, Direct] — a vestigial to (reads Unit) and a real from that builds S from the focus A.

Reverse-only counterpart to Getter — wraps reverseGet: A => S and is the exact mirror of Getter. Getter is Optic[S, Unit, A, Unit, Direct] (a real to reading S => A, vestigial from); Review is the dual Optic[Unit, S, Unit, A, Direct] — a vestigial to (reads Unit) and a real from that builds S from the focus A.

It therefore IS an Optic — with source Unit the to is exactly as vestigial as Getter's from, so "pure Review has no to" is no reason to sit outside the trait. A Review composes with another Review through the fused andThen just as Getters do, and slots into the Direct-carrier optic surface.

A final class storing reverseGet directly — NOT an abstract class with an abstract member — for the same composed-dispatch reason documented on Getter.

To get the build direction out of an Iso or Prism, wrap its reverse directly — Review(iso.reverseGet) / Review(prism.mend) — rather than via a bespoke factory: an Iso/Prism already is a build direction, so cross-optic from* constructors would be redundant (eo has no Prism.fromIso etc. for the same reason).

Review builds from ONE focus; its many-rung sibling is Unfold (assemble a T from an F-layer of parts), reachable by composition through the fused andThen(Unfold) below.

Attributes

Companion
object
Source
Review.scala
Supertypes
trait CanReverseGet[T, B]
trait Optic[Unit, T, Unit, B, Direct]
class Object
trait Matchable
class Any
object Review

Constructors for Review.

Constructors for Review.

Attributes

Companion
class
Source
Review.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Review.type
final class SimpleLens[S, A, XA](get: S => A, split: S => (XA, A), combine: (XA, A) => S) extends SplitCombineLens[S, S, A, A, XA]

Monomorphic split-combine lens (S = T, A = B). The matched source / target lets the to splitter double as the T => (X, A) evidence the mutation extensions need, so place / transfer land on the class body directly. Used by Lens.first, Lens.second, and the eo-generics lens[S](_.field) macro.

Monomorphic split-combine lens (S = T, A = B). The matched source / target lets the to splitter double as the T => (X, A) evidence the mutation extensions need, so place / transfer land on the class body directly. Used by Lens.first, Lens.second, and the eo-generics lens[S](_.field) macro.

Attributes

Companion
object
Source
Lens.scala
Supertypes
class SplitCombineLens[S, S, A, A, XA]
trait CanFold[S, A]
trait CanModifyP[S, S, A, A]
trait CanGet[S, A]
trait Optic[S, S, A, A, Tuple2]
class Object
trait Matchable
class Any
Show all
object SimpleLens

Companion for SimpleLens. Hands out a transformEvidence given so the generic Optic.transform / .place / .transfer extensions pick up the same behaviour as the class-level methods.

Companion for SimpleLens. Hands out a transformEvidence given so the generic Optic.transform / .place / .transfer extensions pick up the same behaviour as the class-level methods.

Attributes

Companion
class
Source
Lens.scala
Supertypes
class Object
trait Matchable
class Any
Self type
SimpleLens.type
class SplitCombineLens[S, T, A, B, XA](read: S => A, val split: S => (XA, A), val combine: (XA, B) => T) extends Optic[S, T, A, B, Tuple2], CanGet[S, A], CanModifyP[S, T, A, B], CanFold[S, A]

Polymorphic split-combine lens — splitter S => (XA, A) + combiner (XA, B) => T, surfacing the complement as X = XA. Does not ship place / transfer (would require a T => (XA, B) evidence that isn't recoverable when T ≠ S); callers route through the generic Optic extensions when the evidence is available.

Polymorphic split-combine lens — splitter S => (XA, A) + combiner (XA, B) => T, surfacing the complement as X = XA. Does not ship place / transfer (would require a T => (XA, B) evidence that isn't recoverable when T ≠ S); callers route through the generic Optic extensions when the evidence is available.

Attributes

Source
Lens.scala
Supertypes
trait CanFold[S, A]
trait CanModifyP[S, T, A, B]
trait CanGet[S, A]
trait Optic[S, T, A, B, Tuple2]
class Object
trait Matchable
class Any
Show all
Known subtypes
class SimpleLens[S, A, XA]
abstract class Traversal[S, T, A, B] extends Optic[S, T, A, B, MultiFocus[PSVec]]

Concrete family class for Traversal — the many-focus optic on the MultiFocus[PSVec] carrier. Every constructor in the Traversal$ companion (and Each) returns this type, so "a Traversal[S, A]" is spelled Traversal[S, S, A, A] (like the four-parameter Optional and Modify classes). Ascribing it is SAFE — the fused members below are inline, so they survive ascription at THIS type (only ascribing the generic Optic[…] falls back to the generic extensions); capability evidence (CanFold[S, A], CanModify[S, A]) is served by the derived givens in each capability's companion.

Concrete family class for Traversal — the many-focus optic on the MultiFocus[PSVec] carrier. Every constructor in the Traversal$ companion (and Each) returns this type, so "a Traversal[S, A]" is spelled Traversal[S, S, A, A] (like the four-parameter Optional and Modify classes). Ascribing it is SAFE — the fused members below are inline, so they survive ascription at THIS type (only ascribing the generic Optic[…] falls back to the generic extensions); capability evidence (CanFold[S, A], CanModify[S, A]) is served by the derived givens in each capability's companion.

'''Fused modify / replace / foldMap, by measurement''' (JMH TraversalBench.eoModify, size 64, -prof gc): the generic extensions summon the PARAMETERIZED mfFunctor[F: Functor] / mfFold[F: Foldable] givens — and a parameterized given instantiates per call — plus an extra capture in the spliced closure shape; together a fixed 40 B/op per operation (4 904 → 4 864 B/op measured, ±0.001). The members below splice the same logic with the CACHED PSVec.pSVecFunctor / pSVecFoldable instances instead. modify / replace are inline deliberately: each call site gets its own spliced body, so the to / from dispatch sites stay per-site monomorphic across the many subclasses (pEach / selfChildren / fixed-arity / the byte-carried integration traversals) — a plain def here would be ONE shared body accumulating every subclass's type profile, the megamorphic trap documented on Getter and PickFold (a plain-def variant was also measured: it recovers only 16 of the 40 B/op). foldMap is the deliberate exception — a virtual def the constructors override with STREAMING folds that skip to(s)'s focus-vector build entirely; see its scaladoc.

Each instance keeps its own existential X (the reassembly context: the original container for Traversal.pEach, the node for Traversal.selfChildren, Unit for the fixed-arity tabulations). The byte-carried integration traversals — eo-jsoniter's JsoniterTraversal and eo-avro's AvroTraversal — extend this class too (same Array[Byte] / MultiFocus[PSVec] shape), so ascribing them as Traversal[Array[Byte], Array[Byte], A, A] is safe; only eo-circe's JsonTraversal is NOT a subtype — its surface is a bespoke Ior[Chain[JsonFailure], _]-accumulating one, not the MultiFocus[PSVec] carrier.

Attributes

Companion
object
Source
Traversal.scala
Supertypes
trait Optic[S, T, A, B, MultiFocus[PSVec]]
class Object
trait Matchable
class Any
Known subtypes
class AvroTraversal[A]
class ComposedTraversal[S, T, A, B, C, D, Xo, Xi]
class TraverseTraversal[T, A, B]
object Traversal

Constructors for Traversal. Every constructor here — each / pEach / selfChildren and the two / three / four fixed-arity variants — rides the MultiFocus[PSVec] carrier, so they all compose through the standard .andThen in both directions (past a Lens, a Prism, another traversal, …).

Constructors for Traversal. Every constructor here — each / pEach / selfChildren and the two / three / four fixed-arity variants — rides the MultiFocus[PSVec] carrier, so they all compose through the standard .andThen in both directions (past a Lens, a Prism, another traversal, …).

'''Keyed access lives in Index / At — as constructor objects, not typeclasses.''' Index(i) / Index(k) focuses one positional or keyed slot (write on an absent slot passes through — no insert), At(k) is the total Map lens to Option[V] (Some upserts, None deletes), and Each is each under the Monocle-searched name. Each returns an ordinary optic that composes after .andThen like any other; a bespoke keyed Optional remains a one-liner when the container isn't a Seq/Map:

def index[K, V](k: K): Optional[Map[K, V], Map[K, V], V, V] =
 Optional[Map[K, V], Map[K, V], V, V](
   getOrModify = m => m.get(k).toRight(m),
   reverseGet = { case (m, v) => m.updated(k, v) },
 )

Known-arity focus sets go through two / three / four. A predicate-filtering WRITE optic (Monocle's deprecated filterIndex / lens-library filtered) is unlawful — a write can flip which elements the predicate selects, breaking composition laws — so it is not provided; filter on the read side instead (Fold.select / AffineFold.select / Optional.selectReadOnly).

Attributes

Companion
class
Source
Traversal.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Traversal.type
final class TraverseTraversal[T[_], A, B] extends Traversal[T[A], T[B], A, B]

The concrete class behind each / pEach — a Traversal over any Traverse[T], with the original container as the reassembly context.

The concrete class behind each / pEach — a Traversal over any Traverse[T], with the original container as the reassembly context.

Attributes

Source
Traversal.scala
Supertypes
class Traversal[T[A], T[B], A, B]
trait Optic[T[A], T[B], A, B, MultiFocus[PSVec]]
class Object
trait Matchable
class Any
final class Unfold[T, B, F[_]] extends Optic[Unit, T, Unit, B, Forget[F]]

Build-only counterpart to Fold — the inhabitant of the build-only / many cell of the optic lattice, exactly as Review is Getter's build-only mirror on the total rung. Fold is Optic[S, Unit, A, Unit, Forget[F]] (a real to reading S => F[A], vestigial from); Unfold is the across-both-axes dual Optic[Unit, T, Unit, B, Forget[F]] — a vestigial to and a real from that assembles one T from a layer of parts F[B]:

Build-only counterpart to Fold — the inhabitant of the build-only / many cell of the optic lattice, exactly as Review is Getter's build-only mirror on the total rung. Fold is Optic[S, Unit, A, Unit, Forget[F]] (a real to reading S => F[A], vestigial from); Unfold is the across-both-axes dual Optic[Unit, T, Unit, B, Forget[F]] — a vestigial to and a real from that assembles one T from a layer of parts F[B]:

 embed :  F[B] => T        // many → one: the algebra of a recursion scheme

This is the F-shape embed of a Corecursive instance (Fold's S => F[A] being the project half), and the aggregation arrow "build an Order from its line-items". Plated's plate bundles both halves inside one read-write traversal; Unfold exposes the embed half standalone, so an algebra can be carried, composed, and consumed as an optic.

'''The vestigial to.''' Read-only optics zero out their write side for free (from discards into Unit, the terminal object). The build-only dual is not free: to: Unit => F[Unit] must produce an F-layer, and there is no canonical F[Unit] without Applicative[F] (pure(())). Two constructors, two answers:

  • Unfold.apply (F: Applicative) — honest vestigial to = pure(()). Read-side operations that reach to (.modify, .foldMap, …) degrade to the singleton layer.
  • Unfold.algebra (no constraint) — for pattern functors (BinF, RoseF, …), which admit Functor/Traverse but no Applicative (pure cannot pick a constructor). Its to is genuinely unreachable through the build-only surface and THROWS if forced — the same reachable-path contract as Composer.direct2forget's singleton-pick from.

A final class storing embed directly — NOT an abstract member — per the composed-dispatch findings on Getter / Review. Fused andThen members keep build-only chains concrete.

Attributes

Companion
object
Source
Unfold.scala
Supertypes
trait Optic[Unit, T, Unit, B, Forget[F]]
class Object
trait Matchable
class Any
object Unfold

Constructors for Unfold — build-only multi-focus optic, backed by Forget[F] (Forget[F][X, B] = F[B]) with S = A = Unit ruling out the read path; .embed is the consumption surface.

Constructors for Unfold — build-only multi-focus optic, backed by Forget[F] (Forget[F][X, B] = F[B]) with S = A = Unit ruling out the read path; .embed is the consumption surface.

Attributes

Companion
class
Source
Unfold.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Unfold.type

Types

type Iso[S, A] = BijectionIso[S, S, A, A]

Monomorphic Iso — an alias for the CONCRETE BijectionIso, not for the generic Optic[S, S, A, A, Direct]. Because it names the concrete class, ascribing val nameI: Iso[Person, (String, Int)] = … keeps code on the hot, fused paths — the fused compose overloads and capability mixins stay statically visible. This is the one family that CAN afford an alias: a single concrete class backs every Iso, where Lens / Prism each have two (which is why those stay prose shorthand — see Lens).

Monomorphic Iso — an alias for the CONCRETE BijectionIso, not for the generic Optic[S, S, A, A, Direct]. Because it names the concrete class, ascribing val nameI: Iso[Person, (String, Int)] = … keeps code on the hot, fused paths — the fused compose overloads and capability mixins stay statically visible. This is the one family that CAN afford an alias: a single concrete class backs every Iso, where Lens / Prism each have two (which is why those stay prose shorthand — see Lens).

Attributes

Source
Iso.scala