dev.constructive.eo

Existential Optics for Scala 3 — the capability layer.

This root package hosts the '''capability traits''' (CanGet, CanGetOption, CanModify, CanFold, CanPut, CanPlace, CanReverseGet, CanTransform, CanModifyA, CanModifyF) that consuming code demands instead of concrete optic types: a method that reads takes a CanGet[S, A], never a Lens. Concrete optics (optics) are for construction and composition; capabilities are for use — one optic given per (S, A) pair keeps summoning unambiguous. recast re-brands an optic's phantom slots where soundness allows.

'''Where things live.''' This core module is deliberately macro-free: auto-derivation ships in the SEPARATE cats-eo-generics artifact (dev.constructive.eo.generics.{lens, prism, plate}) — do not look for a GenLens / Focus equivalent here. Coming from Monocle: Setter is optics.Modify (set is replace), Iso / Lens / Prism / Optional / Traversal / Getter / Fold keep their names, and Each / Index / At exist as plain constructor OBJECTS, not a typeclass zoo — optics.Each is optics.Traversal.each under the searched-for name, optics.Index focuses one positional/keyed slot (never inserts), and optics.At is the total Map lens to Option[V] (Some upserts, None deletes). They return ordinary optics; nothing to instance or derive. All composition, same-carrier or cross-family, is the one .andThen.

Attributes

Members list

Packages

Gate typeclasses keyed by carrier: Accessor (total read), PartialAccessor (read that may miss), ReverseAccessor (build back from the write side). Signatures that take an Optic[…, F] plus one of these on F must list the optic '''first''' in the same using clause — left-to-right resolution pins F before the gate is searched.

Gate typeclasses keyed by carrier: Accessor (total read), PartialAccessor (read that may miss), ReverseAccessor (build back from the write side). Signatures that take an Optic[…, F] plus one of these on F must list the optic '''first''' in the same using clause — left-to-right resolution pins F before the gate is searched.

Attributes

Cross-representation optics bridging native Scala types and their Apache Avro on-the-wire form.

Cross-representation optics bridging native Scala types and their Apache Avro on-the-wire form.

The entry point is AvroPrism.codecPrism (read-aloud API parallel to dev.constructive.eo.circe.codecPrism):

 import dev.constructive.eo.avro.{AvroCodec, codecPrism}
 import hearth.kindlings.avroderivation.AvroEncoder.derived
 import hearth.kindlings.avroderivation.AvroDecoder.derived
 import hearth.kindlings.avroderivation.AvroSchemaFor.derived

 case class Person(name: String, age: Int)
 // `given AvroCodec[Person]` falls out automatically from kindlings'
 // derived `AvroEncoder[Person]`, `AvroDecoder[Person]`, `AvroSchemaFor[Person]`.

 val personPrism: AvroPrism[Person]   = codecPrism[Person]
 val namePrism:   AvroPrism[String]   = personPrism.field(_.name)
 namePrism.modify(_.toUpperCase)(payloadBytes)
 // → the same Array[Byte] payload, with `name` upper-cased in
 //   place — no Person (nor any root record) ever materialised.
 namePrism.record.modify(_.toUpperCase)(record)
 // → the IndexedRecord-carried face, with Ior diagnostics.

The default carrier is the binary wire form itself (Array[Byte], mirroring dev.constructive.eo.jsoniter.JsoniterPrism); the record-carried face behind .record follows dev.constructive.eo.circe's architecture decisions on Json: IndexedRecord plays the role of Json, AvroCodec plays the role of (io.circe.Encoder[A], io.circe.Decoder[A]), and AvroFailure plays the role of JsonFailure.

'''Carrier note.''' Both AvroRecordPrism and eo-circe's JsonPrism are Affine-carried — lawful Optionals whose composed / upcast writes preserve siblings. A drilled focus is an Optional, so the carrier widens to Affine (via the Composer on composition) rather than pretending to be a Prism; the two record faces stay in step.

'''Deliberate duplication.''' PathStep is duplicated, not shared with eo-circe — the UnionBranch case is Avro-only and forcing it into eo-circe would pollute that module. See PathStep's class doc.

Attributes

Cross-representation optics bridging native Scala types and their circe-serialised form.

Cross-representation optics bridging native Scala types and their circe-serialised form.

The entry point is JsonPrism.apply (aliased as codecPrism for the read-aloud API):

 val personPrism: JsonPrism[Person] = codecPrism[Person]
 val streetPrism: JsonPrism[String] =
   personPrism.field(_.address).field(_.street)
 streetPrism.modify(_.toUpperCase)(personJson)
 // → Ior.Right of the same Json, with .address.street upper-cased —
 //   no Person ever materialised.

From a root prism the whole surface lights up: .field (and its Dynamic sugar) drills deeper, .at(i) indexes into an array, .each widens to a JsonTraversal over every element, .fields(_.a, _.b) focuses a bundle of case fields as a Scala 3 NamedTuple. Independent of the path-walkers, platedJson makes Json a recursive self-traversal for whole-document rewrites.

Every operation comes in two tiers: the default methods return Ior[Chain[JsonFailure], _]Ior.Both is partial success, every skip documented in the chain — and the *Unsafe siblings are the silent pass-through hot path.

Coming from circe-optics JsonPath or raw cursors: this is the compile-time-checked equivalent — codecPrism[Person].address.streetroot.address.street.string ≈ an hcursor.downField chain, with typed per-step codecs instead of stringly paths.

Attributes

The composition seam: AssociativeFunctor powers the generic Optic.andThen (threading an inner carrier through an outer one), Composer names the carrier-pair-specific fusions, Morph re-expresses an optic over a different carrier so cross-carrier andThen can pick a direction, and ReadCompose routes read-only collapses (getter ∘ getter and friends) without building intermediate structure.

The composition seam: AssociativeFunctor powers the generic Optic.andThen (threading an inner carrier through an outer one), Composer names the carrier-pair-specific fusions, Morph re-expresses an optic over a different carrier so cross-carrier andThen can pick a direction, and ReadCompose routes read-only collapses (getter ∘ getter and friends) without building intermediate structure.

Attributes

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.

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

Weakened functor hierarchy for carriers that cannot honour full map: ForgetfulFunctor, ForgetfulFold, ForgetfulApplicative and ForgetfulTraverse let read-only carriers (Forget, Direct-collapsed reads) participate in the generic composition machinery by "mapping" in ways that may legally discard or refuse the function.

Weakened functor hierarchy for carriers that cannot honour full map: ForgetfulFunctor, ForgetfulFold, ForgetfulApplicative and ForgetfulTraverse let read-only carriers (Forget, Direct-collapsed reads) participate in the generic composition machinery by "mapping" in ways that may legally discard or refuse the function.

Attributes

Auto-derivation entry points for EO optics — Scala 3 quoted macros for Lens / Prism / Plated. Scaffolds on Mateusz Kubuszok's hearth macro-commons library.

Auto-derivation entry points for EO optics — Scala 3 quoted macros for Lens / Prism / Plated. Scaffolds on Mateusz Kubuszok's hearth macro-commons library.

Derived setters call the primary constructor directly (new S(...), never .copy) — uniform across case classes and enum cases, but the emitted call carries no outer accessor, so derive only for '''top-level''' ADTs, not types nested inside a class.

Attributes

Byte-level JSON optics over jsoniter-scala: JsoniterPrism and JsoniterTraversal focus a JSONPath '''directly in the encoded byte buffer''' — scanning for the span, decoding only the focused slice, and splicing writes back — so reads and writes never materialise the full document AST.

Byte-level JSON optics over jsoniter-scala: JsoniterPrism and JsoniterTraversal focus a JSONPath '''directly in the encoded byte buffer''' — scanning for the span, decoding only the focused slice, and splicing writes back — so reads and writes never materialise the full document AST.

The typed entry point is JsoniterPrism[A] — the root Prism[Array[Byte], A]:

 val streetP: JsoniterPrism[String] =
   JsoniterPrism[Person].field(_.address).field(_.street)
 streetP.modify(_.toUpperCase)(personBytes)
 // → Array[Byte] with .address.street upper-cased —
 //   no Person ever materialised, only the String leaf decoded.

Attributes

Law definitions for every optic family — one trait per shape (LensLaws, PrismLaws, IsoLaws, OptionalLaws, TraversalLaws, FoldLaws, GetterLaws, ModifyLaws, MultiFocusLaws, PlatedLaws, UnfoldLaws, SeamLaws, …), each method one equation. Reusable by downstream projects; the ScalaCheck/specs2 bundles live in laws.discipline, with eo-internal seam laws under laws.eo, carrier laws under laws.data and typeclass laws under laws.typeclass.

Law definitions for every optic family — one trait per shape (LensLaws, PrismLaws, IsoLaws, OptionalLaws, TraversalLaws, FoldLaws, GetterLaws, ModifyLaws, MultiFocusLaws, PlatedLaws, UnfoldLaws, SeamLaws, …), each method one equation. Reusable by downstream projects; the ScalaCheck/specs2 bundles live in laws.discipline, with eo-internal seam laws under laws.eo, carrier laws under laws.data and typeclass laws under laws.typeclass.

This module ships law equations and rule-sets ONLY — no Arbitrary, Cogen, or Eq instances. Your suite supplies the generators for its own S / A types (and, for SeamLaws, the structural equality — see below).

'''Codec-backed optics''' (Avro records, serialised JSON, …) are lawful only ''up to canonical re-encoding'': the family laws compare with raw == (mirroring Monocle), which a decode-then-re-encode round-trip can fail for representation-sensitive equality (e.g. Avro's schema-instance-sensitive GenericData.Record.equals) even when the values are structurally equal. Run those laws on canonical fixtures — sources round-tripped through the codec once — or on a full-cover face whose rebuild goes through the same codec instance; SeamLaws instead takes an INJECTED equality for exactly this reason. AvroPrismLawsSpec in the avro module is the worked reference.

Attributes

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.

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

Recursion schemes as composable optics: Schemes.cata (fold), Schemes.ana (unfold) and Schemes.hylo (refold) over any type with a Plated instance — stack-safe, expressed as eo optics so they cross-compose with the rest of the library (ana(…).cross(cata(…)) '''is''' hylo).

Recursion schemes as composable optics: Schemes.cata (fold), Schemes.ana (unfold) and Schemes.hylo (refold) over any type with a Plated instance — stack-safe, expressed as eo optics so they cross-compose with the rest of the library (ana(…).cross(cata(…)) '''is''' hylo).

Attributes

Type members

Classlikes

object CanFold

Attributes

Companion
trait
Source
CanFold.scala
Supertypes
class Object
trait Matchable
class Any
Self type
CanFold.type
trait CanFold[S, A]

Capability: the foci A visible in an S can be folded through a Monoid — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulFold (every readable family; the primary surface of Fold and Traversal).

Capability: the foci A visible in an S can be folded through a Monoid — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulFold (every readable family; the primary surface of Fold and Traversal).

foldMap is the kernel; headOption / length / exists / foci ride on it. foci is the carrier-free counterpart of the raw-optic all extension (which returns foci still inside their carrier, List[F[X, A]]). Prefer this trait in consuming signatures; see CanGet for the doctrine and the coherence rule.

Attributes

Companion
object
Source
CanFold.scala
Supertypes
class Object
trait Matchable
class Any
Known subtypes
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 Optional[S, T, A, B]
class PickFold[S, A]
class PickMendPrism[S, A, B]
class SplitCombineLens[S, T, A, B, XA]
class SimpleLens[S, A, XA]
Show all
object CanGet

Attributes

Companion
trait
Source
CanGet.scala
Supertypes
class Object
trait Matchable
class Any
Self type
CanGet.type
trait CanGet[S, A]

Capability: an A can be read out of an S — the carrier-free surface of any optic whose carrier admits accessor.Accessor (Lens, Iso, Getter).

Capability: an A can be read out of an S — the carrier-free surface of any optic whose carrier admits accessor.Accessor (Lens, Iso, Getter).

This is the type to name in a consuming signature: leave the subject generic and demand only the evidence the method needs — def render[T](t: T)(using id: CanGet[T, OrderId]). Concrete optic types belong where optics are constructed and composed. One optic given per (S, A) pair should be in scope at a call site — capabilities follow ordinary typeclass coherence; newtype same-typed foci apart rather than relying on implicit priority to pick between them.

Attributes

Companion
object
Source
CanGet.scala
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class BijectionIso[S, T, A, B]
class GetReplaceLens[S, T, A, B]
class Getter[S, A]
class SplitCombineLens[S, T, A, B, XA]
class SimpleLens[S, A, XA]
trait CanGetOption[S, A]

Capability: an A may be read out of an S — the carrier-free surface of any optic whose carrier admits accessor.PartialAccessor (Prism, Optional, AffineFold).

Capability: an A may be read out of an S — the carrier-free surface of any optic whose carrier admits accessor.PartialAccessor (Prism, Optional, AffineFold).

Prefer this trait in consuming signatures; see CanGet for the doctrine and the one-optic- given-per-(S, A) coherence rule.

Attributes

Companion
object
Source
CanGetOption.scala
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class MendTearPrism[S, T, A, B]
class Optional[S, T, A, B]
class PickFold[S, A]
class PickMendPrism[S, A, B]
object CanGetOption

Attributes

Companion
trait
Source
CanGetOption.scala
Supertypes
class Object
trait Matchable
class Any
Self type
trait CanModifyAP[S, T, A, B]

Capability: the foci A inside an S can be rewritten effectfully under any Applicative[G] — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulTraverse[F, Applicative] (Lens, Prism, Optional, Traversal, Fold).

Capability: the foci A inside an S can be rewritten effectfully under any Applicative[G] — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulTraverse[F, Applicative] (Lens, Prism, Optional, Traversal, Fold).

Prefer this trait in consuming signatures; the monomorphic CanModifyA alias covers the common case. For collecting the foci without effects, CanFold.foci is the carrier-free counterpart of the raw-optic all extension. See CanGet for the doctrine and the coherence rule.

Attributes

Companion
object
Source
CanModifyA.scala
Supertypes
class Object
trait Matchable
class Any
object CanModifyAP

Attributes

Companion
trait
Source
CanModifyA.scala
Supertypes
class Object
trait Matchable
class Any
Self type
object CanModifyFP

Attributes

Companion
trait
Source
CanModifyF.scala
Supertypes
class Object
trait Matchable
class Any
Self type
trait CanModifyFP[S, T, A, B]

Capability: the focused A inside an S can be rewritten effectfully under any Functor[G] — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulTraverse[F, Functor] (today: Lens).

Capability: the focused A inside an S can be rewritten effectfully under any Functor[G] — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulTraverse[F, Functor] (today: Lens).

Prefer this trait in consuming signatures; the monomorphic CanModifyF alias covers the common case. See CanGet for the doctrine and the coherence rule.

Attributes

Companion
object
Source
CanModifyF.scala
Supertypes
class Object
trait Matchable
class Any
object CanModifyP

Attributes

Companion
trait
Source
CanModify.scala
Supertypes
class Object
trait Matchable
class Any
Self type
CanModifyP.type
trait CanModifyP[S, T, A, B]

Capability: the focused A inside an S can be rewritten to a B, producing a T — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulFunctor (every writable family: Lens, Iso, Prism, Optional, Traversal, Modify).

Capability: the focused A inside an S can be rewritten to a B, producing a T — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulFunctor (every writable family: Lens, Iso, Prism, Optional, Traversal, Modify).

Prefer this trait in consuming signatures; the monomorphic CanModify alias covers the common S = T, A = B case. A read-then-write method should demand ONE CanModify (whose modify observes and rewrites in a single pass) rather than split CanGet + CanModify evidence — nothing ties two separately-summoned capabilities to the same optic. See CanGet for the doctrine and the coherence rule.

Attributes

Companion
object
Source
CanModify.scala
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class BijectionIso[S, T, A, B]
class GetReplaceLens[S, T, A, B]
class MendTearPrism[S, T, A, B]
class Modify[S, T, A, B]
class Optional[S, T, A, B]
class PickMendPrism[S, A, B]
class SplitCombineLens[S, T, A, B, XA]
class SimpleLens[S, A, XA]
Show all
trait CanPlace[T, B]

Capability: a B can be written into an already-built T — the carrier-free surface of the place / transfer extensions.

Capability: a B can be written into an already-built T — the carrier-free surface of the place / transfer extensions.

Unlike its siblings this capability has NO derived given: the extensions additionally require T => F[X, B] evidence over the optic's existential X, which a generic optic given does not refine. Construct it explicitly at the seam instead — CanPlace.from(myLens) — and pass it along. See CanGet for the doctrine and the coherence rule.

Attributes

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

Attributes

Companion
trait
Source
CanPlace.scala
Supertypes
class Object
trait Matchable
class Any
Self type
CanPlace.type
object CanPutP

Attributes

Companion
trait
Source
CanPut.scala
Supertypes
class Object
trait Matchable
class Any
Self type
CanPutP.type
trait CanPutP[T, A, B]

Capability: a T can be constructed directly from an A, running f: A => B at the focus — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulApplicative (today: the Direct-carrier families, and Folds over an Applicative).

Capability: a T can be constructed directly from an A, running f: A => B at the focus — the carrier-free surface of any optic whose carrier admits forgetful.ForgetfulApplicative (today: the Direct-carrier families, and Folds over an Applicative).

Prefer this trait in consuming signatures; the monomorphic CanPut alias covers A = B. See CanGet for the doctrine and the coherence rule.

Attributes

Companion
object
Source
CanPut.scala
Supertypes
class Object
trait Matchable
class Any
trait CanReverseGet[T, B]

Capability: a T can be built from a B — the carrier-free surface of any optic whose carrier admits accessor.ReverseAccessor (Iso, Prism, Review).

Capability: a T can be built from a B — the carrier-free surface of any optic whose carrier admits accessor.ReverseAccessor (Iso, Prism, Review).

Prefer this trait in consuming signatures; see CanGet for the doctrine and the coherence rule.

Attributes

Companion
object
Source
CanReverseGet.scala
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class BijectionIso[S, T, A, B]
class MendTearPrism[S, T, A, B]
class PickMendPrism[S, A, B]
class Review[T, B]
object CanReverseGet

Attributes

Companion
trait
Source
CanReverseGet.scala
Supertypes
class Object
trait Matchable
class Any
Self type
object CanTransform

Attributes

Companion
trait
Source
CanTransform.scala
Supertypes
class Object
trait Matchable
class Any
Self type
trait CanTransform[T, D, B]

Capability: a D at the focus of an already-built T can be transformed via D => B — the carrier-free surface of the transform extension (the generalised CanPlace).

Capability: a D at the focus of an already-built T can be transformed via D => B — the carrier-free surface of the transform extension (the generalised CanPlace).

Like CanPlace this capability has NO derived given — the extension requires T => F[X, D] evidence over the optic's existential X. Construct it explicitly: CanTransform.from(myLens). See CanGet for the doctrine and the coherence rule.

Attributes

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

Types

type CanModify[S, A] = CanModifyP[S, S, A, A]

Monomorphic CanModifyP (S = T, A = B) — the shape most consuming signatures want: def adjustTimes[T](using cm: CanModify[T, DateTime]): T => T = cm.modify(adjustTime).

Monomorphic CanModifyP (S = T, A = B) — the shape most consuming signatures want: def adjustTimes[T](using cm: CanModify[T, DateTime]): T => T = cm.modify(adjustTime).

Attributes

Source
CanModify.scala
type CanModifyA[S, A] = CanModifyAP[S, S, A, A]

Monomorphic CanModifyAP (S = T, A = B).

Monomorphic CanModifyAP (S = T, A = B).

Attributes

Source
CanModifyA.scala
type CanModifyF[S, A] = CanModifyFP[S, S, A, A]

Monomorphic CanModifyFP (S = T, A = B).

Monomorphic CanModifyFP (S = T, A = B).

Attributes

Source
CanModifyF.scala
type CanPut[T, A] = CanPutP[T, A, A]

Monomorphic CanPutP (A = B).

Monomorphic CanPutP (A = B).

Attributes

Source
CanPut.scala