AvroRecordPrism

dev.constructive.eo.avro.AvroRecordPrism
final class AvroRecordPrism[A] extends Optic[IndexedRecord, IndexedRecord, A, A, Affine]

The org.apache.avro.generic.IndexedRecord-carried face of an AvroPrism — reachable ONLY through AvroPrism.record, never constructed directly:

 AvroRecordPrism[A]  IndexedRecord)  // Fst = source (miss); Snd = single-walk writer

'''An Optional, not a Prism.''' A drilled record focus lives INSIDE a record, so rebuilding the record needs the siblings — which a Prism's from(reverseGet) cannot see (it gets the focus alone). Carrying the source on the Affine seam fixes that at the carrier level: to captures a writer over the walk it already did, and from(Hit) applies it — so .modify / .replace preserve siblings whether called directly, upcast to Optic[…, Affine], or composed via andThen (the composite threads the writer end-to-end). Same carrier and law story as the byte face; only the root full-cover prism is also a lawful Prism, and reverseGet stays for it.

Two call-surface tiers (via AvroOpticOps):

  • Default Ior-bearing: modify / get etc. accumulate Chain[AvroFailure] on failure; partial success surfaces as Ior.Both(chain, inputRecord).
  • *Unsafe: silent pass-through hot path.

Triple-input shape IndexedRecord | Array[Byte] | String (where String is the Avro JSON wire format), decoding byte / JSON input under the cached root schema.

Drilling (.field / .fields / .union / .at / .each / Dynamic selection) lives on AvroPrism alone — drill there, then flip with .record at the end. One drilling mechanism, two carriers.

Attributes

Source
AvroRecordPrism.scala
Graph
Supertypes
trait Optic[IndexedRecord, IndexedRecord, A, A, Affine]
class Object
trait Matchable
class Any

Members list

Grouped members

Operations

inline def cross[C, D](o: Optic[IndexedRecord, IndexedRecord, C, D, Affine])(using Accessor[Affine], ReverseAccessor[Affine]): Optic[A, A, C, D, Affine]

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 = (IndexedRecord, A => IndexedRecord)

Structural leftover: Fst[X] is the source record (Miss pass-through), Snd[X] the single-walk writer captured by to.

Structural leftover: Fst[X] is the source record (Miss pass-through), Snd[X] the single-walk writer captured by to.

Attributes

Source
AvroRecordPrism.scala

Value members

Concrete methods

def from(aff: Affine[X, A]): IndexedRecord

Apply the captured writer to the (possibly modified) focus — sibling-preserving; a Miss passes the source record through unchanged.

Apply the captured writer to the (possibly modified) focus — sibling-preserving; a Miss passes the source record through unchanged.

Attributes

Source
AvroRecordPrism.scala
def get(input: IndexedRecord | Array[Byte] | String): Ior[Chain[AvroFailure], A]

Decode the focused value, threading parse failures (for Array[Byte] input) and walk failures through the Ior channel.

Decode the focused value, threading parse failures (for Array[Byte] input) and walk failures through the Ior channel.

Attributes

Source
AvroRecordPrism.scala
inline def getOptionUnsafe(input: IndexedRecord | Array[Byte] | String): Option[A]

Silent counterpart to getNone on any failure.

Silent counterpart to getNone on any failure.

Attributes

Source
AvroRecordPrism.scala
def reverseGet(a: A): IndexedRecord

Encode a standalone, returning the codec's IndexedRecord payload (or a synthesised empty record when the encoded value isn't record-shaped). Lawful only for the ROOT full-cover prism (a real Prism.reverseGet); on a drilled prism it cannot restore siblings, which is exactly why the Optic seam carries the source instead of calling this. Kept for root full-cover users and the reverseGet round-trip law checks.

Encode a standalone, returning the codec's IndexedRecord payload (or a synthesised empty record when the encoded value isn't record-shaped). Lawful only for the ROOT full-cover prism (a real Prism.reverseGet); on a drilled prism it cannot restore siblings, which is exactly why the Optic seam carries the source instead of calling this. Kept for root full-cover users and the reverseGet round-trip law checks.

Attributes

Source
AvroRecordPrism.scala
def to(record: IndexedRecord): Affine[X, A]

Navigate + decode the focus in one walk; a Hit carries the writer that walk captured.

Navigate + decode the focus in one walk; a Hit carries the writer that walk captured.

Attributes

Source
AvroRecordPrism.scala

Inherited methods

def andThen[C, IB, G[_, _]](inner: Optic[A, Unit, C, IB, G])(using rc: ReadCompose[Affine, G]): rc.Out[IndexedRecord, 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, Affine]): Optic[IndexedRecord, IndexedRecord, C, D, Affine]

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 modify(f: A => A): (IndexedRecord | Array[Byte] | String) => Ior[Chain[AvroFailure], IndexedRecord]

Apply f to the focused value; returns the modified record. Failures (path miss, decode failure, type mismatch) accumulate into Chain[AvroFailure]; partial success returns Ior.Both(chain, inputRecord) (the modify-family preserves the input as the silent fallback).

Apply f to the focused value; returns the modified record. Failures (path miss, decode failure, type mismatch) accumulate into Chain[AvroFailure]; partial success returns Ior.Both(chain, inputRecord) (the modify-family preserves the input as the silent fallback).

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def modifyUnsafe(f: A => A): (IndexedRecord | Array[Byte] | String) => IndexedRecord

Silent counterpart to modify — input pass-through on any failure.

Silent counterpart to modify — input pass-through on any failure.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def place(a: A): (IndexedRecord | Array[Byte] | String) => Ior[Chain[AvroFailure], IndexedRecord]

Replace the focused value with a. Same failure surface as modify.

Replace the focused value with a. Same failure surface as modify.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def placeUnsafe(a: A): (IndexedRecord | Array[Byte] | String) => IndexedRecord

Silent counterpart to place.

Silent counterpart to place.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def replace(a: A): (IndexedRecord | Array[Byte] | String) => IndexedRecord

Replace the focused value with a, rebuilding the record around it — silent tier (pair with place for Ior diagnostics).

Replace the focused value with a, rebuilding the record around it — silent tier (pair with place for Ior diagnostics).

A trait MEMBER deliberately: without it, .replace on a record-carried optic resolves to the generic Either-carrier extension, which routes through from(Right(a)) = reverseGet(a) and reconstructs the focus STANDALONE — for any drilled optic that silently destroys every sibling field. This member shadows the extension with the sibling-preserving placeImpl walk.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def transfer[C](f: C => A): (IndexedRecord | Array[Byte] | String) => C => Ior[Chain[AvroFailure], IndexedRecord]

Lift a C => A into a focus-replacer: transfer(f)(record)(c) decodes c to A via f and writes it at the focused position. Same failure surface as modify.

Lift a C => A into a focus-replacer: transfer(f)(record)(c) decodes c to A via f and writes it at the focused position. Same failure surface as modify.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def transferUnsafe[C](f: C => A): (IndexedRecord | Array[Byte] | String) => C => IndexedRecord

Silent counterpart to transfer.

Silent counterpart to transfer.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def transform(f: IndexedRecord => IndexedRecord): (IndexedRecord | Array[Byte] | String) => Ior[Chain[AvroFailure], IndexedRecord]

Apply f to the raw IndexedRecord at the focused position (no decode / encode through the codec layer). Useful for carrier-aware rewrites that don't want to round-trip through the focus type.

Apply f to the raw IndexedRecord at the focused position (no decode / encode through the codec layer). Useful for carrier-aware rewrites that don't want to round-trip through the focus type.

Note: the focused cursor's value is what gets passed through — for a leaf focus that may be a primitive (Long, Utf8, …), so the user's f: IndexedRecord => IndexedRecord is a slight type misfit at non-record-typed leaves.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala
def transformUnsafe(f: IndexedRecord => IndexedRecord): (IndexedRecord | Array[Byte] | String) => IndexedRecord

Silent counterpart to transform.

Silent counterpart to transform.

Attributes

Inherited from:
AvroOpticOps (hidden)
Source
AvroOpticOps.scala