AvroTraversal

dev.constructive.eo.avro.AvroTraversal
See theAvroTraversal companion object
final class AvroTraversal[A] extends Traversal[Array[Byte], Array[Byte], A, A], Dynamic

Multi-focus counterpart to AvroPrism, carried on the BINARY WIRE FORM like the prism:

 AvroTraversal[A] <: dev.constructive.eo.optics.Traversal[Array[Byte], Array[Byte], A, A]
 type X = (Array[Byte], Option[ElementSpans])

to resolves the prefix to an array-typed field, walks the array's block framing, and locates + slice-decodes the focus inside every element (AvroBinaryCursor.locateElements); from re-encodes each focus and splices all spans back in one pass. Avro's standard (positive-count) array framing carries no byte lengths, so per-element splices can change size freely; payloads whose array used the negative-count (byte-sized) framing are RE-FRAMED on write — the whole array region is rebuilt as one canonical positive-count block with the focus splices applied (AvroBinaryCursor.reframeArray). Same carrier as dev.constructive.eo.jsoniter.JsoniterTraversal (MultiFocus[PSVec]), so .foldMap / .all / .modify / .headOption / same-carrier .andThen light up from the standard extension catalogue.

Read semantics are "fold the focuses that exist": elements whose focus walk or slice decode refuses keep their bytes but leave the focus set (untouched on write). Whole-walk failure (bad prefix / non-array terminal / truncated bytes) yields no foci and from returns the input — silent, like every write-side refusal here (diagnostics live on record).

AvroPrism's '''Laws & preconditions''' section applies verbatim: laws hold up to canonical re-encoding of the focused slices, writes need decodable current focuses, and the payload must be encoded under exactly this traversal's reader schema.

The record-carried face — the Ior-bearing modify / getAll / place / transfer surface over IndexedRecord | Array[Byte] | String input — lives behind ONE factory: record (AvroRecordTraversal). Drilling (.field / .at / .union[Branch] / .fields / Dynamic selection) stays here, the single mechanism; drill first, flip last.

The Fields-vs-Leaf split lives entirely inside focus (per AvroFocus).

Attributes

Companion
object
Source
AvroTraversal.scala
Graph
Supertypes
trait Dynamic
class Traversal[Array[Byte], Array[Byte], A, A]
trait Optic[Array[Byte], Array[Byte], A, A, MultiFocus[PSVec]]
class Object
trait Matchable
class Any
Show all

Members list

Grouped members

Operations

inline def cross[C, D](o: Optic[Array[Byte], Array[Byte], C, D, MultiFocus[PSVec]])(using Accessor[MultiFocus[PSVec]], ReverseAccessor[MultiFocus[PSVec]]): Optic[A, A, C, D, MultiFocus[PSVec]]

Build-then-observe across the build-output ⇄ read-input seam, preserving structure, on a shared carrier F. Flip self (it must be reversible — Accessor[F] and ReverseAccessor[F], i.e. an Iso or Review over Direct) so it reads T from B, then andThen that under the same carrier. The result is the full Optic[B, A, C, D, F], not a collapsed getter: its read capability follows the carrier (.get for Direct), and self's read focus A survives as the composite's write-back focus.

Build-then-observe across the build-output ⇄ read-input seam, preserving structure, on a shared carrier F. Flip self (it must be reversible — Accessor[F] and ReverseAccessor[F], i.e. an Iso or Review over Direct) so it reads T from B, then andThen that under the same carrier. The result is the full Optic[B, A, C, D, F], not a collapsed getter: its read capability follows the carrier (.get for Direct), and self's read focus A survives as the composite's write-back focus.

This is exactly self.reverse.andThen(that). The motivating case is ana.cross(cata): a Review (the unfold) crossed with a getter on the built S (the fold) — a (materializing) hylomorphism whose .get reads the folded value. When that sits on a different carrier (a Prism, a Fold, …), the cross-carrier cross overload below is selected instead.

Seam: that's source is self's T and its back-type is self's S.

Attributes

Inherited from:
Optic
Source
Optic.scala

Type members

Types

type X = (Array[Byte], Option[ElementSpans])

Structural leftover: the original payload plus the located element-span plan (None on a whole-walk failure, in which case from returns the input).

Structural leftover: the original payload plus the located element-span plan (None on a whole-walk failure, in which case from returns the input).

Attributes

Source
AvroTraversal.scala

Value members

Concrete methods

transparent inline def at(i: Int): Any
Extension method from AvroTraversal

.at(i) sugar — extend the suffix by an array index.

.at(i) sugar — extend the suffix by an array index.

Attributes

Source
AvroTraversal.scala
transparent inline def field[B](inline selector: A => B)(using codecB: AvroCodec[B]): AvroTraversal[B]
Extension method from AvroTraversal

.field(_.x) sugar — extend the suffix by a named field.

.field(_.x) sugar — extend the suffix by a named field.

Attributes

Source
AvroTraversal.scala
def fieldNamed[B](schemaName: String)(using codecB: AvroCodec[B]): AvroTraversal[B]
Extension method from AvroTraversal

.fieldNamed[B]("schema_name") — drill by EXPLICIT schema field name (issue #35 escape hatch), the traversal counterpart of AvroPrism.fieldNamed.

.fieldNamed[B]("schema_name") — drill by EXPLICIT schema field name (issue #35 escape hatch), the traversal counterpart of AvroPrism.fieldNamed.

Attributes

Source
AvroTraversal.scala
transparent inline def fields(inline selectors: A => Any*): Any
Extension method from AvroTraversal

.fields(_.a, _.b, ...) — focus a NamedTuple per element.

.fields(_.a, _.b, ...) — focus a NamedTuple per element.

Attributes

Source
AvroTraversal.scala
def from(mf: MultiFocus[PSVec][X, A]): Array[Byte]

Re-encode each focus and splice all spans back in one pass (re-framing the array region when the original framing was byte-sized); any refusal returns the input bytes.

Re-encode each focus and splice all spans back in one pass (re-framing the array region when the original framing was byte-sized); any refusal returns the input bytes.

Attributes

Source
AvroTraversal.scala

The IndexedRecord-carried counterpart of this traversal — the Ior-bearing diagnostic surface (modify / getAll / place / transfer + *Unsafe) over IndexedRecord | Array[Byte] | String input:

The IndexedRecord-carried counterpart of this traversal — the Ior-bearing diagnostic surface (modify / getAll / place / transfer + *Unsafe) over IndexedRecord | Array[Byte] | String input:

 codecPrism[Basket].items.each.name.record.modify(_.toUpperCase)(record)

Attributes

Source
AvroTraversal.scala
transparent inline def selectDynamic(inline name: String): Any

Dynamic field sugar — basket.items.each.name lowers to .field(_.name) via the macro. An Avro field named like a real member of this class must be drilled with the explicit .field(_.x) form.

Dynamic field sugar — basket.items.each.name lowers to .field(_.name) via the macro. An Avro field named like a real member of this class must be drilled with the explicit .field(_.x) form.

Attributes

Source
AvroTraversal.scala
def to(bytes: Array[Byte]): MultiFocus[PSVec][X, A]

Locate every element's focused span and slice-decode the focuses that exist; whole-walk failure yields no foci (and a None plan, so from passes the input through).

Locate every element's focused span and slice-decode the focuses that exist; whole-walk failure yields no foci (and a None plan, so from passes the input through).

Attributes

Source
AvroTraversal.scala
transparent inline def union[Branch]: Any
Extension method from AvroTraversal

.union[Branch] — narrow the per-element focus to one union alternative.

.union[Branch] — narrow the per-element focus to one union alternative.

Attributes

Source
AvroTraversal.scala

Inherited methods

def andThen[C, D, XI](inner: SplitCombineLens[A, A, C, D, XI]): Traversal[Array[Byte], Array[Byte], C, D]

Fused Traversal.andThen(Lens)SplitCombineLens / SimpleLens (macro-lens) inner.

Fused Traversal.andThen(Lens)SplitCombineLens / SimpleLens (macro-lens) inner.

Attributes

Inherited from:
Traversal
Source
Traversal.scala
def andThen[C, D](inner: GetReplaceLens[A, A, C, D]): Traversal[Array[Byte], Array[Byte], C, D]

Fused Traversal.andThen(Lens) — lifts the lens through the same tuple2multifocusPSVec bridge the Morph-routed generic extension uses, then composes on the shared carrier. See the andThen(inner: Traversal) overload for why the concrete return type matters.

Fused Traversal.andThen(Lens) — lifts the lens through the same tuple2multifocusPSVec bridge the Morph-routed generic extension uses, then composes on the shared carrier. See the andThen(inner: Traversal) overload for why the concrete return type matters.

Attributes

Inherited from:
Traversal
Source
Traversal.scala
def andThen[C, D](inner: Traversal[A, A, C, D]): Traversal[Array[Byte], Array[Byte], C, D]

Fused same-carrier Traversal.andThen(Traversal) — same composition as the generic Optic.andThen (the mfAssocPSVec kernel), re-homed under the concrete Traversal class via Traversal.composed. The generic member returns an anonymous Optic, so a composed chain's .modify / .foldMap resolve to the generic extensions — which re-instantiate the parameterized mfFunctor[PSVec] / mfFold[PSVec] givens per call (the measured 40 B/op of the class scaladoc) and share one megamorphic body. Returning Traversal keeps the fused inline members (and per-site monomorphic to / from dispatch) on every chain.

Fused same-carrier Traversal.andThen(Traversal) — same composition as the generic Optic.andThen (the mfAssocPSVec kernel), re-homed under the concrete Traversal class via Traversal.composed. The generic member returns an anonymous Optic, so a composed chain's .modify / .foldMap resolve to the generic extensions — which re-instantiate the parameterized mfFunctor[PSVec] / mfFold[PSVec] givens per call (the measured 40 B/op of the class scaladoc) and share one megamorphic body. Returning Traversal keeps the fused inline members (and per-site monomorphic to / from dispatch) on every chain.

Attributes

Inherited from:
Traversal
Source
Traversal.scala
def andThen[C, IB, G[_, _]](inner: Optic[A, Unit, C, IB, G])(using rc: ReadCompose[MultiFocus[PSVec], G]): rc.Out[Array[Byte], C]

ANY outer ∘ read-only inner — the inner is honestly one-way (T = Unit: a Getter, AffineFold, or Fold), so only the two READ sides matter and the composite collapses to the read-only join of their strengths via compose.ReadCompose (Getter / PickFold / ForgetFold).

ANY outer ∘ read-only inner — the inner is honestly one-way (T = Unit: a Getter, AffineFold, or Fold), so only the two READ sides matter and the composite collapses to the read-only join of their strengths via compose.ReadCompose (Getter / PickFold / ForgetFold).

A trait member (not an extension in the companion) deliberately: once a receiver is statically one of the fused concrete classes, its andThen member overloads enter resolution and Scala 3 never falls back to extension methods when they all fail — the collapse must be in the member overload set to be reachable without an expected-type ascription.

Only the inner's T is pinned to Unit; its B stays free (IB) even though read-only inners always have B = Unit. That keeps this overload strictly LESS specific than the same-carrier andThen above (which accepts every argument this one does whenever B = Unit at the receiver), so a same-carrier read-only ∘ read-only call resolves unambiguously to the AssociativeFunctor path and this one fires exactly on the cross-seam cells the generic member cannot type.

Attributes

Inherited from:
Optic
Source
Optic.scala
inline def andThen[C, D](o: Optic[A, A, C, D, MultiFocus[PSVec]]): Optic[Array[Byte], Array[Byte], C, D, MultiFocus[PSVec]]

Compose with another optic under the shared carrier F. Requires AssociativeFunctor[F]. Cross-carrier composition (Lens → Optional, Lens → Traversal, …) goes through the Morph-summoning overload of this same method.

Compose with another optic under the shared carrier F. Requires AssociativeFunctor[F]. Cross-carrier composition (Lens → Optional, Lens → Traversal, …) goes through the Morph-summoning overload of this same method.

Attributes

Example
case class Address(street: String)
case class Person(address: Address)
val streetLens = lens[Person](_.address).andThen(lens[Address](_.street))
Inherited from:
Optic
Source
Optic.scala
def exists(p: A => Boolean)(s: Array[Byte]): Boolean

True iff a focus satisfies p, via the streaming foldMap under the disjunction monoid — member twin of the generic Optic.exists.

True iff a focus satisfies p, via the streaming foldMap under the disjunction monoid — member twin of the generic Optic.exists.

Attributes

Inherited from:
Traversal
Source
Traversal.scala
def foldMap[M](f: A => M)(s: Array[Byte])(using M: Monoid[M]): M

Fused foldMap — the default folds the focus vector via the cached pSVecFoldable, skipping the per-call mfFold[PSVec] given instantiation the generic extension pays. Deliberately a plain overridable def, unlike modify: the constructors override it with STREAMING folds that never call to(s) at all — a fold has no use for the reassembly context to must pack, so materializing the focus vector is pure overhead on the read path. Traversal.pEach folds the container directly through Traverse[T], Traversal.selfChildren folds the children vector without the carrier wrapper, and Traversal.composed nests the two sides' folds so a depth-k chain streams with no arrays at any depth. The O(n) allocation win dwarfs the fixed per-call cost the old inline splice protected against.

Fused foldMap — the default folds the focus vector via the cached pSVecFoldable, skipping the per-call mfFold[PSVec] given instantiation the generic extension pays. Deliberately a plain overridable def, unlike modify: the constructors override it with STREAMING folds that never call to(s) at all — a fold has no use for the reassembly context to must pack, so materializing the focus vector is pure overhead on the read path. Traversal.pEach folds the container directly through Traverse[T], Traversal.selfChildren folds the children vector without the carrier wrapper, and Traversal.composed nests the two sides' folds so a depth-k chain streams with no arrays at any depth. The O(n) allocation win dwarfs the fixed per-call cost the old inline splice protected against.

Attributes

Inherited from:
Traversal
Source
Traversal.scala
def headOption(s: Array[Byte]): Option[A]

First focus, if any, via the streaming foldMap under a first-Some monoid — the member twin of the generic Optic.headOption extension, here so concrete Traversals skip the focus vector build (the extension routes through to(s)).

First focus, if any, via the streaming foldMap under a first-Some monoid — the member twin of the generic Optic.headOption extension, here so concrete Traversals skip the focus vector build (the extension routes through to(s)).

Attributes

Inherited from:
Traversal
Source
Traversal.scala
def length(s: Array[Byte]): Int

Focus count via the streaming foldMap — member twin of the generic Optic.length.

Focus count via the streaming foldMap — member twin of the generic Optic.length.

Attributes

Inherited from:
Traversal
Source
Traversal.scala
inline def modify(f: A => A): Array[Byte] => Array[Byte]

Fused modify — same logic as the generic extension, with the cached pSVecFunctor in place of the per-call mfFunctor[PSVec] given instantiation (−40 B/op with replace / foldMap's sibling savings; see the class scaladoc). inline so each call site splices its own copy — per-site monomorphic to / from dispatch.

Fused modify — same logic as the generic extension, with the cached pSVecFunctor in place of the per-call mfFunctor[PSVec] given instantiation (−40 B/op with replace / foldMap's sibling savings; see the class scaladoc). inline so each call site splices its own copy — per-site monomorphic to / from dispatch.

Attributes

Inherited from:
Traversal
Source
Traversal.scala
inline def replace(b: A): Array[Byte] => Array[Byte]

Fused replace — constant-function modify.

Fused replace — constant-function modify.

Attributes

Inherited from:
Traversal
Source
Traversal.scala