AvroTraversal

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

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

 AvroTraversal[A] <: Optic[Array[Byte], Array[Byte], A, A, MultiFocus[PSVec]]
 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); the compatibility alias AvroFieldsTraversal points back here.

Attributes

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

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, 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