dev.constructive.eo.data
Carriers — the F[_, _] shapes an optics.Optic's to / from run through: Direct (plain focus, no leftover), Either (prism miss), Affine (miss '''or''' hit-with-context), Forget / opaque ForgetK (read-only collapse), ModifyF (effectful write), MultiFocus / opaque MultiFocusK (many foci), plus the perf substrate (PSVec and the array builders). Each carrier's companion hosts its typeclass instances, so composition resolves with no imports.
Attributes
Members list
Type members
Classlikes
Constructors and typeclass instances for Affine.
Constructors and typeclass instances for Affine.
Attributes
- Companion
- trait
- Source
- Affine.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Affine.type
Carrier for the Optional family — sealed hierarchy: Affine.Miss (no focus; carries Fst[A]) and Affine.Hit (focus present; carries Snd[A] + B). Miss / Hit store fields directly (no Either / Tuple2 wrapper); match Miss(...) / Hit(...) or use fold for zero-alloc access.
Carrier for the Optional family — sealed hierarchy: Affine.Miss (no focus; carries Fst[A]) and Affine.Hit (focus present; carries Snd[A] + B). Miss / Hit store fields directly (no Either / Tuple2 wrapper); match Miss(...) / Hit(...) or use fold for zero-alloc access.
At every constructor site A is a concrete Tuple2 (so Fst[A] / Snd[A] reduce); when carried through an Optic[…, Affine] existential, A is abstract and the match types stay inert.
Type parameters
- A
-
existential leftover tuple
- B
-
focus type
Attributes
- Companion
- object
- Source
- Affine.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
Typeclass instances for Direct, plus the wrap / unwrap boundary. Every operation collapses to plain function application at runtime — zero per-call overhead beyond the user's function.
Typeclass instances for Direct, plus the wrap / unwrap boundary. Every operation collapses to plain function application at runtime — zero per-call overhead beyond the user's function.
Attributes
- Source
- Direct.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Direct.type
API façade under the carrier's public name. The instances live in ForgetK (the opaque anchor's companion, where implicit scope finds them); this re-export keeps Forget.assocFor call-shapes and legacy import data.Forget.given working.
API façade under the carrier's public name. The instances live in ForgetK (the opaque anchor's companion, where implicit scope finds them); this re-export keeps Forget.assocFor call-shapes and legacy import data.Forget.given working.
Attributes
- Source
- Forget.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Forget.type
Capability ladder for Forget. Each typeclass on F unlocks a matching optic operation:
Capability ladder for Forget. Each typeclass on F unlocks a matching optic operation:
F: Functor → ForgetfulFunctor[Forget[F]] → .modify / .replace
F: Foldable → ForgetfulFold[Forget[F]] → .foldMap
F: Traverse → ForgetfulTraverse[Forget[F], Applicative] → .modifyA, .all
F: Applicative → ForgetfulApplicative[Forget[F]] → .put
F: Monad → AssociativeFunctor[Forget[F], _, _] → same-carrier .andThen
(algebraic-lens shape)
Forget[F]'s X is phantom — Traversal / Fold need no outer-structural context on from. For cases where the outer's leftover must survive, use the pair carrier MultiFocus; Forget[F] injects trivially into it via Composer[Forget[F], MultiFocus[F]]. Direct-targeting instances live in Direct.
Attributes
- Source
- Forget.scala
- Supertypes
- Self type
-
ForgetK.type
Lower-priority instance drawer — holds the FlatMap + Comonad AssociativeFunctor which composes via coflatMap (parallel-fold semantics, genuinely different from the Monad-based algebraic-lens composition in ForgetK). Kept at lower priority so Monad wins when both apply.
Lower-priority instance drawer — holds the FlatMap + Comonad AssociativeFunctor which composes via coflatMap (parallel-fold semantics, genuinely different from the Monad-based algebraic-lens composition in ForgetK). Kept at lower priority so Monad wins when both apply.
Attributes
- Source
- Forget.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
-
object ForgetK
Typeclass instances for ModifyF.
Typeclass instances for ModifyF.
Attributes
- Companion
- class
- Source
- ModifyF.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
ModifyF.type
Carrier for the Modify family — pairs a source Fst[A] with a continuation Snd[A] => B.
Carrier for the Modify family — pairs a source Fst[A] with a continuation Snd[A] => B.
Same-carrier composition (modify.andThen(modify)) ships via ModifyF.assocModifyF — AssociativeFunctor[ModifyF, Xo, Xi] with type Z = (Fst[Xo], Snd[Xi]). The deferred-modify semantic fits the protocol once you observe that composeTo only needs to seed (xo, identity) (no inner-to call required, since ModifyF's continuation is structurally identity at every canonical construction site — coerceToModify and Modify.apply); composeFrom then extracts the user's c2d from the mapped continuation and routes it through inner.from then outer.from. The asInstanceOf casts inside the instance are sound under the universal convention that every ModifyF optic stores X = (S_outer, A_focus) (enforced at every construction site).
Type parameters
- A
-
existential leftover tuple
- B
-
focus written back
Attributes
- Companion
- object
- Source
- ModifyF.scala
- Supertypes
-
class AnyValtrait Matchableclass Any
API façade under the carrier's public name. The instances live in MultiFocusK (the opaque anchor's companion, where implicit scope finds them); this re-export keeps MultiFocus.apply / MultiFocus.fromLensF call-shapes and legacy import data.MultiFocus.given working.
API façade under the carrier's public name. The instances live in MultiFocusK (the opaque anchor's companion, where implicit scope finds them); this re-export keeps MultiFocus.apply / MultiFocus.fromLensF call-shapes and legacy import data.MultiFocus.given working.
Attributes
- Source
- MultiFocus.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
MultiFocus.type
Attributes
- Source
- MultiFocus.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
MultiFocusK.type
Constructors for PSVec — the primary entry points are empty, singleton, and unsafeWrap (zero-copy from an Array[Any]). The three Slice / Single / Empty subclasses are internal to the dev.constructive.eo.data package and stable across release lines only at the aggregate PSVec supertype level.
Constructors for PSVec — the primary entry points are empty, singleton, and unsafeWrap (zero-copy from an Array[Any]). The three Slice / Single / Empty subclasses are internal to the dev.constructive.eo.data package and stable across release lines only at the aggregate PSVec supertype level.
Attributes
- Companion
- trait
- Source
- PSVec.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
PSVec.type
Lightweight array-backed focus vector underlying MultiFocus[PSVec]. Three variants so empty and singleton focus vectors don't pay a backing-array allocation:
Lightweight array-backed focus vector underlying MultiFocus[PSVec]. Three variants so empty and singleton focus vectors don't pay a backing-array allocation:
- PSVec.Empty — shared singleton; Prism / Affine miss branches allocate nothing.
- PSVec.Single — element stored inline (~16 B vs a backing-array view's ~40 B).
- PSVec.Slice — arbitrary
(arr, offset, length)view. slice is a zero-copy pointer update — whatmfAssocPSVec.composeFromrelies on for O(1) per-element reassembly.
Equality is value-based across variants.
Attributes
- Companion
- object
- Source
- PSVec.scala
- Supertypes
-
trait IterableOnce[B]class Objecttrait Matchableclass Any
- Known subtypes
A concrete index value for the Function1[X0, *] (Grate) carrier — the witness that makes a bundle read real instead of forged.
A concrete index value for the Function1[X0, *] (Grate) carrier — the witness that makes a bundle read real instead of forged.
'''Why a witness exists at all.''' A Grate bundle is a function X0 => A; reading it needs an X0, and no rule of the type system produces one. Exactly one site in the library needs that read: the from of the bridge's product (Function1BroadcastOptic, private[eo]), which must turn a written MultiFocus[Function1[X0, *]][Unit, B] back into a T, and whose own carrier stores only Unit. Every other read in the Grate surface is handed its index by the caller (to(s), the .at(i) extension, F.index), and every write walks the index space itself (MultiFocusK.tuple counts 0..size-1, representable tabulates). So this typeclass is the whole index-supply story, and it is consulted in exactly one place.
'''What the choice of index does — and does not — affect.''' Instances are read on a path whose bundle is constant by construction (see that optic's from), where every index yields the same value: this witness changes which value is read only when someone hands from a varying bundle by hand. It never changes .modify / .replace / collect*, and it never changes a grate ∘ iso composition — the kernel rebuilds those per index through broadcastFrom, which needs no index.
'''Supply.''' The shipped instances name the canonical first index of the index types the Grate factories fix: Int for MultiFocusK.tuple (and for apply over a Function1[Int, *]), Boolean, Unit, plus any singleton type via ValueOf. For every other index type the caller supplies one — either a local given (given RepresentativeIndex[Symbol] = RepresentativeIndex.at(Symbol("x"))) or the at smart constructor at the use site. MultiFocusK.representable pins its index to F.Representation, which has no canonical inhabitant — so a grate over one takes its witness from the caller, the same value the read side passes to .at(i) per call.
'''The shipped instances do privilege a value''' (0 for Int, false for Boolean) — the thing MultiFocusK.representable's doc rules out for a constructor index, and rightly: an index that lives on the optic is a claim about the optic. This one is not that. It is the position a read falls back to when nobody is there to name one, it keeps the bridge implicit (so iso.andThen(grate) stays import-free), and it is unobservable on every shipped path — which is why shipping it is a convenience rather than a semantics. The alternative the design note prices out is no shipped instances at all: three composition cells go red and every caller writes a given.
An '''uninhabited''' index type (Function1[Nothing, *]-indexed, a phantom slot) gets no instance, deliberately: there is no index to witness, so the bridge refuses rather than reading a bundle at a value that cannot exist.
Attributes
- Companion
- object
- Source
- RepresentativeIndex.scala
- Supertypes
-
class Objecttrait Matchableclass Any
Attributes
- Companion
- trait
- Source
- RepresentativeIndex.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
RepresentativeIndex.type
Types
Identity carrier: Direct[X, A] = A (opaque). X is phantom. Used by Iso and Getter where the focus is fully determined and no reassembly information is needed — the optic's to / from are plain functions, so it is the forgetful functor (it forgets the leftover X entirely). Its instances therefore live under the Forgetful* typeclasses (forgetful.ForgetfulFunctor, forgetful.ForgetfulTraverse, …). The F-shape sibling Forget lives in its own file.
Identity carrier: Direct[X, A] = A (opaque). X is phantom. Used by Iso and Getter where the focus is fully determined and no reassembly information is needed — the optic's to / from are plain functions, so it is the forgetful functor (it forgets the leftover X entirely). Its instances therefore live under the Forgetful* typeclasses (forgetful.ForgetfulFunctor, forgetful.ForgetfulTraverse, …). The F-shape sibling Forget lives in its own file.
opaque (not a transparent alias) so it is a distinct type for implicit search: object Direct is its companion, so Accessor[Direct], AssociativeFunctor[Direct, …], etc. resolve via companion scope with no import, and the compiler never dealiases Direct[X, A] to A (which would lose those givens). It still erases to A, so Direct.apply (wrap) / value (unwrap) are identity at runtime.
Attributes
- Source
- Direct.scala
Curried carrier view of ForgetK — the F[_, _] shape Optic expects.
Adapt a F[_] container to the two-parameter carrier shape by wrapping it under the phantom X. Equivalent to the classic Haskell newtype Forget r a b = Forget (a -> r) construction but applied to a type constructor F: Forget[F][X, A] = F[A], ignoring X completely.
Adapt a F[_] container to the two-parameter carrier shape by wrapping it under the phantom X. Equivalent to the classic Haskell newtype Forget r a b = Forget (a -> r) construction but applied to a type constructor F: Forget[F][X, A] = F[A], ignoring X completely.
Used by dev.constructive.eo.optics.Fold (read-only), its build-only dual dev.constructive.eo.optics.Unfold, and the multi-focus family (dev.constructive.eo.data.MultiFocus) as a uniform "F-shape carrier" whose optic-level capabilities scale with the typeclasses F itself admits.
Attributes
- Source
- Forget.scala
Extract the first element type of a Tuple2. Stays as an unreduced match type when T is not a Tuple2 — this is load bearing for Affine.assoc accepting unbounded existentials.
Extract the first element type of a Tuple2. Stays as an unreduced match type when T is not a Tuple2 — this is load bearing for Affine.assoc accepting unbounded existentials.
Attributes
- Source
- Affine.scala
Curried carrier view of MultiFocusK — the F[_, _] shape Optic expects.
Curried carrier view of MultiFocusK — the F[_, _] shape Optic expects.
Attributes
- Source
- MultiFocus.scala
Unified pair carrier for the algebraic-lens, kaleidoscope, and grate optic families — MultiFocus[F][X, A] = (X, Focus[F, A]): a structural leftover X paired with a focus half. One carrier serves all three families; only the choice of F differs (a container for Traversal, Function1-shaped for the grate encoding, …).
Unified pair carrier for the algebraic-lens, kaleidoscope, and grate optic families — MultiFocus[F][X, A] = (X, Focus[F, A]): a structural leftover X paired with a focus half. One carrier serves all three families; only the choice of F differs (a container for Traversal, Function1-shaped for the grate encoding, …).
The focus half is a sum, because two shapes of optic share this carrier and the difference is not derivable from F:
- MultiFocusK.Broadcast — an index-independent focus: one value
a, known without consulting an index (X0-shaped carriers), plus the same value presented as the carrier's ownF[A]. Written byforgetful2multifocusFunction1(the Iso shim), byMultiFocus.broadcast, and by every composition whose write collapsed to a single value. F[A]itself — a genuine tabulation / focus vector: reading a position means reading this. Written by every other factory (representable,tuple,apply, …).
Keeping the sum inside the carrier (rather than in an optic-level class) makes constancy a property of the data, so it survives map / collectWith / andThen — the kernels read it instead of inventing an index when a value is needed "at whatever position". MultiFocusK.foci stays total by carrying the lifted F[A] in the broadcast case, so every consumer that only wants "the focus vector" is unchanged.
The kaleidoscope aggregation ("summarise the foci, write the summary back") has two natural derivations, and the choice is structural — not derivable from a single typeclass:
- Functor-broadcast:
fa.map(_ => f(fa)). Length-preserving; default. NeedsFunctor[F]. - Applicative-broadcast:
F.pure(f(fa)). Singleton/cartesian. NeedsApplicative[F].
The default is the first; List users wanting the singleton collapse can compose _.headOption downstream or call collectList explicitly. See docs/research/2026-04-29-fixedtraversal-fold-spike.md for the grate-absorption justification.
Type parameters
- F
-
classifier shape — operation requirements:
.modify/.replaceneedFunctor[F]..foldMapneedsFoldable[F]..modifyA/.allneedTraverse[F]..collectMap/.collectWithneedFunctor[F];collectListis List-specific.- Same-carrier
.andThenneedsTraverse[F] + MultiFocusFromList[F]. fromPrismF/fromOptionalFneedMonoidK[F].
Attributes
- Source
- MultiFocus.scala