Concrete Optic subclass for the AffineFold family — the read-only 0-or-1-focus optic, Optic[S, Unit, A, Unit, Affine]. Both T and the write-focus B are Unit (honestly one-way, like Getter / Fold): there is no value to put back, so .modify / .replace don't apply. The surface is .getOption and .foldMap. Composes with Lens / Prism via the same Morph bridges full Optional uses (they key off the carrier, not the T slot).
"An AffineFold[S, A]" is prose shorthand — there is deliberately NO type alias (one existed and was removed): an alias could only name the generic Optic[S, Unit, A, Unit, Affine] — composed read-collapse results are generic, not PickFold — and ascribing that knocks a CONSTRUCTED fold off the hot, fused paths below (getOption / andThen here on the concrete class). Ascribe PickFold[S, A] when you hold a constructed one; spell the full Optic[…] only for composed results — or, per the doctrine, leave vals un-ascribed and demand CanGetOption[S, A] / CanFold[S, A] in consuming signatures.
A final class storing pick directly, NOT a shared anonymous wrapper: a single anon class would host one pick.apply bytecode site for every AffineFold instance, the megamorphic-dispatch trap PrintInlining exposed on the abstract-class Getter (the JIT profiles that one site over all instances' lambdas and gives up inlining). Same storage shape as PickMendPrism (pick) and ForgetFold (read). Specialised existential X = (Unit, Unit): Affine.Hit stores snd: Unit + b: A — one reference slot less than the (S, A) payload of a full Optional, since from throws its input away anyway. Returned by AffineFold.apply / AffineFold.select so hand-written affine folds pick up the fused members automatically.
Build-then-observe across the build-output ⇄ read-input seam, preserving structure, on a shared carrierF. Flip self (it must be reversible — Accessor[F]andReverseAccessor[F], i.e. an Iso or Review over Direct) so it reads T from B, then andThenthat 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 carrierF. Flip self (it must be reversible — Accessor[F]andReverseAccessor[F], i.e. an Iso or Review over Direct) so it reads T from B, then andThenthat 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.
Existential leftover carried alongside the focus — the type-level witness the carrier uses to rebuild T. Concrete at construction (Lens.apply sets X = S, Prism.apply sets X = S, …) and abstract when the optic is bound to Optic[…, F] without refinement.
Existential leftover carried alongside the focus — the type-level witness the carrier uses to rebuild T. Concrete at construction (Lens.apply sets X = S, Prism.apply sets X = S, …) and abstract when the optic is bound to Optic[…, F] without refinement.
Fused AffineFold.andThen(AffineFold) — flatMaps the picks; no Affine wrappers, no Morph. inline so each compose site splices its own lambda (per-site monomorphic dispatch — see Getter.andThen).
Fused AffineFold.andThen(AffineFold) — flatMaps the picks; no Affine wrappers, no Morph. inline so each compose site splices its own lambda (per-site monomorphic dispatch — see Getter.andThen).
Read-only outer ∘ ANY inner — a PickFold only reads (partially), so the inner's write side is irrelevant and the composite is the read-only join via compose.ReadCompose (PickFold unless the inner has many foci, then ForgetFold). Covers the writable inners (affineFold ∘ lens / prism / optional / traversal); the fused andThen(Getter) / andThen(PickFold) members above stay as the more-specific fast paths.
Read-only outer ∘ ANY inner — a PickFold only reads (partially), so the inner's write side is irrelevant and the composite is the read-only join via compose.ReadCompose (PickFold unless the inner has many foci, then ForgetFold). Covers the writable inners (affineFold ∘ lens / prism / optional / traversal); the fused andThen(Getter) / andThen(PickFold) members above stay as the more-specific fast paths.
Override of the trait's read-only-inner overload, re-homed here so it shares an owner with andThenReadAny above — see Getter's twin override for the ambiguity rationale.
Override of the trait's read-only-inner overload, re-homed here so it shares an owner with andThenReadAny above — see Getter's twin override for the ambiguity rationale.
Fused getOption — reads pick directly, skipping the Affine carrier round-trip the generic extension performs. Wins over the extension by member precedence at static type.
Fused getOption — reads pick directly, skipping the Affine carrier round-trip the generic extension performs. Wins over the extension by member precedence at static type.
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))