Skip to content

feat(schemes): typed recursion schemes as composable optics — the zoo (para/apo/histo/futu, zygo/mutu, fused refolds, M drivers), Graft[Affine] build seam, fused cross - #24

Open
kryptt wants to merge 61 commits into
mainfrom
feat/typed-recursion-schemes
Open

kryptt wants to merge 61 commits into
mainfrom
feat/typed-recursion-schemes

Conversation

@kryptt

@kryptt kryptt commented Jun 9, 2026 •

Copy link
Copy Markdown
Contributor

Typed recursion schemes as composable optics — the zoo, the fusion, the M path

Thesis

A recursion scheme is an Optic over the Direct carrier whose existential X is the index of the recursion — what the scheme retains — and the (co)free (co)monads are the universal indices: cata's X = Nothing (the forgetful fold), para's F[(S, A)] (the store-comonad complement — which makes para a lawful Lens: paraLens), histo's Attr = νX. A × F[X] (the cofree index), apo's Either[S, A] (the Prism residual worn build-side), futu's Coattr = μX. A + F[X] (the free index). Deforestation is choosing the forgetful index: the fused hylo and the materialising ana.cross(cata) are the same optic at two X-resolutions.

The categorical ground: lens = store-comonad coalgebra and biplate = Cartesian-store coalgebra (O'Connor, Functor is to Lens as Applicative is to Biplate, arXiv:1103.2841); the zoo's unification via comonadic/adjoint folds (Uustalu–Vene–Pardo 2001; Hinze–Wu–Gibbons, ICFP 2013). Full bibliography: docs/research/2026-06-15-typed-schemes-bibliography.md.

What ships

  • cats-eo-schemes (new artifact): cata/ana/hylo over a user-supplied pattern functor F[_] (Traverse[F]) + hand-written Basis (Project/Embed), and the zoo — para / apo / histo / futu / zygo / mutu / cozygo / comutu, fused dyna / codyna / chrono / elgot / coelgot, meta / metaChrono, prepro / postpro — plus the effectful *M family on Monad[M].tailRecM (single-pass linear-M contract) and paraLens (the paramorphism as a lawful Lens with a caller-supplied coherent put).
  • Fusion by composition: ana.cross(cata) (pure) fuses into Hylo — no intermediate S — because the citizens keep their (co)algebra; hyloM is the fused M spelling. FusionSpec pins the hylo law and the no-intermediate-S witness.
  • The decoration carrier is Affine worn on the build seam (Miss = finished slot, Hit = keep going) with the new Graft[Affine] instance (done/step) — the injection vocabulary the build-side citizens construct and consume. An earlier draft shipped a separate BiAffine carrier with an identical shape; it was dropped pre-merge (review: the arms are isomorphic and the duplication bought nothing).
  • One stack-safe engine (Machines.foldLayered): < 512-deep on-stack fast path, heap ArrayDeque past it, stack-safe to 10⁶ (tested per driver).
  • The earlier untyped Plated/PSVec path is removed — the typed path subsumes it (erased positional indexing made algebra arity slips a runtime error).
  • Getter.andThen C8 fix: a Direct-carried read-only citizen previously tied three overloads (E051) and getter.andThen(cata) did not compile; a strictly-most-specific Getter member (parameter type Optic[A, Unit, C, Unit, Direct], DummyImplicit-shaped) now resolves it to the concrete Getter. Pinned by GetterAndThenResolutionSpec; the full CompositionMatrixSpec still passes.

Benchmark deltas vs droste (B/op, 8 191-node tree, CI sweep)

  • cata 2.2×, hylo 1.1×, ana 1.6× droste — the residual is the stack-safety machinery (droste's basic schemes are naive call-stack recursion, stack-unsafe).
  • para and apo halve droste's allocation (~0.5×) — eo decorates on the same array machine; droste's zoo re-embeds subterms / re-allocates the Either spine.
  • O(1) graft with a law-shaped eq guarantee (droste's native zoo.apo is also O(1); parity with a guarantee).
  • histo/futu trail ~1.2–1.4× — the price of stack-safety droste doesn't pay.
  • The fused hylo builds no intermediate S: ~2.4× less allocation than the materialising cross spelling (byte-identical to the hand-written manual pair).
  • eoHyloM (the tailRecM per-event floor at Id): −49% over two optimisation rounds.

Full tables: site/docs/benchmarks.md (CI-swept numbers).

Test/law coverage

Law and behaviour suites across laws + tests + schemes (~536 examples): hylo fusion law + no-intermediate-S pin, degeneration laws (para↦cata, apo↦ana, heads-only histo↦cata, single-layer futu↦ana), graft eq guarantee, decoration round-trips, M = Id cross-architecture agreement, stack-safety to 10⁶ per driver, linear-M contract, and the carrier laws on Affine's build seam.

Follow-ups (out of scope here)

  • The elgot Calculator.selection port (the seam sketch in docs/brainstorms/2026-06-12-elgot-seam-sketch.md PASSed — additive follow-up).
  • The existential-X spike items (docs/brainstorms/2026-06-12-existential-x-is-the-decoration.md): memoized refolds, the honest-hylo X-parameter, the Affine composition-row extensions.
  • Higher-order decoration; cats-free interop.

Review notes: doc claims are scoped to the shipped seams (see site/docs/schemes.md, rewritten against the final API); the papers are consolidated in docs/research/2026-06-15-typed-schemes-bibliography.md.

@github-actions

github-actions Bot commented Jun 9, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Cloudflare Pages preview for feat/typed-recursion-schemes is live:

https://77621bfa.cats-eo-docs.pages.dev

Branch alias: https://feat-typed-recursion-schemes.cats-eo-docs.pages.dev

Built from commit d703be5bbcc45aa4a68a36c0e041a30ee1fa215e · updated on every push.

@kryptt

kryptt commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

U6 benchmark done (folded into this PR)

Wired eoF (typed cataF/anaF/hyloF) rows into SchemesBench vs the existing eo PSVec / droste / hand rows, and ran -prof gc (B/op — deterministic, box-independent; 8 191-node tree).

Scheme eo (PSVec) eoF (typed Eval) droste basic eoF ÷ droste
cata 197 568 2 589 211 164 824 15.7×
hylo 295 849 2 589 213 328 641 7.9×
ana 589 713 2 686 489 327 632 8.2×

Decision: the Eval trampoline is ~8–16× droste-basic B/op (~316 B/node of Eval machinery — inherent, not a bug), so it misses the allocation-parity bar. It ships as the correct / type-safe / stack-safe v1 with allocation as the documented tradeoff (droste basic is neither stack-safe nor optic-composable, so it's not apples-to-apples). Reaching parity needs the pre-planned explicit typed-heap-machine driver — now a tracked follow-up, no longer a v1-conditional. eo's own PSVec path stays droste-competitive (cata 1.2×, hylo 0.9×, ana 1.8×).

Table + rationale: site/docs/benchmarks.md (new recursion-schemes section); Open Question resolved in the plan.

Remaining follow-ups: the typed-heap-machine driver (for B/op parity), deriving Project/Embed, and a pure F[A] => A overload.

@kryptt

kryptt commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Profiling-driven optimization: typed driver now ~1.1–2.2× droste (was ~8–16×)

The U6 profiling flagged the Eval trampoline as the allocation hot spot (~316 B/node). Acting on it, I replaced the Eval driver with the pre-planned explicit foldLayered heap machine — the same <512-on-stack / heap-ArrayDeque hybrid as the PSVec engines, keeping F typed at the algebra seam (foldLeft reads a layer's children into a per-node array reused as the result accumulator; map rebuilds the typed F[result]; leaf layers skip the rebuild via a phantom F[N]→F[R] recast).

B/op (-prof gc, 8 191-node tree):

Scheme eoF was (Eval) eoF now (machine) eo PSVec droste ÷ droste
cata 2 589 211 361 387 197 568 164 824 2.2×
hylo 2 589 213 361 386 295 849 328 641 1.1×
ana 2 686 489 524 194 589 713 327 632 1.6×

~7× allocation cut. hylo is at parity with droste basic; ana now even beats eo's own PSVec path. The residual cata gap is inherent native-Bin-vs-Fix (eo projects a layer per node; droste's unfix is free — the same cost eo's PSVec cata pays), not waste. And it stays typed + stack-safe, which droste's basic schemes are not.

Bonus wins from dropping Eval:

This resolves the U6 decision: the heap-machine driver ships as v1 — no Eval, no parity follow-up needed. site/docs/benchmarks.md, schemes.md, and the plan's Open Question are updated. (Commit 9c13ae9.)

@kryptt
kryptt force-pushed the feat/typed-recursion-schemes branch from 310be0d to 70497cc Compare June 11, 2026 23:41
@kryptt kryptt changed the title feat(schemes): typed pattern-functor recursion schemes (cataF/anaF/hyloF) feat(schemes): typed recursion schemes — cataF/anaF/hyloF + the zoo (BiAffine, Decor, para/apo/histo/futu, fused cross, M-generic drivers) Jun 11, 2026

@kryptt kryptt left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still needs some polish

*/

/** Fold-scheme citizen: Getter-shaped, carrying the node-supplied algebra for fusion. */
final class Cata[F[_], S, A] private[schemes] (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be it's own file in a 'zoo' package

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 19d409c — Cata now lives in its own file schemes/zoo/Cata.scala (new dev.constructive.eo.schemes.zoo package).

Comment thread schemes/src/main/scala/dev/constructive/eo/schemes/zoo/Ana.scala Outdated
/** Generic effectful-fold citizen: `run: S => M[A]`. What `hyloM` and the fused
* `AnaM.andThen(CataM)` return.
*/
sealed class FoldM[M[_], S, A] private[schemes] (val run: S => M[A])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to the zoo package and make it public

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 19d409c — FoldM is schemes/zoo/FoldM.scala, now public with an open constructor (class FoldM[M[_], S, A](val run: S => M[A])): users can wrap their own S => M[A] as a citizen. CataM/AnaM still extend it; the no-longer-sealed trade-off is noted in its scaladoc.

def from(d: Forget[M][X, Unit]): Unit = ()

/** Effectful fold-scheme citizen: carries its algebra for fusion. */
final class CataM[M[_], F[_], S, A] private[schemes] (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zoo package

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 19d409c — schemes/zoo/CataM.scala.

/** Effectful unfold-scheme citizen: `run: Seed => M[S]`, carrying the coalgebra + instances for
* fusion.
*/
final class AnaM[M[_], F[_], Seed, S] private[schemes] (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zoo package

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 19d409c — schemes/zoo/AnaM.scala.

Comment thread schemes/src/main/scala/dev/constructive/eo/schemes/zoo/Gather.scala Outdated
* the typed [[foldLayered]] machine.
*/
private def unfoldFold[N, R](expand: N => PSVec[N], combine: (N, PSVec[R]) => R): N => R =
private val EmptyAnyRefs: Array[AnyRef] = new Array[AnyRef](0)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this? Who uses this? Is this threadsafe?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answered in 19d409c, in EmptyAnyRefs' scaladoc (now in Machines.scala): it's the shared zero-length leaf sentinel — childrenArr returns it for every leaf layer so the engines don't allocate a fresh empty array per leaf. Thread-safe by construction: it's never written (every store loop bounds at arr.length, which is 0), so it's an immutable constant shared across all concurrent runs. The new SchemesConcurrencySpec exercises exactly that sharing.

Comment thread schemes/src/main/scala/dev/constructive/eo/schemes/Schemes.scala
kryptt added a commit that referenced this pull request Jun 12, 2026
…PR review)

Addresses every inline thread on PR #24:

- One entity per file in the new schemes.zoo package: Attr, Coattr,
  Gather, Scatter, Cata, Ana, FoldM, CataM, AnaM (Citizens/CitizensM/
  Decorations.scala dissolved). FoldM is now PUBLIC with an open
  constructor — users can wrap their own S => M[A] as a citizen;
  CataM/AnaM still extend it (no longer sealed, noted in scaladoc).

- Schemes.scala keeps only the factory methods; ALL machinery moved to
  private[schemes] object Machines (OnStackLimit, EmptyAnyRefs,
  childrenArr, AscendToken, rebuildLayer(+Paired), foldLayered/Or/M,
  fusedPairedFold(+M)). Machines' header documents the thread-safety
  model: every machine allocates its mutable state per invocation (per
  FORCE on the M path), the only shared values are immutable sentinels.
  EmptyAnyRefs' scaladoc answers what/who/why-threadsafe directly (the
  PR question): a zero-length leaf sentinel used by childrenArr in every
  engine; read-only by construction (store loops bound at arr.length).

- The thread-safety claim is now TESTED, both places the review asked:
  SchemesConcurrencySpec (16 concurrent tasks mixing cata/hylo/ana/
  para/histo/futu/cataM[Eval] over shared optics and fixtures) and
  PlatedConcurrencySpec in tests/ (concurrent Plated transform/universe/
  rewrite over a shared structure).

Fixed in review of the restructure itself: the concurrency spec's
coalgebra guarded at n <= 0, making seed 1 its own child — a
non-terminating unfold the engines correctly turn into heap exhaustion.
Guard is n <= 1; the suite runs in the default 1GB test heap.

All 511 tests green; mdoc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kryptt kryptt changed the title feat(schemes): typed recursion schemes — cataF/anaF/hyloF + the zoo (BiAffine, Decor, para/apo/histo/futu, fused cross, M-generic drivers) feat(schemes): typed recursion schemes — cata/ana/hylo + the zoo (BiAffine, Gather/Scatter, para/apo/histo/futu, fused cross, M drivers) Jun 12, 2026
// ===========================================================================================

/** Fold-side decoration optic: gather-only (build-only member). `from` = gather. */
type Gather[F[_], W, A] =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a trait or a class, and avoid the annonymous class creation down below

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cf18e41 — Gather is an abstract class extending Optic directly: a decoration implements ONE named method, gather(layer, a): W, and the BiAffine to/from plumbing lives once in the class. The named values are named final classes (Gather.Id, Gather.Histo) — no anonymous instances anywhere (fixtures, benches, and the docs zygo all shrank to one-method extensions).


/** Unfold-side decoration optic: scatter (`to`, an affine match) + pointed unit (`from` on Step).
*/
type Scatter[F[_], W, A] =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment from Gather

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cf18e41 — same shape: abstract class with scatter(w): Either[F[W], A] + unit(a): W, named final classes Scatter.Id/Scatter.Futu.

* execute zero iterations when `arr` is [[EmptyAnyRefs]]. No element is ever written into it.
* An immutable zero-length array is safe to share across any number of concurrent callers.
*/
private[schemes] val EmptyAnyRefs: Array[AnyRef] = new Array[AnyRef](0)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be renamed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed in cf18e41: EmptyAnyRefs → NoChildren (the semantic name — a leaf layer's children), scaladoc kept.

Comment thread schemes/src/main/scala/dev/constructive/eo/schemes/Machines.scala
* its combiner closure (stored in the frame on the heap path). Same on-stack/heap hybrid and
* stack-safety as [[unfoldFold]].
def cata[F[_], S, W, A](
decor: Gather[F, W, A]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to gather

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cf18e41 — decor → gather (and scatter on the ana side).

def cata[F[_], S, W, A](
decor: Gather[F, W, A]
)(galg: (S, F[W]) => A)(using F: Traverse[F], P: Project[F, S]): Getter[S, A] =
if decor.asInstanceOf[AnyRef] eq Gather.cata[F, A] then

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets find a more elegant way to detect this that avoids the asInstanceOf calls

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in cf18e41 by DELETING the dispatch rather than beautifying it: the plain cata(alg) overload now implements directly on the machine (it IS the fast path — nothing to detect), and the generic cata(gather)(galg) always runs the generic route, calling gather.gather(layer, a) directly. Zero asInstanceOf at the seam, zero eq checks, no singletons — and a perf bonus: with the per-node Step wrapper gone the generic route is now byte-identical to the fast path (361,321 vs 361,385 B/op; previously equal only via escape analysis). Agreement stays law-pinned: cata(Gather.cata)(alg) == cata(alg).

def ana[F[_], A, W, S](
decor: Scatter[F, W, A]
)(gcoalg: A => F[W])(using F: Traverse[F], E: Embed[F, S]): Review[S, A] =
if decor.asInstanceOf[AnyRef] eq Scatter.ana[F, A] then

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, scala3 has more elegant ways of enabling this behaviour

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same resolution in cf18e41 — ana(coalg) direct, ana(scatter)(gcoalg) always generic via scatter.scatter/scatter.unit, zero casts, law-pinned agreement.

Comment thread site/docs/schemes.md Outdated
All three run on a single stack-safe, post-order machine (heap-stacked, not JVM-call-stacked),
so they are safe to depths a hand-written recursion would overflow. The examples below use the
circe `Plated[Json]` from `cats-eo-circe` as a concrete recursive `S`.
> An earlier `PSVec`-based untyped path (`cata`/`ana`/`hylo` driven by `Plated`) was

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be delegated to the bottom of the file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cf18e41 — the removal note now sits at the bottom of the page under a divider.

*/
class FoldM[M[_], S, A](val run: S => M[A])
extends Optic[S, Unit, A, Unit, Forget[M]]:
type X = Nothing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see X = Nothing here and in AnaM, CataM while we have Attr and CoAttr that pretty much are exactly the extra info needed on every to / from step. Surely we can better integrate these two ideas.. I do see ATtr, and CoAttr come in for histo and futu morphism. Can you think deeply about this and see if we're actually missing a very beautiful connection between optics and recursion schemes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're seeing something real — I've thought it through and written it up as docs/brainstorms/2026-06-12-existential-x-is-the-decoration.md (committed on the branch). The short version:

X = Nothing is a choice, and it's the forgetful one. In eo's encoding X is "whatever to must retain for from to rebuild". The citizens pin X = Nothing because Direct/Forget are the forgetful carriers — a Cata-as-Getter keeps only the answer. Ask instead "what would a non-forgetful fold retain?" and the zoo answers, member by member:

  • para: X = F[(S, A)] — the retained subterms are exactly a lens complement, and from = re-embed-them makes get-put hold definitionally. para is a lawful Lens wearing its decoration as X.
  • histo: X = Attr[F, A]. And here's the kicker: Attr = νX. A × F[X] — Attr is literally the fixpoint of the Gather optic's per-layer leftover (Unit, F[W]). Dually Coattr = μX. A + F[X] is the fixpoint of the Scatter side. Attr/Coattr aren't auxiliary data — they're the universal existentials of decorated schemes. The engine's out-array of W's is the X being threaded; the comonadic reading matches (lawful lens = store coalgebra; histo = gcata over cofree = the iterated store).
  • apo/futu: the same statement on the Prism side — the sum residual.

Sharpest corollary: deforestation is choosing the forgetful existential. Fused ana.cross(cata) vs the materializing composition are the same optic at two X-resolutions (Nothing vs the structure/memo) — the fusion law we pinned is an instance of a general principle.

What it buys if we pursue it (the brainstorm ranks these): para-as-lawful-Lens; memoized incremental refolds (cata.withHistory: X = Attr — only the spine above a change recomputes, lens laws become memo-coherence laws); the hylo fused/materializing choice exposed as an X parameter; and the BiAffine matrix row, where composing decorated schemes = composing their Xs.

Recommendation: not in this PR — it ships the forgetful citizens + the per-layer Gather/Scatter optics, which are exactly the substrate this builds on. I'd slot it as the act after the elgot follow-up: elgot completes the decoration vocabulary, this completes the existential story. (It's also the paper-shaped claim of the whole design: recursion schemes are optics indexed by their existential, and the (co)free (co)monads are the universal indices.)

@kryptt kryptt Jun 13, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is my intuition that's driving my insistence... I will try to thread this needle further myself. In fact it is the driving intuition for this entire branch and codebase

* `pending` is consumed only on the [[Ascend]] arm, which is reached only after a real result
* was threaded in.
*/
private val NoResult: AnyRef = new AnyRef

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private object NoResult also works

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in aa1dd43 — private object NoResult (and it now types the loop state: Pending[R] = R | NoResult.type).

Comment thread schemes/src/main/scala/dev/constructive/eo/schemes/Machines.scala Outdated
final private class Frame[F[_], N](
val node: N,
val layer: F[N],
val arr: Array[AnyRef],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arr is a bad name, pick a more descriptive name

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in aa1dd43 — arr → slots (matching the new Slot alias), i → next (slots below next hold results, at/above still hold children — the invariant is in the field's scaladoc).

// in which case `pending` carries the result to store against the top frame.
@tailrec def loop(op: AnyRef, pending: AnyRef, stack: List[Frame[F, N]]): R =
if op ne Ascend then
val n = op.asInstanceOf[N]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract transparent inline methods here (loop) to make it more readable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in aa1dd43 — the loop body delegates to transparent inline def descend(n) / bubble phase helpers; their loop calls are in tail position after inlining so @tailrec still verifies. (One caveat discovered: the WALK itself can't also be inline — nested inline methods are an implementation restriction — so heapWalk is a plain method, which is fine: it only runs past OnStackLimit, the cold path.)

// Same single-loop sentinel encoding as [[foldLayered]]'s heap walk; the graft arm
// (`Left`) feeds `pending` directly — finished, by reference.
@tailrec def loop(op: AnyRef, pending: AnyRef, stack: List[Frame[F, N]]): R =
if op ne Ascend then

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need more deduplication with the other loop, this is essentially duplicate

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in aa1dd43 — the two pure heap loops are now ONE shared heapWalk(root, expandOr, combine): foldLayered instantiates the Or-channel with a constant Right, foldLayeredOr adapts its combine. The hot on-stack recursions stay specialized per engine (that's where the 361k pin lives — verified byte-exact after the dedup), and the M machine is documented as the same walk threaded through tailRecM.

*/
type Coalg[N, R] = N => (PSVec[N], PSVec[R] => R)
def fLayer[F[_], S](using P: Project[F, S], E: Embed[F, S]): Optic[S, S, S, S, Forget[F]] =
new Optic[S, S, S, S, Forget[F]]:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid the anonymous class here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in aa1dd43 — fLayer returns a named private FLayer[F, S] class; the anonymous Optic is gone.

@kryptt kryptt added help wanted Extra attention is needed experimental Exploratory / spike work — not intended for merge as-is labels Jul 28, 2026
kryptt added a commit that referenced this pull request Sep 4, 2026
…PR review)

Addresses every inline thread on PR #24:

- One entity per file in the new schemes.zoo package: Attr, Coattr,
  Gather, Scatter, Cata, Ana, FoldM, CataM, AnaM (Citizens/CitizensM/
  Decorations.scala dissolved). FoldM is now PUBLIC with an open
  constructor — users can wrap their own S => M[A] as a citizen;
  CataM/AnaM still extend it (no longer sealed, noted in scaladoc).

- Schemes.scala keeps only the factory methods; ALL machinery moved to
  private[schemes] object Machines (OnStackLimit, EmptyAnyRefs,
  childrenArr, AscendToken, rebuildLayer(+Paired), foldLayered/Or/M,
  fusedPairedFold(+M)). Machines' header documents the thread-safety
  model: every machine allocates its mutable state per invocation (per
  FORCE on the M path), the only shared values are immutable sentinels.
  EmptyAnyRefs' scaladoc answers what/who/why-threadsafe directly (the
  PR question): a zero-length leaf sentinel used by childrenArr in every
  engine; read-only by construction (store loops bound at arr.length).

- The thread-safety claim is now TESTED, both places the review asked:
  SchemesConcurrencySpec (16 concurrent tasks mixing cata/hylo/ana/
  para/histo/futu/cataM[Eval] over shared optics and fixtures) and
  PlatedConcurrencySpec in tests/ (concurrent Plated transform/universe/
  rewrite over a shared structure).

Fixed in review of the restructure itself: the concurrency spec's
coalgebra guarded at n <= 0, making seed 1 its own child — a
non-terminating unfold the engines correctly turn into heap exhaustion.
Guard is n <= 1; the suite runs in the default 1GB test heap.

All 511 tests green; mdoc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kryptt
kryptt force-pushed the feat/typed-recursion-schemes branch from d87956c to 7073562 Compare September 4, 2026 13:32
@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Benchmark A/B

Allocation (B/op) — authoritative

Benchmark params base head Δ
SchemesBench.eoCata - 197,568.5 361,385.2 +82.9%
OrderJsoniterBench.eoReadStreet size=64 72.0 128.0 +77.7%
JsoniterBench.jReadId size=64 48.0 72.0 +50.0%
PlatedBench.visitorUniverseJson n=64 8,120.0 4,088.0 -49.7%
PlatedBench.visitorUniverseJson n=4096 524,386.4 295,011.3 -43.7%
OrderJsoniterBench.eoReadStreet size=512 128.2 72.2 -43.7%
PlatedBench.visitorUniverseJson n=512 40,954.7 57,274.2 +39.8%
SchemesBench.eoHylo - 295,848.6 361,385.2 +22.2%
OrderAvroBench.eoModifyNames size=512 109,711.0 97,423.8 -11.2%
SchemesBench.eoAna - 589,712.7 524,193.2 -11.1%
JsoniterBench.jMiss size=64 0.0 0.0 +9.2%
JsoniterBench.jMiss size=512 0.1 0.0 -9.2%
AvroVulcanBench.encode_vulcanRaw - 1,224.0 1,272.0 +3.9%
AvroVulcanBench.encode_bridged - 1,272.0 1,224.0 -3.8%
OrderJsoniterBench.monocleSumPrices size=64 45,817.6 47,329.6 +3.3%
OpticBuildBench.reuseUse - 2,720.0 2,648.0 -2.6%
OpticBuildBench.buildAndUse - 2,816.0 2,888.0 +2.6%
JsoniterBench.jModifyId size=8 992.0 968.0 -2.4%
OrderCirceBench.directNames size=64 76,163.0 77,723.8 +2.0%
OrderCirceBench.eoNames size=8 10,944.1 10,728.1 -2.0%
442 more benchmarks
Benchmark params base head Δ
OrderCirceBench.eoNamesIor size=8 11,360.1 11,576.1 +1.9%
AffineFoldBench.mGetOption_6 - 0.0 0.0 -1.7%
AvroBytesBench.eoModifyPartner - 984.0 1,000.0 +1.6%
AvroJsonBridgeBench.eoWideToAvro - 2,040.0 2,072.0 +1.6%
PowerSeriesNestedBench.eoModify_nested size=16 4,792.1 4,720.1 -1.5%
MultiFocusBench.eoModify_powerEach size=32 3,200.0 3,152.0 -1.5%
AffineFoldBench.mGetOption_0_empty - 0.0 0.0 -1.5%
OptionalBench.mModify_loyalty_empty - 0.0 0.0 -1.5%
PrismBench.mGetRightAbsent - 0.0 0.0 -1.4%
MultiFocusBench.naive_sumQty size=4 0.0 0.0 -1.4%
ZioDiBench.eoDrillGet - 0.0 0.0 -1.4%
GetterBench.mGet_orderId - 0.0 0.0 +1.3%
AvroJsonBridgeBench.eoClickToJson - 3,952.0 4,000.0 +1.2%
CapsBench.getDeepCap - 0.0 0.0 -1.2%
PrismBench.eoGetOptionPresent - 0.0 0.0 -1.1%
GenericsBench.genPrismGetMiss - 0.0 0.0 +1.1%
OptionalBench.eoModify_loyalty_empty - 0.0 0.0 +1.0%
AffineFoldBench.eoGetOption_loyalty - 0.0 0.0 +1.0%
GetterBench.mGet_0 - 0.0 0.0 +1.0%
PrismBench.mGetOptionPresent - 0.0 0.0 -0.9%
GenericsBench.handPrismModifyMiss - 0.0 0.0 +0.9%
AffineFoldBench.eoGetOption_0 - 0.0 0.0 -0.9%
OrderCirceBench.monocleStreet size=64 112,782.0 113,798.9 +0.9%
CapsBench.getDerivedHeld - 0.0 0.0 +0.9%
KyoDiBench.eoMapGet - 0.0 0.0 -0.8%
CapsBench.foldMapDerivedPerCall - 0.0 0.0 -0.8%
AffineFoldBench.mGetOption_loyalty_empty - 0.0 0.0 -0.8%
CapsBench.getDirect - 0.0 0.0 +0.8%
OrderJsoniterBench.monocleSumPrices size=512 1,187,678.3 1,178,454.4 -0.8%
GetterBench.eoGet_0 - 0.0 0.0 +0.8%
LensBench.mGet - 0.0 0.0 -0.8%
KyoDiBench.handMapGet - 0.0 0.0 +0.7%
MultiFocusBench.eoModify_multiFocus size=32 8,664.0 8,600.0 -0.7%
GenericsBench.handPrismGetMiss - 0.0 0.0 +0.7%
GetterBench.mGet_6 - 0.0 0.0 +0.7%
PrismBench.eoGetOptionAbsent - 0.0 0.0 -0.6%
AffineFoldBench.eoGetOption_0_asAffineFold - 0.0 0.0 -0.6%
PrismBench.eoGetRightAbsent - 0.0 0.0 +0.5%
GetterBench.eoGet_3 - 0.0 0.0 -0.5%
CapsBench.foldMapCap - 0.0 0.0 +0.5%
AffineFoldBench.eoGetOption_loyalty_empty - 0.0 0.0 -0.5%
GenericsBench.genPrismModifyMiss - 0.0 0.0 +0.5%
GetterBench.eoGet_orderId - 0.0 0.0 +0.5%
PowerSeriesNestedBench.eoModify_nested size=64 14,664.5 14,592.5 -0.5%
CapsBench.foldMapDerivedHeld - 0.0 0.0 +0.5%
AffineFoldBench.mGetOption_loyalty - 0.0 0.0 +0.4%
AffineFoldBench.mGetOption_3 - 0.0 0.0 +0.4%
AffineFoldBench.eoGetOption_0_empty - 0.0 0.0 -0.4%
OptionalBench.mModify_0_empty - 0.0 0.0 +0.3%
CapsBench.foldMapDirect - 0.0 0.0 -0.3%
SchemesBench.handCata - 0.1 0.1 -0.3%
CapsBench.getDerivedPerCall - 0.0 0.0 +0.3%
OrderCirceBench.eoNamesIor size=64 87,492.2 87,721.5 +0.3%
CapsBench.getDeepDirect - 0.0 0.0 -0.2%
CapsBench.getCap - 0.0 0.0 +0.2%
FoldBench.mFoldMap size=8 0.0 0.0 +0.2%
MultiFocusCollectBench.eoCollectMap_constSum - 0.0 0.0 -0.2%
GenericsBench.handLensGet - 0.0 0.0 +0.2%
PowerSeriesNestedBench.eoModify_nested size=256 53,675.0 53,771.2 +0.2%
SchemesBench.handHylo - 0.1 0.1 +0.2%
PrismBench.mGetOptionAbsent - 0.0 0.0 -0.2%
GetterBench.mGet_3 - 0.0 0.0 +0.2%
GenericsBench.genLensGet - 0.0 0.0 +0.2%
ZioDiBench.eoServiceGet - 0.0 0.0 +0.1%
PowerSeriesNestedBench.monocle_nested size=64 58,912.9 58,833.0 -0.1%
LensBench.eoGet - 0.0 0.0 +0.1%
JsoniterBench.cModifyId size=8 20,715.9 20,692.0 -0.1%
PowerSeriesNestedBench.eoModify_nested size=1024 210,468.2 210,704.1 +0.1%
FoldBench.eoFoldMap size=8 0.0 0.0 -0.1%
OrderCirceBench.monocleStreet size=8 17,064.1 17,048.1 -0.1%
OrderAvroBench.naiveModifyNames size=64 27,936.3 27,912.3 -0.1%
MultiFocusBench.eoModify_multiFocus size=256 76,112.6 76,176.4 +0.1%
OrderCirceBench.monocleNames size=8 19,472.1 19,456.1 -0.1%
GetterBench.eoGet_6 - 0.0 0.0 +0.1%
OrderJsoniterBench.nativeReadStreet size=512 454.0 453.7 -0.1%
JsoniterBench.jReplaceId size=512 41,913.1 41,889.1 -0.1%
JsoniterBench.jModifyId size=512 41,921.1 41,945.1 +0.1%
GenericsBench.rawLensGet - 0.0 0.0 +0.0%
AffineFoldBench.mGetOption_0 - 0.0 0.0 -0.0%
OrderCirceBench.eoStreetIor size=64 2,736.1 2,736.9 +0.0%
OrderCirceBench.naiveNames size=64 120,869.4 120,837.6 -0.0%
OptionalBench.eoModify_0_empty - 0.0 0.0 +0.0%
OrderJsoniterBench.nativeSumPrices size=512 86,351.2 86,330.6 -0.0%
JsoniterBench.cSumPrices size=64 156,252.9 156,276.3 +0.0%
OrderCirceBench.hcursorNames size=64 77,807.5 77,818.6 +0.0%
JsoniterBench.jMiss size=8 0.0 0.0 -0.0%
OrderAvroBench.naiveModifyStreet size=512 169,037.3 169,060.7 +0.0%
OrderCirceBench.naiveStreet size=64 113,779.9 113,795.3 +0.0%
OrderJsoniterBench.nativeSumPrices size=8 1,512.3 1,512.5 +0.0%
OrderCirceBench.eoStreetIor size=512 2,737.5 2,737.8 +0.0%
OrderAvroBench.monocleModifyStreet size=512 169,084.4 169,074.5 -0.0%
OrderCirceBench.eoStreet size=512 2,721.7 2,721.9 +0.0%
PowerSeriesBench.monocle_powerEach size=4 1,176.0 1,176.0 -0.0%
JsoniterBench.cReplaceId size=512 1,066,702.6 1,066,734.4 +0.0%
OrderJsoniterBench.nativeSumPrices size=64 10,936.7 10,936.4 -0.0%
PowerSeriesPrismBench.monocle_sparse size=32 7,040.2 7,040.0 -0.0%
JsoniterBench.jSumPrices size=512 63,634.1 63,632.5 -0.0%
OrderCirceBench.directNames size=512 613,993.5 614,008.3 +0.0%
JsoniterBench.cSumPrices size=512 1,240,792.9 1,240,820.8 +0.0%
PlatedBench.mUniverseExpr n=4096 4,687,816.5 4,687,720.2 -0.0%
OrderCirceBench.eoStreet size=64 2,721.0 2,721.0 -0.0%
PowerSeriesBench.monocle_powerEach size=4096 967,839.9 967,856.0 +0.0%
PowerSeriesPrismBench.eoModify_sparse size=2048 104,683.4 104,685.1 +0.0%
JsoniterBench.cReadStreet size=64 101,308.5 101,310.1 +0.0%
OrderCirceBench.hcursorStreet size=512 3,032.9 3,032.9 +0.0%
OrderCirceBench.monocleNames size=512 1,121,810.6 1,121,824.4 +0.0%
JsoniterBench.cSumPrices size=8 22,704.6 22,704.8 +0.0%
OrderCirceBench.naiveNames size=512 965,274.8 965,285.9 +0.0%
OrderCirceBench.eoNames size=512 614,038.8 614,031.7 -0.0%
PowerSeriesPrismBench.naive_sparse size=2048 24,612.9 24,612.7 -0.0%
OrderJsoniterBench.naiveSumPrices size=512 230,119.9 230,122.4 +0.0%
PlatedBench.eoUniverseDeep n=4096 786,583.4 786,576.0 -0.0%
OrderCirceBench.naiveStreet size=512 908,025.9 908,033.9 +0.0%
JsoniterBench.cReadId size=64 101,309.7 101,308.9 -0.0%
MultiFocusBench.eoModify_multiFocus size=1024 331,116.6 331,114.2 -0.0%
JsoniterBench.jReadId size=512 48.0 48.0 +0.0%
OrderCirceBench.directStreet size=512 2,728.8 2,728.9 +0.0%
OrderCirceBench.monocleStreet size=512 908,027.9 908,033.9 +0.0%
OrderCirceBench.hcursorNames size=512 601,680.6 601,684.2 +0.0%
JsoniterBench.cReplaceId size=64 136,217.3 136,218.1 +0.0%
PowerSeriesPrismBench.eoModify_sparse size=512 24,785.6 24,785.7 +0.0%
PowerSeriesPrismBench.monocle_sparse size=2048 523,144.7 523,142.2 -0.0%
PowerSeriesNestedBench.monocle_nested size=1024 1,118,852.3 1,118,857.3 +0.0%
OrderJsoniterBench.monocleReadStreet size=512 193,227.2 193,228.0 +0.0%
JsoniterBench.cReadId size=512 797,938.0 797,941.5 +0.0%
OrderJsoniterBench.naiveReadStreet size=512 193,227.0 193,227.9 +0.0%
JsoniterBench.cReplaceId size=8 20,668.0 20,668.0 -0.0%
PlatedBench.eoUniverseJson n=4096 786,471.1 786,473.8 +0.0%
PlatedBench.visitorTransformExpr n=4096 360,470.9 360,469.7 -0.0%
TraversalBench.mModify size=512 176,898.4 176,897.9 -0.0%
OrderJsoniterBench.monocleModifyStreet size=512 333,565.4 333,566.4 +0.0%
JsoniterBench.jReadStreet size=8 128.0 128.0 -0.0%
OrderJsoniterBench.nativeReadStreet size=64 424.5 424.5 -0.0%
PowerSeriesPrismBench.monocle_sparse size=512 166,693.6 166,693.3 -0.0%
OrderJsoniterBench.monocleModifyStreet size=64 30,081.7 30,081.8 +0.0%
JsoniterBench.cModifyId size=64 136,265.1 136,264.8 -0.0%
OrderJsoniterBench.eoSumPrices size=512 63,692.7 63,692.8 +0.0%
PowerSeriesBench.naive_powerEach size=4096 114,782.0 114,781.8 -0.0%
JsoniterBench.jReadStreet size=512 128.1 128.1 +0.0%
OrderCirceBench.hcursorStreet size=64 3,032.1 3,032.1 +0.0%
OrderAvroBench.monocleModifyNames size=64 39,865.8 39,865.9 +0.0%
OrderAvroBench.naiveModifyNames size=512 226,287.3 226,286.8 -0.0%
PlatedBench.visitorUniverseDeep n=4096 196,703.5 196,703.9 +0.0%
OrderCirceBench.directStreet size=64 2,728.1 2,728.1 -0.0%
PlatedBench.eoTransformExpr n=4096 655,358.5 655,357.2 -0.0%
OrderJsoniterBench.naiveSumPrices size=64 29,337.3 29,337.2 -0.0%
OrderJsoniterBench.naiveModifyStreet size=512 333,564.3 333,564.8 +0.0%
MultiFocusBench.eoFold_powerEach size=1024 43,035.8 43,035.8 -0.0%
PlatedBench.visitorTransformDeep n=4096 163,886.6 163,886.8 +0.0%
MultiFocusBench.naive_sumQty size=1024 16,129.1 16,129.0 -0.0%
PowerSeriesNestedBench.naive_nested size=1024 115,071.6 115,071.7 +0.0%
OrderJsoniterBench.eoModifyStreet size=512 42,003.8 42,003.8 -0.0%
PlatedBench.visitorUniverseExpr n=4096 196,654.3 196,654.1 -0.0%
OrderCirceBench.monocleNames size=64 132,761.8 132,762.0 +0.0%
OrderAvroBench.eoReadStreet size=512 88.0 88.0 -0.0%
OrderJsoniterBench.naiveReadStreet size=64 24,705.2 24,705.2 -0.0%
PlatedBench.mTransformExpr n=4096 753,734.0 753,734.9 +0.0%
PowerSeriesBench.eoModify_powerEach size=1024 41,413.9 41,413.9 -0.0%
OrderJsoniterBench.naiveModifyStreet size=64 30,057.9 30,057.9 -0.0%
OrderCirceBench.eoNamesIor size=512 683,549.9 683,549.3 -0.0%
PowerSeriesBench.monocle_powerEach size=256 107,338.4 107,338.5 +0.0%
PlatedBench.mUniverseJson n=4096 6,489,929.9 6,489,924.0 -0.0%
PlatedBench.mTransformExpr n=512 69,585.7 69,585.6 -0.0%
PowerSeriesNestedBench.monocle_nested size=256 430,200.3 430,200.7 +0.0%
OrderJsoniterBench.nativeReadStreet size=8 424.0 424.0 +0.0%
JsoniterBench.cReadStreet size=512 797,940.2 797,940.9 +0.0%
OrderJsoniterBench.eoReadStreet size=8 128.0 128.0 +0.0%
OrderAvroBench.monocleModifyNames size=512 382,757.2 382,757.5 +0.0%
TraversalBench.eoFoldPrices size=512 12,312.3 12,312.3 +0.0%
AvroBytesBench.eoReadPartner - 480.0 480.0 -0.0%
MultiFocusBench.eoModify_powerEach size=1024 115,176.8 115,176.9 +0.0%
OrderCirceBench.hcursorNames size=8 10,768.1 10,768.1 +0.0%
TraversalBench.eoFoldNested size=512 20,200.8 20,200.8 -0.0%
OrderCirceBench.directStreet size=8 2,728.0 2,728.0 +0.0%
OrderCirceBench.eoStreet size=8 2,720.0 2,720.0 -0.0%
OrderJsoniterBench.eoSumPrices size=8 1,176.0 1,176.0 +0.0%
OrderAvroBench.naiveReadStreet size=512 69,794.9 69,794.9 -0.0%
PowerSeriesNestedBench.monocle_nested size=4 5,568.0 5,568.0 -0.0%
PowerSeriesBench.eoModify_powerEach size=4096 164,374.2 164,374.3 +0.0%
OrderJsoniterBench.naiveSumPrices size=8 4,248.0 4,248.0 -0.0%
OrderJsoniterBench.eoSumPrices size=64 8,121.3 8,121.2 -0.0%
OrderAvroBench.monocleModifyStreet size=64 20,904.2 20,904.2 -0.0%
PowerSeriesPrismBench.eoModify_sparse size=128 4,816.1 4,816.1 -0.0%
JsoniterBench.cModifyId size=512 1,066,786.6 1,066,787.2 +0.0%
OrderAvroBench.eoModifyNames size=8 2,160.0 2,160.0 -0.0%
OrderJsoniterBench.monocleReadStreet size=64 24,705.2 24,705.2 -0.0%
TraversalBench.mFoldPrices size=512 162,564.9 162,564.9 -0.0%
OrderCirceBench.eoStreetIor size=8 2,736.0 2,736.0 -0.0%
JsoniterBench.jReadStreet size=64 128.0 128.0 -0.0%
SchemesBench.drosteCata - 164,824.3 164,824.3 +0.0%
MultiFocusBench.eoModify_powerEach size=256 26,080.6 26,080.6 -0.0%
OrderAvroBench.eoModifyStreet size=512 328.0 328.0 -0.0%
OrderJsoniterBench.naiveModifyStreet size=8 4,640.1 4,640.1 -0.0%
PlatedBench.mUniverseJson n=64 73,208.5 73,208.4 -0.0%
PowerSeriesBench.naive_powerEach size=256 7,224.3 7,224.3 +0.0%
PowerSeriesPrismBench.naive_sparse size=128 1,568.0 1,568.0 -0.0%
OrderAvroBench.monocleReadStreet size=512 69,795.0 69,795.0 +0.0%
FoldBench.eoFoldMap size=512 7,936.2 7,936.2 +0.0%
AvroBytesBench.prunedReadCountry - 1,976.0 1,976.0 -0.0%
PlatedBench.eoUniverseExpr n=64 12,168.0 12,168.0 -0.0%
FoldBench.eoFoldPrices size=512 12,312.4 12,312.4 -0.0%
AffineFoldBench.eoGetOption_3 - 16.0 16.0 +0.0%
ClickRecordBench.encode_derived - 744.0 744.0 +0.0%
PlatedBench.mUniverseJson n=512 699,922.1 699,922.3 +0.0%
MultiFocusBench.naive_sumQty size=256 3,840.0 3,840.1 +0.0%
TraversalBench.eoModify size=512 36,897.2 36,897.2 +0.0%
AvroDecodeReuseBench.confluentRecordReaderFresh - 3,696.0 3,696.0 +0.0%
FoldBench.mFoldMap size=512 7,936.2 7,936.2 -0.0%
OrderJsoniterBench.monocleReadStreet size=8 3,648.0 3,648.0 +0.0%
JsoniterBench.jSumPrices size=8 1,176.0 1,176.0 +0.0%
PlatedBench.eoTransformDeep n=512 57,361.2 57,361.3 +0.0%
AvroEncodeRouteBench.eo_encodeValue - 568.0 568.0 -0.0%
PowerSeriesBench.naive_powerEach size=64 1,848.0 1,848.0 +0.0%
PlatedBench.mUniverseExpr n=512 475,012.8 475,012.7 -0.0%
PowerSeriesBench.monocle_powerEach size=1024 279,425.9 279,426.0 +0.0%
MultiFocusBench.naive_listMap size=1024 65,578.2 65,578.1 -0.0%
PowerSeriesPrismBench.eoModify_sparse size=32 1,360.0 1,360.0 -0.0%
PowerSeriesNestedBench.naive_nested size=64 7,512.2 7,512.2 +0.0%
OrderCirceBench.hcursorStreet size=8 3,032.0 3,032.0 +0.0%
AvroBytesBench.eoSliceGraftPayload - 1,192.0 1,192.0 +0.0%
PowerSeriesNestedBench.monocle_nested size=16 15,696.1 15,696.1 -0.0%
PowerSeriesPrismBench.monocle_sparse size=8 1,952.0 1,952.0 +0.0%
PlatedBench.visitorUniverseDeep n=512 24,632.7 24,632.7 +0.0%
PowerSeriesBench.naive_powerEach size=1024 28,731.2 28,731.2 +0.0%
PlatedBench.eoUniverseExpr n=512 98,185.5 98,185.5 -0.0%
AvroBytesBench.eoModifyCountry - 928.0 928.0 -0.0%
OrderJsoniterBench.eoModifyStreet size=64 5,432.1 5,432.1 -0.0%
SchemesBench.drosteHylo - 328,640.5 328,640.5 +0.0%
AffineFoldBench.eoGetOption_6 - 16.0 16.0 -0.0%
AvroBytesBench.prunedReadPartner - 1,592.0 1,592.0 +0.0%
PlatedBench.visitorTransformDeep n=512 20,496.5 20,496.5 -0.0%
JsoniterBench.jModifyId size=64 5,328.0 5,328.0 -0.0%
AvroJsonBridgeBench.naiveWideToAvro - 3,504.0 3,504.0 -0.0%
PowerSeriesBench.eoModify_powerEach size=256 10,688.5 10,688.5 +0.0%
JsoniterBench.jReadId size=8 48.0 48.0 -0.0%
AvroDecodeReuseBench.confluentRecordReader - 1,560.0 1,560.0 +0.0%
PlatedBench.mUniverseExpr n=64 45,424.3 45,424.3 +0.0%
PowerSeriesNestedBench.naive_nested size=256 29,019.3 29,019.3 +0.0%
SchemesBench.drosteAna - 327,632.3 327,632.4 +0.0%
PlatedBench.eoTransformDeep n=4096 624,382.3 624,382.3 +0.0%
MultiFocusBench.eoFold_powerEach size=256 9,240.2 9,240.2 -0.0%
OrderAvroBench.naiveModifyStreet size=8 2,968.0 2,968.0 -0.0%
JsoniterBench.cReadStreet size=8 15,568.0 15,568.0 +0.0%
ClickRecordBench.encode_derivedThroughPrism - 744.0 744.0 +0.0%
PowerSeriesBench.monocle_powerEach size=64 14,504.1 14,504.1 -0.0%
MultiFocusBench.naive_listMap size=256 16,424.1 16,424.1 -0.0%
JsoniterBench.jReplaceId size=64 5,296.0 5,296.0 +0.0%
OrderAvroBench.naiveModifyStreet size=64 20,880.2 20,880.2 -0.0%
OrderCirceBench.directNames size=8 10,688.1 10,688.1 -0.0%
OrderJsoniterBench.eoModifyStreet size=8 1,072.0 1,072.0 +0.0%
PlatedBench.eoUniverseDeep n=512 98,377.5 98,377.5 -0.0%
AvroBytesBench.naiveModifyCountry - 7,616.0 7,616.0 +0.0%
PowerSeriesPrismBench.naive_sparse size=512 6,176.3 6,176.3 +0.0%
PowerSeriesBench.eoModify_powerEach size=64 3,008.0 3,008.0 +0.0%
AvroEncodeRouteBench.handwritten_stream - 240.0 240.0 -0.0%
OrderJsoniterBench.monocleModifyStreet size=8 4,664.1 4,664.1 +0.0%
CompositionBench.reuseLens6 - 120.0 120.0 +0.0%
OrderCirceBench.eoNames size=64 79,299.2 79,299.2 +0.0%
OrderAvroBench.monocleModifyNames size=8 5,400.0 5,400.0 -0.0%
PowerSeriesPrismBench.monocle_sparse size=128 27,808.2 27,808.2 -0.0%
PlatedBench.visitorUniverseExpr n=512 24,584.7 24,584.7 -0.0%
OrderAvroBench.monocleModifyStreet size=8 2,992.0 2,992.0 -0.0%
PlatedBench.visitorUniverseExpr n=64 3,080.0 3,080.0 -0.0%
PlatedBench.eoUniverseJson n=512 98,186.6 98,186.6 -0.0%
AvroJsonBridgeBench.eoClickToAvro - 4,664.0 4,664.0 +0.0%
OptionalBench.eoModify_6 - 208.0 208.0 -0.0%
OrderAvroBench.eoReadStreet size=8 88.0 88.0 -0.0%
AvroBytesBench.eoReadCountry - 520.0 520.0 +0.0%
OrderJsoniterBench.monocleSumPrices size=8 6,448.1 6,448.1 +0.0%
AvroDecodeReuseBench.cachedDecodeRecord - 1,224.0 1,224.0 -0.0%
AvroJsonBridgeBench.naiveClickToAvro - 3,928.0 3,928.0 -0.0%
KyoDiBench.eoVarUpdateFocus - 216.0 216.0 -0.0%
MultiFocusBench.eoModify_multiFocus size=4 1,320.0 1,320.0 +0.0%
PlatedBench.visitorTransformExpr n=64 5,608.0 5,608.0 -0.0%
PlatedBench.eoTransformDeep n=64 7,184.0 7,184.0 +0.0%
FoldBench.mFoldMap size=64 768.0 768.0 -0.0%
AvroBytesBench.eoGraftPayload - 720.0 720.0 +0.0%
PlatedBench.eoTransformExpr n=64 10,144.0 10,144.0 -0.0%
AvroEncodeRouteBench.naive_freshPlumbing - 2,624.0 2,624.0 +0.0%
OrderAvroBench.naiveModifyNames size=8 3,752.0 3,752.0 -0.0%
PowerSeriesPrismBench.eoModify_sparse size=8 496.0 496.0 -0.0%
TraversalBench.eoModify size=8 608.0 608.0 +0.0%
AvroDecodeReuseBench.freshDecodeRecord - 3,344.0 3,344.0 -0.0%
AvroVulcanBench.fieldGet_bridged - 432.0 432.0 +0.0%
JsoniterBench.cReadId size=8 15,568.0 15,568.0 +0.0%
PlatedBench.mTransformExpr n=64 11,728.0 11,728.0 +0.0%
OrderJsoniterBench.naiveReadStreet size=8 3,648.0 3,648.0 +0.0%
AvroVulcanBench.fieldGet_native - 432.0 432.0 +0.0%
AvroVulcanBench.decode_bridged - 832.0 832.0 -0.0%
AvroVulcanBench.decode_vulcanRaw - 832.0 832.0 +0.0%
ZioDiBench.eoServiceReplace - 400.0 400.0 +0.0%
KyoDiBench.handVarUpdate - 200.0 200.0 +0.0%
AvroVulcanBench.decode_native - 48.0 48.0 +0.0%
OptionalBench.mModify_6 - 496.0 496.0 -0.0%
OpticBuildBench.build - 184.0 184.0 -0.0%
PlatedBench.eoUniverseJson n=64 12,168.1 12,168.1 +0.0%
AvroBytesBench.naiveReadPartner - 5,424.0 5,424.0 -0.0%
TraversalBench.eoFoldNested size=64 2,680.0 2,680.0 -0.0%
AvroBytesBench.naiveReadCountry - 4,256.0 4,256.0 -0.0%
FoldBench.eoFoldPrices size=8 216.0 216.0 -0.0%
OrderAvroBench.monocleReadStreet size=64 8,848.1 8,848.1 +0.0%
MultiFocusCollectBench.eoCollectList_listSum - 56.0 56.0 +0.0%
AvroVulcanBench.rootGet_native - 600.0 600.0 -0.0%
MultiFocusBench.eoFold_powerEach size=32 920.0 920.0 +0.0%
OrderAvroBench.naiveReadStreet size=8 1,208.0 1,208.0 +0.0%
MultiFocusCollectBench.naive_zipMeanBroadcast - 1,176.0 1,176.0 -0.0%
PowerSeriesNestedBench.naive_nested size=4 792.0 792.0 +0.0%
OrderAvroBench.monocleReadStreet size=8 1,208.0 1,208.0 +0.0%
CompositionBench.buildLens1 - 72.0 72.0 -0.0%
LensBench.mModifyDeep - 176.0 176.0 +0.0%
ClickRecordBench.encode_positional - 23,912.0 23,912.0 +0.0%
PowerSeriesNestedBench.eoModify_nested size=4 2,264.0 2,264.0 -0.0%
PlatedBench.eoUniverseExpr n=4096 786,383.5 786,383.5 -0.0%
PowerSeriesPrismBench.naive_sparse size=32 416.0 416.0 -0.0%
PowerSeriesBench.eoModify_powerEach size=4 608.0 608.0 +0.0%
MultiFocusCollectBench.naive_tuple3Rewrite - 96.0 96.0 -0.0%
JsoniterBench.jSumPrices size=64 8,120.3 8,120.3 +0.0%
AvroVulcanBench.rootGet_bridged - 1,424.0 1,424.0 -0.0%
PlatedBench.visitorTransformExpr n=512 45,032.8 45,032.8 -0.0%
PowerSeriesNestedBench.naive_nested size=16 2,136.0 2,136.0 +0.0%
ZioDiBench.handDrillModify - 592.0 592.0 +0.0%
SchemesBench.handAna - 163,816.1 163,816.1 -0.0%
TraversalBench.mModify size=64 14,408.0 14,408.0 -0.0%
AvroJsonBridgeBench.naiveWideToJson - 4,376.0 4,376.0 +0.0%
CompositionBench.buildLensOptional3 - 184.0 184.0 -0.0%
PlatedBench.visitorTransformDeep n=64 2,576.0 2,576.0 -0.0%
MultiFocusBench.eoFold_powerEach size=4 328.0 328.0 +0.0%
PowerSeriesBench.monocle_powerEach size=16 3,720.0 3,720.0 -0.0%
AffineFoldBench.eoGetOption_0_asOptional - 16.0 16.0 +0.0%
FoldBench.mFoldPrices size=64 1,560.0 1,560.0 -0.0%
GenericsBench.handLensModify - 24.0 24.0 +0.0%
SetterBench.eoModify_orderId - 40.0 40.0 -0.0%
OrderAvroBench.eoModifyStreet size=8 328.0 328.0 -0.0%
AvroVulcanBench.encode_native - 56.0 56.0 +0.0%
GenericsBench.rawPrismGetHit - 16.0 16.0 -0.0%
OrderCirceBench.naiveNames size=8 17,808.1 17,808.1 -0.0%
GenericsBench.rawPrismModifyHit - 24.0 24.0 -0.0%
PlatedBench.visitorUniverseDeep n=64 3,128.0 3,128.0 +0.0%
TraversalBench.eoFoldPrices size=64 1,560.0 1,560.0 -0.0%
OrderCirceBench.naiveStreet size=8 17,040.1 17,040.1 -0.0%
SetterBench.mModify_0 - 24.0 24.0 -0.0%
MultiFocusCollectBench.naive_tuple6Rewrite - 184.0 184.0 -0.0%
ClickRecordBench.encode_vulcanFull - 28,408.1 28,408.1 +0.0%
LensBench.eoModifyDeep - 152.0 152.0 -0.0%
TraversalBench.eoModify size=64 4,640.0 4,640.0 -0.0%
LensBench.mReplace - 40.0 40.0 -0.0%
OrderAvroBench.eoReadStreet size=64 88.0 88.0 +0.0%
IsoBench.mGet - 32.0 32.0 -0.0%
KyoDiBench.eoEnvFocus - 320.0 320.0 -0.0%
ZioDiBench.eoDrillModify - 472.0 472.0 +0.0%
FoldBench.eoFoldMap size=64 768.0 768.0 -0.0%
MultiFocusCollectBench.naive_constSum - 16.0 16.0 -0.0%
IsoBench.eoGet - 32.0 32.0 +0.0%
SetterBench.mModify_orderId - 40.0 40.0 +0.0%
LensBench.eoReplace - 40.0 40.0 -0.0%
CapsBench.modifyDerivedHeld - 40.0 40.0 -0.0%
PowerSeriesBench.naive_powerEach size=4 168.0 168.0 -0.0%
PrismBench.eoRightReverseGet - 16.0 16.0 -0.0%
KyoDiBench.eoMapDrillModify - 216.0 216.0 +0.0%
PowerSeriesPrismBench.naive_sparse size=8 128.0 128.0 +0.0%
CompositionBench.reuseLens3 - 72.0 72.0 -0.0%
PrismBench.mReverseGet - 16.0 16.0 +0.0%
OptionalBench.eoReplace_loyalty - 88.0 88.0 +0.0%
AvroJsonBridgeBench.eoWideToJson - 1,472.0 1,472.0 -0.0%
OptionalBench.mReplace_loyalty - 88.0 88.0 -0.0%
OrderAvroBench.eoModifyStreet size=64 328.0 328.0 -0.0%
PowerSeriesBench.eoModify_powerEach size=16 1,088.0 1,088.0 +0.0%
OptionalBench.eoModify_0 - 112.0 112.0 +0.0%
CapsBench.modifyCap - 40.0 40.0 -0.0%
CapsBench.modifyDeepCap - 176.0 176.0 +0.0%
OptionalBench.mModify_loyalty - 112.0 112.0 -0.0%
MultiFocusCollectBench.eoModify_multiFocusTuple6 - 224.0 224.0 +0.0%
CapsBench.modifyDerivedPerCall - 40.0 40.0 +0.0%
ReviewBench.naiveBuild_0 - 24.0 24.0 +0.0%
FoldBench.mFoldPrices size=8 216.0 216.0 -0.0%
SetterBench.mModify_3 - 168.0 168.0 -0.0%
LensBench.mModify - 40.0 40.0 -0.0%
TraversalBench.mFoldPrices size=64 15,408.1 15,408.1 +0.0%
AvroEncodeRouteBench.eo_encodeToAny - 312.0 312.0 +0.0%
OptionalBench.eoModify_3 - 160.0 160.0 -0.0%
AvroBytesBench.naivePassthroughPayload - 14,088.1 14,088.1 -0.0%
OptionalBench.eoModify_loyalty - 112.0 112.0 -0.0%
SetterBench.eoModify_0 - 24.0 24.0 -0.0%
MultiFocusBench.naive_listMap size=32 2,088.0 2,088.0 +0.0%
ReviewBench.eoReverseGet_6 - 120.0 120.0 -0.0%
KyoDiBench.handMapDrillModify - 216.0 216.0 +0.0%
GenericsBench.genPrismGetHit - 16.0 16.0 -0.0%
ZioDiBench.handServiceGet - 120.0 120.0 -0.0%
CapsBench.modifyDirect - 40.0 40.0 +0.0%
TraversalBench.mFoldPrices size=8 2,000.0 2,000.0 +0.0%
ZioDiBench.handDrillGet - 120.0 120.0 +0.0%
MultiFocusBench.naive_listMap size=4 296.0 296.0 +0.0%
OrderAvroBench.eoModifyNames size=64 14,152.1 14,152.1 -0.0%
ReviewBench.eoReverseGet_0 - 24.0 24.0 -0.0%
ClickRecordBench.encode_hand - 768.0 768.0 +0.0%
PrismBench.mGetRightPresent - 16.0 16.0 -0.0%
ReviewBench.naiveBuild_3 - 72.0 72.0 -0.0%
KyoDiBench.handEnvUse - 312.0 312.0 +0.0%
ReviewBench.eoReverseGet_3 - 72.0 72.0 +0.0%
TraversalBench.eoFoldNested size=8 328.0 328.0 +0.0%
PrismBench.eoReverseGet - 16.0 16.0 -0.0%
FoldBench.mFoldPrices size=512 12,312.3 12,312.3 +0.0%
PlatedBench.eoUniverseDeep n=64 12,360.0 12,360.0 -0.0%
CompositionBench.buildLens3 - 184.0 184.0 -0.0%
GenericsBench.handPrismGetHit - 16.0 16.0 +0.0%
PlatedBench.eoTransformExpr n=512 81,825.7 81,825.7 -0.0%
GenericsBench.genLensModify - 24.0 24.0 -0.0%
ZioDiBench.handServiceReplace - 520.0 520.0 +0.0%
SetterBench.mModify_6 - 288.0 288.0 -0.0%
AvroJsonBridgeBench.naiveClickToJson - 5,856.0 5,856.0 +0.0%
JsoniterBench.jReplaceId size=8 936.0 936.0 -0.0%
GenericsBench.rawLensModify - 24.0 24.0 +0.0%
TraversalBench.mModify size=8 1,896.0 1,896.0 -0.0%
SetterBench.eoModify_3 - 72.0 72.0 -0.0%
MultiFocusCollectBench.eoCollectMap_zipMean - 880.0 880.0 +0.0%
IsoBench.mReverseGet - 32.0 32.0 +0.0%
OrderAvroBench.naiveReadStreet size=64 8,848.1 8,848.1 -0.0%
PowerSeriesBench.naive_powerEach size=16 504.0 504.0 -0.0%
OptionalBench.mModify_3 - 304.0 304.0 -0.0%
IsoBench.eoReverseGet - 32.0 32.0 -0.0%
TraversalBench.eoFoldPrices size=8 216.0 216.0 +0.0%
OptionalBench.mModify_0 - 112.0 112.0 -0.0%
MultiFocusBench.eoModify_powerEach size=4 800.0 800.0 +0.0%
SetterBench.eoModify_6 - 120.0 120.0 +0.0%
CompositionBench.buildLens6 - 352.0 352.0 -0.0%
CompositionBench.reuseLens1 - 40.0 40.0 -0.0%
MultiFocusBench.naive_sumQty size=32 256.0 256.0 +0.0%
PrismBench.mRightReverseGet - 16.0 16.0 -0.0%
CompositionBench.reuseLeaf - 24.0 24.0 -0.0%
ReviewBench.naiveBuild_6 - 120.0 120.0 +0.0%
GenericsBench.genPrismModifyHit - 24.0 24.0 +0.0%
PrismBench.eoGetRightPresent - 16.0 16.0 +0.0%
OptionalBench.mReplace_0 - 40.0 40.0 +0.0%
MultiFocusCollectBench.eoModify_multiFocusTuple3 - 128.0 128.0 +0.0%
GenericsBench.handPrismModifyHit - 24.0 24.0 +0.0%
FoldBench.eoFoldPrices size=64 1,560.0 1,560.0 +0.0%
OptionalBench.eoReplace_0 - 40.0 40.0 -0.0%
AvroBytesBench.naiveModifyPartner - 8,696.0 8,696.0 +0.0%
LensBench.eoModify - 40.0 40.0 -0.0%
CapsBench.modifyDeepDirect - 152.0 152.0 +0.0%
CompositionBench.reuseLensOptional3 - 160.0 160.0 -0.0%
MultiFocusCollectBench.naive_listSum - 56.0 56.0 +0.0%
Timing (ns/op) — directional only, same-VM but shared runner
Benchmark params base head Δ
SchemesBench.eoCata - 76,632.0 165,928.8 +116.5%
SchemesBench.eoHylo - 84,267.2 166,212.1 +97.2%
SchemesBench.eoAna - 104,793.3 172,493.8 +64.6%
MultiFocusBench.eoModify_powerEach size=32 765.0 1,020.8 +33.4%
SchemesBench.drosteCata - 38,872.7 47,837.6 +23.1%
PlatedBench.visitorUniverseJson n=512 25,904.6 21,336.6 -17.6%
JsoniterBench.jReadStreet size=8 224.9 191.8 -14.7%
PowerSeriesNestedBench.monocle_nested size=4 1,408.6 1,220.5 -13.4%
MultiFocusBench.eoModify_multiFocus size=32 1,566.6 1,364.4 -12.9%
OpticBuildBench.reuseUse - 1,038.8 1,164.3 +12.1%
MultiFocusBench.eoModify_multiFocus size=256 11,242.1 12,466.7 +10.9%
PlatedBench.eoUniverseDeep n=4096 109,034.7 98,954.7 -9.2%
JsoniterBench.jMiss size=512 185.1 168.0 -9.2%
SchemesBench.drosteHylo - 64,764.0 70,668.4 +9.1%
JsoniterBench.jMiss size=64 168.5 183.9 +9.1%
SchemesBench.drosteAna - 47,468.3 51,725.6 +9.0%
AvroBytesBench.eoReadPartner - 230.3 210.1 -8.8%
PowerSeriesPrismBench.monocle_sparse size=8 341.1 370.0 +8.5%
OrderJsoniterBench.eoReadStreet size=512 198.9 214.9 +8.0%
PowerSeriesPrismBench.eoModify_sparse size=512 5,667.1 6,119.8 +8.0%
OrderCirceBench.eoStreet size=8 1,101.9 1,016.0 -7.8%
OrderCirceBench.directNames size=512 241,406.0 259,874.5 +7.7%
OrderCirceBench.eoStreetIor size=512 1,041.9 1,121.6 +7.6%
JsoniterBench.jReadId size=64 34.2 36.8 +7.6%
OrderJsoniterBench.eoReadStreet size=64 213.5 199.6 -6.5%
AvroDecodeReuseBench.confluentRecordReaderFresh - 1,313.8 1,395.0 +6.2%
JsoniterBench.jSumPrices size=512 91,621.1 86,099.3 -6.0%
PlatedBench.mUniverseJson n=64 23,791.5 22,383.6 -5.9%
PlatedBench.visitorUniverseJson n=4096 168,168.8 158,439.8 -5.8%
OrderJsoniterBench.naiveSumPrices size=8 1,981.9 1,868.0 -5.7%
AvroJsonBridgeBench.naiveWideToAvro - 1,023.4 965.8 -5.6%
OrderAvroBench.monocleModifyStreet size=64 6,008.9 5,670.9 -5.6%
PowerSeriesBench.monocle_powerEach size=4096 176,761.5 186,644.8 +5.6%
AvroVulcanBench.encode_bridged - 261.4 246.8 -5.6%
OrderCirceBench.naiveStreet size=512 177,727.4 187,574.2 +5.5%
OrderCirceBench.hcursorStreet size=64 1,112.0 1,172.4 +5.4%
AvroVulcanBench.encode_vulcanRaw - 245.2 258.5 +5.4%
ClickRecordBench.encode_derived - 522.6 550.9 +5.4%
OrderCirceBench.hcursorStreet size=512 1,133.5 1,192.9 +5.2%
OrderCirceBench.eoStreet size=512 1,097.7 1,043.1 -5.0%
OrderCirceBench.directStreet size=64 1,066.3 1,014.8 -4.8%
JsoniterBench.cReadStreet size=64 25,589.0 26,752.7 +4.5%
PlatedBench.mUniverseExpr n=4096 2,941,563.5 2,808,653.2 -4.5%
AffineFoldBench.eoGetOption_3 - 14.0 14.6 +4.4%
JsoniterBench.jModifyId size=64 512.4 490.1 -4.3%
AvroJsonBridgeBench.eoClickToJson - 2,739.8 2,855.6 +4.2%
JsoniterBench.cSumPrices size=512 345,217.9 359,397.8 +4.1%
OrderJsoniterBench.naiveSumPrices size=64 12,246.4 11,748.4 -4.1%
CompositionBench.buildLens1 - 5.9 5.6 -4.0%
PowerSeriesPrismBench.monocle_sparse size=32 1,103.0 1,060.4 -3.9%
OrderJsoniterBench.monocleModifyStreet size=64 18,812.1 19,537.1 +3.9%
JsoniterBench.cModifyId size=8 8,369.2 8,687.8 +3.8%
OrderCirceBench.monocleStreet size=64 20,450.7 21,221.6 +3.8%
OrderJsoniterBench.monocleSumPrices size=64 15,163.5 15,734.6 +3.8%
PlatedBench.mTransformExpr n=512 15,749.9 15,161.3 -3.7%
AvroDecodeReuseBench.confluentRecordReader - 417.8 433.1 +3.7%
AvroBytesBench.eoSliceGraftPayload - 306.4 317.4 +3.6%
JsoniterBench.cReadId size=64 27,110.0 26,134.8 -3.6%
OrderCirceBench.eoNames size=512 248,025.5 239,229.0 -3.5%
JsoniterBench.jReadId size=512 33.1 34.2 +3.5%
AvroBytesBench.prunedReadCountry - 956.1 922.7 -3.5%
PlatedBench.visitorUniverseJson n=64 2,338.5 2,419.6 +3.5%
MultiFocusCollectBench.naive_tuple3Rewrite - 7.9 7.6 -3.4%
OrderAvroBench.eoModifyNames size=512 33,698.6 32,594.7 -3.3%
MultiFocusBench.eoModify_multiFocus size=4 215.2 222.1 +3.2%
JsoniterBench.cModifyId size=64 52,648.9 54,300.3 +3.1%
TraversalBench.eoFoldPrices size=512 1,866.8 1,924.3 +3.1%
OrderCirceBench.directNames size=64 30,706.8 31,650.8 +3.1%
OrderJsoniterBench.naiveSumPrices size=512 93,340.5 96,204.4 +3.1%
OrderCirceBench.monocleStreet size=512 180,046.1 185,435.5 +3.0%
PowerSeriesNestedBench.eoModify_nested size=1024 71,301.3 73,386.1 +2.9%
JsoniterBench.cReadStreet size=8 4,095.8 4,210.6 +2.8%
JsoniterBench.cReadId size=512 200,339.5 205,915.7 +2.8%
PowerSeriesNestedBench.monocle_nested size=64 8,669.1 8,901.2 +2.7%
ZioDiBench.eoServiceReplace - 65.2 66.9 +2.7%
AvroBytesBench.eoModifyCountry - 319.9 311.4 -2.6%
JsoniterBench.jReplaceId size=64 472.2 484.5 +2.6%
PowerSeriesNestedBench.monocle_nested size=16 2,675.7 2,607.2 -2.6%
PowerSeriesBench.monocle_powerEach size=4 208.6 213.9 +2.5%
PlatedBench.visitorTransformExpr n=4096 64,440.1 62,823.5 -2.5%
OrderJsoniterBench.nativeSumPrices size=512 62,290.0 63,802.9 +2.4%
OrderCirceBench.directNames size=8 3,994.0 4,090.6 +2.4%
AvroEncodeRouteBench.eo_encodeValue - 635.4 620.0 -2.4%
OrderAvroBench.naiveModifyStreet size=8 942.3 919.5 -2.4%
OrderJsoniterBench.naiveReadStreet size=64 11,549.4 11,277.3 -2.4%
OrderCirceBench.hcursorStreet size=8 1,079.5 1,104.8 +2.3%
OrderJsoniterBench.eoSumPrices size=8 1,443.2 1,475.8 +2.3%
OrderCirceBench.directStreet size=512 1,055.4 1,078.5 +2.2%
MultiFocusCollectBench.naive_zipMeanBroadcast - 159.7 156.3 -2.2%
MultiFocusBench.naive_sumQty size=1024 4,110.9 4,021.5 -2.2%
AvroEncodeRouteBench.naive_freshPlumbing - 659.3 673.5 +2.2%
OrderJsoniterBench.eoReadStreet size=8 197.1 201.3 +2.1%
AvroVulcanBench.fieldGet_bridged - 99.9 102.0 +2.1%
OrderAvroBench.monocleModifyNames size=8 1,420.5 1,391.1 -2.1%
OrderJsoniterBench.nativeReadStreet size=8 824.0 841.1 +2.1%
OrderJsoniterBench.monocleReadStreet size=8 1,797.9 1,835.1 +2.1%
MultiFocusBench.naive_sumQty size=256 728.4 743.4 +2.1%
OrderJsoniterBench.naiveModifyStreet size=8 3,155.2 3,091.1 -2.0%
JsoniterBench.cReplaceId size=64 51,747.0 50,695.9 -2.0%
KyoDiBench.eoVarUpdateFocus - 63.7 62.4 -2.0%
AvroVulcanBench.fieldGet_native - 99.5 101.5 +2.0%
OrderCirceBench.hcursorNames size=64 29,958.6 30,556.2 +2.0%
AvroVulcanBench.decode_vulcanRaw - 185.9 189.6 +2.0%
SchemesBench.handAna - 20,780.7 20,373.0 -2.0%
AvroVulcanBench.decode_bridged - 196.9 193.1 -2.0%
OrderCirceBench.hcursorNames size=8 4,117.9 4,198.2 +1.9%
JsoniterBench.cReplaceId size=8 8,728.2 8,558.8 -1.9%
SetterBench.eoModify_orderId - 3.4 3.3 -1.9%
JsoniterBench.jModifyId size=8 112.5 110.3 -1.9%
PowerSeriesNestedBench.eoModify_nested size=16 1,472.4 1,444.0 -1.9%
AvroBytesBench.prunedReadPartner - 627.5 639.6 +1.9%
LensBench.mModifyDeep - 29.4 30.0 +1.9%
OrderJsoniterBench.monocleSumPrices size=512 427,881.7 419,775.3 -1.9%
JsoniterBench.cReadId size=8 3,996.5 4,070.4 +1.8%
OrderCirceBench.hcursorNames size=512 240,337.2 244,770.7 +1.8%
MultiFocusBench.eoFold_powerEach size=1024 14,797.1 14,524.2 -1.8%
OrderAvroBench.monocleModifyStreet size=8 923.9 907.1 -1.8%
PowerSeriesBench.monocle_powerEach size=256 21,534.9 21,913.8 +1.8%
TraversalBench.eoFoldNested size=512 4,857.0 4,772.0 -1.7%
JsoniterBench.jSumPrices size=8 1,437.5 1,462.6 +1.7%
AffineFoldBench.mGetOption_6 - 24.2 23.8 -1.7%
PowerSeriesPrismBench.monocle_sparse size=512 34,313.8 33,722.1 -1.7%
OptionalBench.mModify_6 - 121.1 119.1 -1.7%
MultiFocusCollectBench.naive_tuple6Rewrite - 14.5 14.2 -1.7%
CompositionBench.buildLensOptional3 - 21.4 21.0 -1.7%
PlatedBench.eoUniverseJson n=4096 218,565.8 222,262.6 +1.7%
AvroJsonBridgeBench.naiveClickToAvro - 1,469.1 1,444.7 -1.7%
GenericsBench.rawPrismModifyHit - 2.3 2.2 -1.6%
MultiFocusBench.eoModify_powerEach size=256 8,302.3 8,171.7 -1.6%
PowerSeriesBench.monocle_powerEach size=64 2,173.6 2,140.0 -1.5%
ZioDiBench.handDrillModify - 90.3 91.7 +1.5%
OpticBuildBench.build - 44.8 44.1 -1.5%
ClickRecordBench.encode_derivedThroughPrism - 528.0 536.0 +1.5%
ZioDiBench.eoDrillGet - 10.4 10.3 -1.5%
JsoniterBench.cReadStreet size=512 206,387.6 203,305.7 -1.5%
OrderJsoniterBench.naiveModifyStreet size=64 19,206.6 18,920.1 -1.5%
AffineFoldBench.mGetOption_0_empty - 0.9 0.9 -1.5%
SetterBench.mModify_0 - 2.5 2.4 -1.5%
JsoniterBench.jReadId size=8 34.8 34.2 -1.5%
GetterBench.mGet_orderId - 0.5 0.5 +1.5%
OrderJsoniterBench.eoModifyStreet size=512 4,133.1 4,073.2 -1.4%
AvroEncodeRouteBench.handwritten_stream - 162.1 159.8 -1.4%
LensBench.mReplace - 3.4 3.4 -1.4%
OrderAvroBench.monocleModifyNames size=512 91,723.9 93,045.2 +1.4%
TraversalBench.mFoldPrices size=512 31,098.1 30,652.1 -1.4%
GenericsBench.handLensModify - 2.7 2.8 +1.4%
PrismBench.mGetRightAbsent - 1.1 1.1 -1.4%
KyoDiBench.handVarUpdate - 60.9 61.8 +1.4%
GenericsBench.rawPrismGetHit - 1.8 1.8 -1.4%
OrderCirceBench.eoStreet size=64 1,030.6 1,044.5 +1.3%
ClickRecordBench.encode_positional - 6,340.8 6,425.9 +1.3%
PlatedBench.eoTransformExpr n=4096 129,810.6 128,102.6 -1.3%
OptionalBench.mModify_loyalty_empty - 1.6 1.6 -1.3%
JsoniterBench.cModifyId size=512 410,320.0 405,028.7 -1.3%
CapsBench.getDeepCap - 1.5 1.5 -1.3%
AffineFoldBench.eoGetOption_loyalty - 1.0 1.0 +1.3%
AvroVulcanBench.rootGet_native - 172.3 170.1 -1.3%
OrderCirceBench.eoNamesIor size=8 4,561.1 4,619.2 +1.3%
JsoniterBench.cReplaceId size=512 409,680.9 404,483.2 -1.3%
OrderCirceBench.directStreet size=8 1,017.6 1,030.5 +1.3%
AffineFoldBench.eoGetOption_0_asOptional - 2.2 2.2 +1.2%
AvroDecodeReuseBench.cachedDecodeRecord - 407.4 402.4 -1.2%
GenericsBench.handPrismModifyMiss - 1.0 1.1 +1.2%
JsoniterBench.cSumPrices size=8 6,189.8 6,114.2 -1.2%
LensBench.eoReplace - 3.4 3.3 -1.2%
PlatedBench.visitorTransformExpr n=64 985.4 973.5 -1.2%
OrderCirceBench.eoNames size=8 4,365.9 4,313.6 -1.2%
AvroBytesBench.naiveModifyCountry - 2,638.5 2,669.9 +1.2%
OrderAvroBench.naiveModifyNames size=8 1,106.1 1,093.1 -1.2%
OrderAvroBench.eoReadStreet size=512 35.8 35.4 -1.2%
AvroVulcanBench.decode_native - 17.3 17.5 +1.2%
OrderJsoniterBench.monocleReadStreet size=512 87,815.4 88,839.6 +1.2%
OrderJsoniterBench.naiveReadStreet size=512 87,649.4 88,668.6 +1.2%
SetterBench.mModify_orderId - 3.4 3.4 +1.2%
AffineFoldBench.eoGetOption_6 - 27.9 27.6 -1.2%
FoldBench.eoFoldMap size=512 1,237.4 1,251.7 +1.2%
OrderAvroBench.naiveModifyNames size=64 7,655.6 7,567.7 -1.1%
AvroBytesBench.eoModifyPartner - 397.2 392.7 -1.1%
AffineFoldBench.eoGetOption_0 - 0.9 0.9 -1.1%
PrismBench.eoGetOptionPresent - 0.9 0.9 -1.1%
PowerSeriesBench.eoModify_powerEach size=4 124.2 125.6 +1.1%
GetterBench.mGet_0 - 0.5 0.5 +1.1%
OrderAvroBench.eoReadStreet size=8 35.6 35.2 -1.1%
TraversalBench.eoModify size=8 96.0 94.9 -1.1%
PowerSeriesPrismBench.eoModify_sparse size=8 119.3 118.1 -1.1%
TraversalBench.mModify size=512 33,287.2 32,939.0 -1.0%
AvroJsonBridgeBench.eoClickToAvro - 2,919.0 2,949.5 +1.0%
OptionalBench.eoModify_6 - 137.2 135.8 -1.0%
MultiFocusBench.naive_sumQty size=4 8.5 8.4 -1.0%
CapsBench.getDirect - 1.0 1.0 +1.0%
IsoBench.mGet - 3.6 3.6 -1.0%
AvroBytesBench.eoGraftPayload - 158.1 159.7 +1.0%
PlatedBench.visitorUniverseDeep n=4096 54,332.4 54,871.0 +1.0%
OrderJsoniterBench.nativeReadStreet size=512 33,346.7 33,018.3 -1.0%
FoldBench.eoFoldPrices size=512 2,173.8 2,152.4 -1.0%
OptionalBench.mReplace_loyalty - 7.3 7.2 -1.0%
OrderCirceBench.monocleNames size=512 226,152.4 228,329.2 +1.0%
MultiFocusBench.eoModify_powerEach size=1024 33,930.9 34,252.7 +0.9%
IsoBench.eoGet - 3.5 3.5 +0.9%
OrderCirceBench.eoStreetIor size=8 1,018.0 1,027.6 +0.9%
PlatedBench.eoTransformDeep n=512 11,756.8 11,866.5 +0.9%
GenericsBench.genPrismGetMiss - 1.1 1.1 +0.9%
PowerSeriesNestedBench.naive_nested size=64 1,323.1 1,335.4 +0.9%
AvroVulcanBench.rootGet_bridged - 393.4 389.8 -0.9%
LensBench.mGet - 1.2 1.2 -0.9%
OrderJsoniterBench.eoModifyStreet size=64 686.3 680.1 -0.9%
OrderJsoniterBench.eoModifyStreet size=8 289.0 291.6 +0.9%
FoldBench.mFoldMap size=512 1,242.1 1,230.9 -0.9%
PlatedBench.eoTransformDeep n=64 1,413.2 1,425.8 +0.9%
OrderJsoniterBench.monocleReadStreet size=64 11,625.1 11,521.0 -0.9%
JsoniterBench.jReadStreet size=64 201.4 199.6 -0.9%
PlatedBench.eoUniverseExpr n=512 14,080.4 13,957.7 -0.9%
PowerSeriesPrismBench.monocle_sparse size=128 3,892.4 3,858.8 -0.9%
PowerSeriesBench.naive_powerEach size=256 1,615.7 1,629.6 +0.9%
CompositionBench.reuseLens6 - 123.9 125.0 +0.9%
OptionalBench.eoReplace_loyalty - 7.6 7.7 +0.9%
KyoDiBench.eoMapGet - 2.4 2.4 -0.9%
AffineFoldBench.mGetOption_loyalty_empty - 1.0 1.0 -0.8%
PowerSeriesPrismBench.eoModify_sparse size=128 1,321.1 1,310.1 -0.8%
FoldBench.mFoldMap size=64 158.1 156.8 -0.8%
CapsBench.foldMapDerivedPerCall - 21.2 21.1 -0.8%
MultiFocusBench.naive_listMap size=256 2,064.7 2,048.0 -0.8%
TraversalBench.eoModify size=512 7,243.4 7,301.7 +0.8%
OrderCirceBench.naiveNames size=64 22,896.0 23,079.5 +0.8%
PlatedBench.visitorTransformDeep n=4096 41,981.4 42,314.2 +0.8%
PowerSeriesNestedBench.naive_nested size=4 124.3 125.3 +0.8%
PlatedBench.mTransformExpr n=64 2,486.9 2,506.4 +0.8%
MultiFocusCollectBench.naive_constSum - 1.8 1.8 -0.8%
GetterBench.eoGet_0 - 0.9 0.9 +0.8%
PrismBench.eoGetOptionAbsent - 0.9 0.9 -0.8%
AvroDecodeReuseBench.freshDecodeRecord - 1,345.5 1,335.3 -0.8%
TraversalBench.eoFoldNested size=64 533.4 529.4 -0.8%
CapsBench.modifyDerivedHeld - 5.2 5.1 -0.8%
AvroBytesBench.eoReadCountry - 171.2 172.5 +0.7%
PowerSeriesBench.eoModify_powerEach size=1024 12,451.5 12,361.3 -0.7%
AvroVulcanBench.encode_native - 11.8 11.9 +0.7%
OrderJsoniterBench.monocleModifyStreet size=512 158,132.4 159,259.6 +0.7%
PlatedBench.mUniverseJson n=512 247,500.9 249,259.7 +0.7%
MultiFocusCollectBench.eoModify_multiFocusTuple6 - 27.4 27.6 +0.7%
CapsBench.modifyCap - 4.3 4.2 -0.7%
OrderAvroBench.naiveModifyStreet size=64 6,016.8 5,974.4 -0.7%
PlatedBench.mTransformExpr n=4096 162,167.4 163,310.6 +0.7%
JsoniterBench.cSumPrices size=64 41,589.8 41,882.8 +0.7%
ReviewBench.naiveBuild_0 - 2.3 2.3 +0.7%
MultiFocusCollectBench.eoCollectMap_constSum - 2.0 2.0 -0.7%
PowerSeriesNestedBench.monocle_nested size=256 92,653.4 93,287.5 +0.7%
MultiFocusBench.naive_listMap size=1024 8,340.5 8,286.6 -0.6%
MultiFocusCollectBench.eoCollectList_listSum - 32.1 32.3 +0.6%
PrismBench.eoRightReverseGet - 2.3 2.3 -0.6%
PowerSeriesPrismBench.naive_sparse size=32 74.4 73.9 -0.6%
PowerSeriesPrismBench.eoModify_sparse size=2048 23,044.8 22,897.7 -0.6%
FoldBench.eoFoldPrices size=8 25.9 25.7 -0.6%
PlatedBench.visitorUniverseExpr n=4096 52,637.4 52,309.4 -0.6%
PrismBench.mGetOptionPresent - 1.0 1.0 -0.6%
OrderAvroBench.naiveModifyStreet size=512 45,635.6 45,908.4 +0.6%
JsoniterBench.jModifyId size=512 3,813.5 3,790.9 -0.6%
PowerSeriesNestedBench.monocle_nested size=1024 247,279.4 248,734.7 +0.6%
AffineFoldBench.eoGetOption_0_asAffineFold - 0.9 0.9 -0.6%
PrismBench.mReverseGet - 2.6 2.6 +0.6%
OrderAvroBench.monocleModifyStreet size=512 46,061.0 45,796.7 -0.6%
LensBench.eoModifyDeep - 33.3 33.1 -0.6%
CapsBench.modifyDerivedPerCall - 4.7 4.7 +0.6%
ZioDiBench.handDrillGet - 13.9 14.0 +0.6%
LensBench.eoGet - 1.0 1.0 +0.6%
GenericsBench.handPrismGetMiss - 1.0 1.0 +0.6%
OptionalBench.eoModify_loyalty_empty - 1.2 1.3 +0.6%
OrderAvroBench.monocleReadStreet size=8 475.7 478.3 +0.6%
OrderJsoniterBench.monocleSumPrices size=8 2,431.9 2,445.3 +0.6%
ReviewBench.naiveBuild_3 - 6.7 6.7 -0.5%
GenericsBench.handLensGet - 1.0 1.0 +0.5%
ZioDiBench.eoDrillModify - 94.2 94.7 +0.5%
PlatedBench.eoUniverseDeep n=512 14,380.2 14,305.3 -0.5%
SetterBench.eoModify_0 - 2.4 2.4 -0.5%
PowerSeriesBench.monocle_powerEach size=16 610.4 607.2 -0.5%
SetterBench.mModify_3 - 28.1 27.9 -0.5%
PlatedBench.visitorUniverseDeep n=512 6,515.5 6,548.9 +0.5%
OrderJsoniterBench.nativeSumPrices size=64 7,759.2 7,720.1 -0.5%
GenericsBench.genPrismModifyMiss - 1.2 1.2 +0.5%
PlatedBench.visitorTransformDeep n=512 4,900.7 4,876.2 -0.5%
LensBench.mModify - 4.7 4.7 -0.5%
GetterBench.eoGet_orderId - 0.9 0.9 +0.5%
OpticBuildBench.buildAndUse - 1,121.0 1,126.5 +0.5%
PowerSeriesPrismBench.eoModify_sparse size=32 346.1 347.8 +0.5%
CapsBench.modifyDirect - 4.1 4.1 +0.5%
OrderCirceBench.monocleNames size=8 3,636.0 3,653.6 +0.5%
OrderAvroBench.naiveReadStreet size=8 473.2 475.5 +0.5%
JsoniterBench.jReadStreet size=512 197.7 198.6 +0.5%
OptionalBench.eoModify_0_empty - 1.2 1.3 +0.5%
CapsBench.getDerivedPerCall - 1.6 1.6 +0.5%
OptionalBench.mModify_loyalty - 16.2 16.1 -0.5%
PlatedBench.mUniverseExpr n=512 197,338.2 196,397.8 -0.5%
TraversalBench.mModify size=64 1,803.8 1,795.4 -0.5%
ClickRecordBench.encode_vulcanFull - 7,772.5 7,808.8 +0.5%
PlatedBench.mUniverseExpr n=64 17,617.5 17,699.4 +0.5%
OrderJsoniterBench.nativeSumPrices size=8 1,135.4 1,140.6 +0.5%
OrderAvroBench.monocleReadStreet size=512 25,123.5 25,239.9 +0.5%
MultiFocusBench.eoFold_powerEach size=4 83.9 84.3 +0.5%
AvroJsonBridgeBench.naiveWideToJson - 1,880.1 1,888.7 +0.5%
PowerSeriesBench.naive_powerEach size=4 25.0 24.9 -0.5%
FoldBench.mFoldPrices size=64 156.2 155.5 -0.5%
ZioDiBench.eoServiceGet - 5.3 5.4 +0.4%
MultiFocusBench.eoModify_multiFocus size=1024 50,873.1 51,100.4 +0.4%
OrderJsoniterBench.monocleModifyStreet size=8 3,168.7 3,182.6 +0.4%
OrderJsoniterBench.naiveModifyStreet size=512 156,774.7 157,462.8 +0.4%
PowerSeriesNestedBench.eoModify_nested size=4 697.0 694.0 -0.4%
KyoDiBench.eoEnvFocus - 84.7 84.3 -0.4%
AvroBytesBench.naiveReadPartner - 2,022.3 2,013.6 -0.4%
ZioDiBench.handServiceGet - 14.0 13.9 -0.4%
OrderAvroBench.eoModifyStreet size=512 111.6 111.1 -0.4%
ReviewBench.eoReverseGet_0 - 2.4 2.4 -0.4%
PlatedBench.eoUniverseJson n=64 2,976.7 2,989.2 +0.4%
MultiFocusBench.eoFold_powerEach size=32 376.0 377.5 +0.4%
PlatedBench.mUniverseJson n=4096 3,289,760.8 3,276,027.6 -0.4%
PowerSeriesPrismBench.naive_sparse size=128 288.6 287.4 -0.4%
FoldBench.mFoldPrices size=8 26.5 26.4 -0.4%
OptionalBench.eoModify_0 - 21.1 21.2 +0.4%
PowerSeriesNestedBench.naive_nested size=1024 21,724.6 21,812.8 +0.4%
OrderCirceBench.monocleStreet size=8 3,202.0 3,189.1 -0.4%
GenericsBench.rawLensModify - 2.2 2.3 +0.4%
PlatedBench.visitorTransformDeep n=64 425.4 423.7 -0.4%
PowerSeriesBench.monocle_powerEach size=1024 60,285.1 60,044.2 -0.4%
CapsBench.getDerivedHeld - 2.3 2.3 +0.4%
KyoDiBench.eoMapDrillModify - 38.8 39.0 +0.4%
GenericsBench.genPrismGetHit - 2.1 2.1 -0.4%
AffineFoldBench.eoGetOption_0_empty - 0.9 0.9 -0.4%
PowerSeriesBench.naive_powerEach size=4096 23,282.7 23,193.3 -0.4%
AvroJsonBridgeBench.eoWideToAvro - 875.0 871.7 -0.4%
MultiFocusBench.naive_listMap size=4 32.9 33.1 +0.4%
OrderJsoniterBench.eoSumPrices size=64 10,842.4 10,802.3 -0.4%
OptionalBench.eoModify_loyalty - 16.6 16.6 -0.4%
AffineFoldBench.eoGetOption_loyalty_empty - 1.0 1.0 -0.4%
OrderCirceBench.naiveNames size=8 3,346.5 3,334.5 -0.4%
OrderJsoniterBench.naiveReadStreet size=8 1,770.8 1,777.1 +0.4%
OrderCirceBench.naiveNames size=512 193,419.1 194,109.5 +0.4%
GetterBench.mGet_6 - 28.1 28.2 +0.4%
PowerSeriesNestedBench.eoModify_nested size=256 17,605.9 17,668.3 +0.4%
PowerSeriesBench.naive_powerEach size=64 395.1 396.5 +0.4%
FoldBench.mFoldMap size=8 20.9 21.0 +0.4%
MultiFocusBench.eoFold_powerEach size=256 3,120.4 3,109.5 -0.3%
OrderCirceBench.eoNamesIor size=64 33,693.2 33,577.1 -0.3%
TraversalBench.eoFoldPrices size=64 155.4 154.9 -0.3%
OrderCirceBench.naiveStreet size=64 21,287.6 21,215.2 -0.3%
OrderCirceBench.naiveStreet size=8 3,182.6 3,172.3 -0.3%
PlatedBench.eoTransformExpr n=64 1,995.0 1,988.5 -0.3%
AvroBytesBench.naiveReadCountry - 1,688.3 1,682.9 -0.3%
KyoDiBench.handEnvUse - 84.1 84.4 +0.3%
OrderAvroBench.eoModifyStreet size=8 111.0 110.7 -0.3%
CapsBench.modifyDeepCap - 29.6 29.7 +0.3%
OrderCirceBench.eoNames size=64 30,596.4 30,689.4 +0.3%
PowerSeriesPrismBench.naive_sparse size=8 24.9 25.0 +0.3%
KyoDiBench.handMapGet - 1.9 1.9 +0.3%
OrderAvroBench.monocleModifyNames size=64 9,046.2 9,019.1 -0.3%
OrderAvroBench.eoModifyNames size=8 574.8 573.1 -0.3%
PowerSeriesNestedBench.naive_nested size=16 364.3 365.3 +0.3%
PrismBench.mGetRightPresent - 2.4 2.4 -0.3%
TraversalBench.eoFoldNested size=8 49.3 49.4 +0.3%
PowerSeriesNestedBench.eoModify_nested size=64 4,555.4 4,542.9 -0.3%
CompositionBench.buildLens3 - 21.4 21.4 -0.3%
PowerSeriesBench.eoModify_powerEach size=64 789.3 791.4 +0.3%
OrderCirceBench.monocleNames size=64 24,333.5 24,397.0 +0.3%
MultiFocusCollectBench.eoModify_multiFocusTuple3 - 16.4 16.4 -0.3%
JsoniterBench.jReplaceId size=512 3,794.3 3,784.6 -0.3%
PowerSeriesPrismBench.naive_sparse size=2048 4,972.2 4,984.7 +0.3%
PrismBench.eoGetRightAbsent - 1.0 1.0 +0.2%
ClickRecordBench.encode_hand - 141.9 142.2 +0.2%
GetterBench.eoGet_6 - 28.6 28.5 -0.2%
OrderAvroBench.monocleReadStreet size=64 3,240.3 3,232.9 -0.2%
AvroEncodeRouteBench.eo_encodeToAny - 53.8 53.9 +0.2%
TraversalBench.mFoldPrices size=8 325.2 325.9 +0.2%
PrismBench.eoReverseGet - 2.3 2.3 -0.2%
CapsBench.foldMapCap - 21.5 21.5 +0.2%
PlatedBench.visitorUniverseDeep n=64 750.2 751.8 +0.2%
FoldBench.eoFoldMap size=8 21.5 21.4 -0.2%
IsoBench.mReverseGet - 3.1 3.2 +0.2%
PlatedBench.eoUniverseJson n=512 24,750.6 24,697.8 -0.2%
MultiFocusBench.naive_listMap size=32 250.5 251.0 +0.2%
PrismBench.eoGetRightPresent - 2.3 2.3 +0.2%
PlatedBench.visitorUniverseExpr n=64 800.6 798.9 -0.2%
OrderCirceBench.eoNamesIor size=512 256,052.6 255,536.7 -0.2%
GenericsBench.genLensModify - 3.6 3.6 -0.2%
PowerSeriesPrismBench.monocle_sparse size=2048 106,871.8 106,663.7 -0.2%
ZioDiBench.handServiceReplace - 74.0 74.1 +0.2%
OrderAvroBench.naiveModifyNames size=512 59,555.7 59,440.2 -0.2%
MultiFocusBench.eoModify_powerEach size=4 190.5 190.8 +0.2%
AvroJsonBridgeBench.eoWideToJson - 682.8 681.5 -0.2%
PlatedBench.visitorUniverseExpr n=512 6,541.5 6,529.2 -0.2%
TraversalBench.eoModify size=64 843.3 841.7 -0.2%
OptionalBench.mModify_0_empty - 1.4 1.4 +0.2%
AffineFoldBench.mGetOption_0 - 0.8 0.8 -0.2%
IsoBench.eoReverseGet - 3.0 3.0 -0.2%
KyoDiBench.handMapDrillModify - 33.5 33.6 +0.2%
ReviewBench.eoReverseGet_6 - 34.1 34.0 -0.2%
CapsBench.foldMapDerivedHeld - 22.1 22.2 +0.2%
SchemesBench.handCata - 12,594.6 12,572.2 -0.2%
SchemesBench.handHylo - 10,780.6 10,798.9 +0.2%
CapsBench.foldMapDirect - 21.5 21.5 -0.2%
OrderJsoniterBench.eoSumPrices size=512 87,978.5 88,122.0 +0.2%
OrderJsoniterBench.nativeReadStreet size=64 4,263.2 4,256.3 -0.2%
PowerSeriesBench.eoModify_powerEach size=16 257.9 258.3 +0.2%
GetterBench.mGet_3 - 9.0 9.1 +0.2%
MultiFocusCollectBench.eoCollectMap_zipMean - 122.4 122.6 +0.2%
FoldBench.eoFoldMap size=64 157.3 157.0 -0.2%
GenericsBench.genLensGet - 1.0 1.0 +0.2%
CompositionBench.reuseLens3 - 45.4 45.3 -0.1%
SetterBench.eoModify_3 - 11.2 11.2 -0.1%
JsoniterBench.jReplaceId size=8 107.5 107.3 -0.1%
OptionalBench.eoModify_3 - 57.7 57.6 -0.1%
PlatedBench.eoUniverseExpr n=64 1,788.5 1,791.1 +0.1%
CompositionBench.buildLens6 - 38.7 38.7 -0.1%
PowerSeriesBench.eoModify_powerEach size=256 3,004.9 3,009.0 +0.1%
PowerSeriesBench.naive_powerEach size=1024 6,721.9 6,730.9 +0.1%
MultiFocusBench.naive_sumQty size=32 58.3 58.3 -0.1%
ReviewBench.eoReverseGet_3 - 19.4 19.4 +0.1%
ReviewBench.naiveBuild_6 - 11.0 11.0 +0.1%
TraversalBench.mFoldPrices size=64 2,060.6 2,063.0 +0.1%
SetterBench.mModify_6 - 64.3 64.2 -0.1%
PlatedBench.eoTransformExpr n=512 15,957.2 15,939.5 -0.1%
TraversalBench.mModify size=8 283.9 283.6 -0.1%
TraversalBench.eoFoldPrices size=8 25.6 25.7 +0.1%
GenericsBench.handPrismModifyHit - 2.8 2.8 -0.1%
CapsBench.getDeepDirect - 1.3 1.3 -0.1%
CompositionBench.reuseLeaf - 3.0 3.0 -0.1%
JsoniterBench.jMiss size=8 167.7 167.9 +0.1%
PlatedBench.visitorTransformExpr n=512 7,554.2 7,547.3 -0.1%
OptionalBench.mModify_3 - 71.3 71.2 -0.1%
PowerSeriesNestedBench.naive_nested size=256 5,231.8 5,236.3 +0.1%
GenericsBench.genPrismModifyHit - 2.9 2.9 +0.1%
OptionalBench.mModify_0 - 21.6 21.6 -0.1%
OrderCirceBench.eoStreetIor size=64 1,033.0 1,033.9 +0.1%
PowerSeriesBench.eoModify_powerEach size=4096 51,904.3 51,947.6 +0.1%
PrismBench.mRightReverseGet - 2.6 2.6 +0.1%
PowerSeriesPrismBench.naive_sparse size=512 1,172.7 1,173.6 +0.1%
AffineFoldBench.mGetOption_loyalty - 1.0 1.0 +0.1%
OptionalBench.mReplace_0 - 3.7 3.7 +0.1%
GetterBench.eoGet_3 - 16.2 16.2 -0.1%
PlatedBench.eoUniverseDeep n=64 1,869.5 1,868.2 -0.1%
OrderAvroBench.naiveReadStreet size=512 25,124.5 25,108.4 -0.1%
PrismBench.mGetOptionAbsent - 1.0 1.0 -0.1%
CapsBench.getCap - 1.2 1.2 -0.1%
PlatedBench.eoTransformDeep n=4096 118,520.6 118,590.0 +0.1%
JsoniterBench.jSumPrices size=64 10,905.8 10,911.9 +0.1%
OrderAvroBench.eoReadStreet size=64 35.3 35.3 +0.1%
OptionalBench.eoReplace_0 - 4.5 4.5 -0.1%
SetterBench.eoModify_6 - 23.7 23.7 +0.1%
PowerSeriesBench.naive_powerEach size=16 101.3 101.3 -0.0%
OrderAvroBench.eoModifyStreet size=64 110.5 110.5 -0.0%
LensBench.eoModify - 4.0 4.0 -0.0%
AvroJsonBridgeBench.naiveClickToJson - 2,919.0 2,917.7 -0.0%
GenericsBench.handPrismGetHit - 2.0 2.0 +0.0%
GenericsBench.rawLensGet - 0.6 0.6 +0.0%
OrderAvroBench.eoModifyNames size=64 4,129.5 4,128.2 -0.0%
CompositionBench.reuseLens1 - 16.5 16.5 -0.0%
PlatedBench.eoUniverseExpr n=4096 98,259.8 98,236.8 -0.0%
OrderAvroBench.naiveReadStreet size=64 3,221.4 3,222.0 +0.0%
FoldBench.eoFoldPrices size=64 155.4 155.5 +0.0%
CompositionBench.reuseLensOptional3 - 57.6 57.6 -0.0%
CapsBench.modifyDeepDirect - 33.1 33.1 +0.0%
AvroBytesBench.naiveModifyPartner - 2,920.3 2,919.9 -0.0%
FoldBench.mFoldPrices size=512 2,112.8 2,113.0 +0.0%
AffineFoldBench.mGetOption_3 - 9.7 9.7 -0.0%
AvroBytesBench.naivePassthroughPayload - 4,886.9 4,886.4 -0.0%
MultiFocusCollectBench.naive_listSum - 29.6 29.6 +0.0%

New (head only, not diffed): SchemesBench.drosteApo, SchemesBench.drosteApoGraft, SchemesBench.drosteFutu, SchemesBench.drosteHisto, SchemesBench.drostePara, SchemesBench.eoApo, SchemesBench.eoApoGraft, SchemesBench.eoFutu, SchemesBench.eoHisto, SchemesBench.eoHyloM, SchemesBench.eoPara, SchemesBench.eoRefoldCross, SchemesBench.eoRefoldManual

base_sha: 51a0be009be07f57b1f998da3c0aa4dc83d7fcf0 · head_sha: d703be5bbcc45aa4a68a36c0e041a30ee1fa215e · jdk: temurin-21 · runner: ubuntu-22.04 · jmh_params: -i 3 -wi 2 -f 1 -t 1 -foe true -prof gc -rf json -jvmArgsAppend -XX:-ResizeTLAB -jvmArgsAppend -Xms1g -jvmArgsAppend -Xmx1g · profile: pr:-i3-wi2-f1-t1-gc-rt1g

@kryptt kryptt changed the title feat(schemes): typed recursion schemes — cata/ana/hylo + the zoo (BiAffine, Gather/Scatter, para/apo/histo/futu, fused cross, M drivers) feat(schemes): typed recursion schemes as composable optics — the zoo (para/apo/histo/futu, zygo/mutu, fused refolds, M drivers), Graft[Affine] build seam, fused cross Sep 4, 2026
kryptt added a commit that referenced this pull request Sep 4, 2026
- docs/research/2026-06-15-typed-schemes-bibliography.md: anchor paper
  (O'Connor's Multiplate, arXiv:1103.2841) + its relevant references and
  citations, the recursion-schemes canon, and the Scala-ecosystem
  implementations, each mapped to what PR #24's surface claims.
- docs/plans/2026-06-15-001-cleanup-typed-schemes-merge-plan.md: the
  merge-blocking cleanup (schemes.md teaches the retired 2-arg algebra API
  and a removed zoo.Gather type — the docs build is the red CI gate) and
  the non-blocking polish items, sequenced.
kryptt and others added 11 commits October 1, 2026 00:05
…loF)

Opt-in, type-safe complement to #23's PSVec-driven Schemes.cata/ana/hylo.
The user supplies a pattern functor F[_] + Traverse[F] and hand-writes a
Basis (Project[F,S] / Embed[F,S]); algebras then pattern-match F's NAMED
constructors instead of indexing an erased PSVec[AnyRef].

- Basis.scala: Project/Embed (+ combined Basis) type classes with coherence laws.
- Schemes.{cataF,anaF,hyloF,fLayer}: a cats.Eval trampoline over Traverse[F]
  (the Plated.rewrite precedent / droste's stack-safe hyloM shape). cataF/hyloF
  return DirectGetter, anaF returns Review — Direct-carried, so they compose via
  andThen/cross with NO new core carrier instances. fLayer realizes the project/
  embed layer as Optic[S,S,S,S,Forget[F]] (no Optic trait change).
- Empirically stack-safe to 10^6 for all three (anaF the OOM frontier), plus a
  wide-and-deep RoseF; pure hylo law + Project/Embed coherence (BinF and RoseF)
  under ScalaCheck; cataF cross-checked against #23's Plated cata. #23 untouched.
- mdoc docs section + top-of-page caveat pointing at the typed path.

The Eval trampoline is O(depth) but allocation-heavy, so the module forks its
tests with -Xmx2g (the 10^6 typed + #23 cases together OOM sbt's in-process heap).

Deferred follow-ups: JMH B/op vs droste basic (Eval-vs-heap driver decision);
deriving Project/Embed from the S<->F correspondence; a pure F[A]=>A overload.

Plan: docs/plans/2026-06-09-002-feat-typed-recursion-schemes-plan.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the eoF (typed Eval) rows to SchemesBench alongside eo (PSVec) / droste /
hand, and upgrades the bench fixture's Functor[BinF] to a Traverse[BinF] (+ a
Basis[BinF, Bin]) so the typed schemes run on the same 2^12 workload.

Result (-prof gc, B/op, 8191 nodes): the Eval trampoline is ~8-16x droste basic
(cata 15.7x, hylo 7.9x, ana 8.2x) — ~316 B/node of Eval machinery. It MISSES the
allocation-parity bar, so it ships as the correct/type-safe/stack-safe v1 with
allocation as a documented tradeoff; the explicit typed-heap-machine driver
becomes a tracked follow-up (no longer a v1-conditional). droste basic is neither
stack-safe nor optic-composable, so the comparison is not apples-to-apples.

Documents the table + decision in site/docs/benchmarks.md (new recursion-schemes
section) and resolves the Eval-vs-heap Open Question in the plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…chine

U6 profiling showed the Eval-trampoline cataF/anaF/hyloF at ~8-16x droste-basic
B/op (~316 B/node of Eval machinery). Replaced it with the pre-planned explicit
machine: the same <512-on-stack / heap-ArrayDeque hybrid as the PSVec engines,
keeping F typed at the algebra seam.

- foldLayered: foldLeft reads a layer's children into a per-node array (reused as
  the result accumulator, folded in place); the deep recursion runs on the
  machine; map rebuilds the typed F[result] the algebra destructures.
- rebuildLayer: leaf layers (0 N-slots) skip the rebuild via a phantom F[N]->F[R]
  recast — half the nodes in a binary tree. Shared EmptyAnyRefs for leaf arrays.
- Any lawful Traverse[F] now works: stack-safety is the machine's, not the user's
  foldRight, so the Eval-lazy-foldRight caveat is gone and the -Xmx2g test fork is
  removed (10^6 runs in the default heap, like #23's PSVec cases).

B/op vs droste basic (SchemesBench, 8191 nodes): cata 2.2x, hylo 1.1x (parity),
ana 1.6x (now beats eo's own PSVec ana) — a ~7x allocation cut from the Eval
driver. Residual cata gap is inherent native-Bin-vs-Fix (eo projects a layer per
node; droste's unfix is free). Still typed + stack-safe, which droste basic isn't.

Updates site/docs/benchmarks.md + schemes.md and resolves the Eval-vs-heap Open
Question in the plan (heap machine ships; no follow-up needed for parity).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a worked example + test: compose a lens chain (innerL.andThen(treeL):
Lens[Doc, Bin]) to focus a recursive field inside a record, fold it with cataF
(wrapping the lens read in a Getter, per eo's read-only-composition idiom), and
write the field back through the same composed lens. Demonstrates the "schemes
are optics" value prop concretely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fLayer adapts to main's opaque ForgetK carrier (wrap via ForgetK.apply,
unwrap via .value; def-encoded to/from per the val→def encoding).
DirectGetter references follow the Getter rename.

docs/plans/2026-06-11-001: the twice-reviewed plan growing this PR into
the recursion-scheme zoo (BiAffine carrier, Decor family, para/apo/
histo/futu, fused cross, M-generic driver).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dation

Affine's data shape worn on the build seam: Done = "this slot is already
finished — do not call the coalgebra" (payload meaning pinned per optic
value via Fst[X]: apo grafts a finished subtree, futu unrolls a prebuilt
layer); Step = keep going (focus + one-F-layer leftover via Snd[X]).

Carrier-owned instances: ForgetfulFunctor/Fold/Traverse, PartialAccessor,
and the new Graft capability (done/step build-channel injection — the
vocabulary generic scheme drivers use). No AssociativeFunctor and no
Composer bridges: the composition-matrix row is an explicit follow-up.

Laws (cats-eo-laws + OpticsLawsSpec): instance laws mirroring AffineLaws
plus graft-channel coherence — Done has no focus, folds empty, is
map-inert; Step carries exactly its focus. BiAffineSpec pins the
citizen-level graft-finality equations (from(Done(w)) == w, to/from
round-trips) against a toy full citizen with X = (W, F[W]).

Plan: docs/plans/2026-06-11-001 stage 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hand-rolled, droste-style (no cats-free dep, no Eval suspension fields
the foldLayered machine never suspends on). Attr = cofree-without-
laziness (a result decorating each node's full history); Coattr =
free-without-suspension (a seed to expand, or a prebuilt layer unrolled
without consulting the coalgebra). histo's O(n) decoration space is
documented as inherent, not hidden.

Plan: docs/plans/2026-06-11-001 stage 2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…naF re-derived

The gather/scatter pair of a generalized scheme, reified as optic values
over the BiAffine carrier, sides pinned in the type:

- DecorGather[F, W, A]  = Optic[Unit, W, Unit, A, BiAffine] {X = (Unit, F[W])}
  fold side, build-only: from = gather (histo's gather IS the Attr
  constructor); vestigial read throws (Unfold.algebra precedent).
- DecorScatter[F, W, A] = Optic[W, W, A, A, BiAffine] {X = (F[W], Unit)}
  unfold side, full citizen: to = scatter (Step = call the coalgebra,
  Done = prebuilt layer, no call), from on Step = the POINTED UNIT (gana's
  pure: ana = id, apo = Right, futu = Coattr.Pure) — unit law
  to(from(Step((), a))) == Step((), a).

Named values: Decor.cata/para/histo (gather) + Decor.ana/apo/futu
(scatter). cata/ana/histo/futu are identity-stable singletons; the
generic drivers recognise cata/ana by identity and take the direct
decoration-free engine path, so the re-derived cataF(alg)/anaF(coalg)
are byte-identical in behaviour AND cost. Generic-route honesty
documented: Decor.apo unrolls grafts via Project (distApo, O(graft)) and
Decor.para re-embeds — the native apoF/paraF engines (next stage) avoid
both.

New generic overloads: cataF(decor)(galg) (interior gather∘galg, root
galg alone — droste's gcata shape) and anaF(decor)(gcoalg) (scatter per
slot, root through the unit). Fully typed against the X refinements —
no casts at the decoration seam.

DecorLawsSpec: per-value gather/scatter equations, unit laws, vestigial
throws, identity-stability, fresh-user-id-value vs fast-path agreement
(both sides), histo heads-only == cata, futu two-layers-per-step build.

Plan: docs/plans/2026-06-11-001 stage 3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, histoF, futuF

paraF: native route — the machine already walks real S nodes and keeps
each frame's projected layer, so child slots pair (original subterm,
result) positionally via rebuildLayerPaired. No per-node re-embed
(droste's Gather.para must reconstruct the subterm it threw away).

apoF: native O(1) graft via foldLayeredOr, foldLayered's graft-aware
sibling — Left(s) slots are already-finished results, placed BY
REFERENCE: never recursed, never projected. (droste's scatter-apo
re-walks grafts through project, O(graft) per graft.)

histoF/futuF: definitional one-liners on the generic decorated drivers
at Decor.histo / Decor.futu — the proof the Decor family is correctly
shaped.

SchemesZooSpec: degeneration laws (para-ignoring-subterms == cata,
never-grafting apo == ana, heads-only histo == cata, single-layer futu
== ana), the graft law (grafted subtree present by `eq` reference — the
bench-noise-immune form of the O(1) claim), a real course-of-value
algebra (grandchildren through history), and stack-safety to 10^6 per
member including deep Coattr chains.

Plan: docs/plans/2026-06-11-001 stage 4 (zoo half).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kryptt and others added 29 commits October 1, 2026 00:05
Propagates the renames that earlier commits left dangling in docs/comments:
  - F-suffix: `eoHyloF` → `eoHylo`, "cataF/anaF benches" → "cata/ana"
  - Decor → Gather/Scatter (BiAffine scaladoc, BiAffineLaws, BiAffineSpec,
    benchmarks.md decoration-route note)
  - benchmarks.md cross rows: `eoCrossFused`/`eoCrossMaterialized` →
    `eoRefoldCross`/`eoRefoldManual`, and corrected the now-FALSE "fused cross
    beats materialising" claim — post duality-fix `ana.cross(cata)` materialises
    (885 577 B/op, == manual), `hylo` is the fusion (361 385); the X-indexed
    `proto` spike is what recovers hylo cost through `cross`.

Comment/doc only; tests + mdoc green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o/chrono as fusion

Reset the schemes module around the thesis: a recursion scheme is an Optic
whose existential X is the index of the recursion, and the (co)free (co)monads
are the universal indices.

- New `Scheme[X, A]` carrier (opaque, = A): keeps the (co)algebra alive so the
  fused seams are reachable (Direct's collapsed closures cannot fuse).
- Citizens in `schemes.zoo`: Cata (X = Nothing, node-blind F[A] => A),
  Ana (X = S), Hylo, Histo (X = Attr = cofree), Futu (X = Coattr = free).
- hylo is no longer a primitive: `ana.cross(cata)` fuses into the zero-S refold
  over the Scheme carrier. chrono = `futu.cross(histo)` is hylo at the universal
  indices (free -> cofree), also fusing; `Schemes.{hylo,chrono}` need only
  Traverse[F] (no Project/Embed) — the compile-time deforestation proof.
- cata is now genuinely node-blind (was paramorphism-flavored `(S, F[A]) => A`).
- Machine fix (type safety, not less): rebuildLayer now runs inside the engine,
  so the raw union `Array[Slot[N,R]]` never escapes into concretely-instantiated
  code. Algebras receive a typed F[R]. Fixes a latent ClassCastException when
  both slot halves are Serializable (Coattr/Attr) — the union erased to
  Serializable[] and failed the Object[] checkcast.
- cross is the sole fusion seam (no andThen alias — keeps andThen's
  compose-at-focus meaning unambiguous).

FusionSpec/ChronoSpec witness deforestation via an instrumented Basis
(project/embed never called on the fused path). 29/29 green.

Stripped the prior decorated/effectful surface (para/apo/gather/scatter/
cataM/anaM/hyloM) and its specs; they return as follow-ups off this spine.
Out of scope (still on the old API): benchmarks/*Schemes*, site/docs/schemes.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…→unfold dual

Completes the 2×2: refold vs metamorphism × trivial vs universal index.

- meta = cata.meta(ana) / Schemes.meta: fold an F-recursive S to a neck value A,
  then unfold a DIFFERENT G-recursive T. The direction-dual of hylo.
- metaChrono = histo.meta(futu) / Schemes.metaChrono: the same at the universal
  indices (cofree fold → free unfold). Degenerates to meta.
- New Meta[S, A, T] citizen, X = A (the neck). The non-trivial existential is the
  point: meta CANNOT fuse — fold over F and unfold over G ≠ F leave no
  project∘embed to cancel, so the neck is genuinely materialised. Honest mirror of
  Hylo's X = Nothing. The compile-time tell: meta keeps BOTH Bases where hylo
  drops them.
- meta hosts the seam on the fold (Cata/Histo, fold-first), dual to cross hosting
  on the unfold (Ana/Futu); distinct name, no andThen collision.

MetaSpec (6) uses a genuinely heterogeneous Bin --leafSum--> Int --spine--> Rose
(F = BinF, G = RoseF) and witnesses no-fusion via split counting bases: the
F-fold's project AND the G-build's embed both fire (contrast hylo: neither).
35/35 green, formatted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e barrier

Empirical refutation of the F=:=G fusion hypothesis: a same-functor metamorphism
(F = G = BinF, one Basis serving both the fold's Project and the unfold's Embed)
STILL calls both project and embed. The functor mismatch was only a sufficient
witness for no-fusion; the cause is the scalar neck — the fold's projects (input)
are never adjacent to the unfold's embeds (output), so no project∘embed cancels.
Contrast hylo, where ana's embed-step and cata's project-step share one layer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three groups off the cata/ana/hylo/histo/futu/chrono/meta spine:

1. Subterm-retaining schemes (X = the optic complement):
   - para  (Getter, X = F[(S,A)] — store-comonad complement): alg F[(S,A)] => A
     sees each child's original subterm, not just its summary. Degenerates to
     cata. (X is the writable-Lens complement; get-put is definitional, put-get
     needs algebra-coherence, so the lawful put stays a scoped follow-up — shipped
     as the sound read.)
   - apo   (Review, X = Either[S,A] — Prism residual): coalg A => F[Either[S,A]]
     grafts a finished subtree by reference (O(1), test pins `eq`). Build-side
     dual of para. Degenerates to ana.

2. Short-circuit / seed-reading refolds (both fuse → Hylo):
   - elgot   (coalg A => Either[B, F[A]] short-circuits) — wires the previously
     dead Machines.foldLayeredOr.
   - coelgot (alg (A, F[B]) => B reads the seed).
   Both degenerate to hylo.

3. Refold-quadrant diagonals (fuse, Traverse-only):
   - dyna   = ana.cross(histo)  (plain unfold → cofree fold)
   - codyna = futu.cross(cata)  (free unfold → plain fold; descriptive name)
   Added as cross overloads on Ana(Histo)/Futu(Cata) + Schemes ctors. Degenerate
   to hylo.

para re-projects each node to recover subterms (no engine change, no raw-Slot
escape). ZooExtendedSpec (11): degeneration laws, the distinguishing capability
of each, apo graft-by-reference, dyna/codyna == their seams, 10^6 stack-safety.
47/47 green, formatted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code-review follow-up addressing two challenges:

1. Scheme carrier was redundant. Direct[X,A]=A is value-identical and strictly
   more capable (it already has Accessor+ReverseAccessor PLUS ForgetfulFold/
   Applicative/Traverse + AssociativeFunctor). The original "Direct can't fuse"
   rationale conflated the Getter/Review *classes* (which store opaque closures)
   with the Direct *carrier* — fusion comes from the citizens carrying their
   (co)algebra + cross being a member, which is carrier-independent. Deleted
   Scheme.scala; all citizens now extend Optic[...,Direct]. Bonus: scheme reads
   compose with core Getters directly (getter.andThen(cata)) — the .readOnly
   bridge is gone.

2. One final class + file per scheme (uniform with the primitives). New:
   Chrono, Dyna, Codyna, Elgot, Coelgot, MetaChrono — each a final class storing
   its run/refold fn with the machine-wiring in its companion. Schemes is now a
   thin factory listing of one-line delegations. cross seams return the named
   type (Ana.cross(Histo)→Dyna, Futu.cross(Histo)→Chrono, Futu.cross(Cata)→
   Codyna, Histo.meta(Futu)→MetaChrono).

   Honesty: the fused-refold classes (Chrono/Dyna/Codyna/Elgot/Coelgot) share
   X=Nothing — fusion discards the index, so they are nominally-distinct named
   types, not distinct existentials. Documented as such rather than faking a
   per-refold X. The genuine indices stay with cata/para/histo/ana/apo/futu/meta.

   final-class-stores-fn (not an abstract base) is deliberate — matches core
   Getter/Review and avoids the documented ~1.8x megamorphic-dispatch penalty.

Closed two review gaps: a deep (>512) apo graft test exercising the heapWalk
Left arm, and a wide/variadic-functor (RoseF) para zip-alignment test. Bounded
the 10^6 para+apo test's peak heap (scope each tree) to kill a GC-pressure flake.

49/49 green, formatted. foldLayeredM kept as reserved (per decision).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ecorate

Functionally-equivalent cleanup pass (49/49 unchanged):

- ReadScheme / BuildScheme base shapes (SchemeShapes.scala) centralize the
  Direct carrier wrapping (to/from) into ONE place instead of respelling it in
  all 14 citizens — so the next carrier change is a 2-file edit, not the 14-file
  churn the Scheme→Direct migration just paid by hand. Each citizen now supplies
  only its `read`/`write` fn + its `type X`. (The virtual read/write is one
  dispatch per O(n) fold — immaterial here, unlike core Getter's hot composed
  reads; documented.)
- Coattr.expand extracts the futu unrolling repeated in Futu/Chrono/Codyna/
  MetaChrono (4 copies → 1).
- Attr.decorate extracts the cofree-decorating combine repeated in Histo/Dyna/
  Chrono/MetaChrono (4 copies → 1).
- Deleted dead Machines.rebuildLayerPaired (orphaned when the engine moved
  rebuildLayer inside foldLayered; para re-projects instead).
- Dropped now-unused per-citizen imports (Optic/Direct live in the base);
  tightened run-field visibility to private where same-class-only.

Net -87 lines. Also added two review-gap tests last commit (deep apo graft,
variadic-functor para alignment) and bounded the 10^6 para+apo test's peak heap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complete the recursion-scheme zoo's two index towers and add the
orthogonal natural-transformation axis:

- zygo / mutu: the comonad-tower auxiliary rung (X = F[(B,A)] / F[(A,B)]);
  para is zygo at B=S, aux=embed. mutu generalises zygo (mutual recursion).
- cozygo / comutu: the build-side g-apo duals (X = Either[B,A] / Either[A,B]).
- prepro / postpro: keep the trivial index, decorate the layer optic with an
  accumulating natural transformation η : F ~> F (O(n·depth); η = id recovers
  cata/ana). The axis orthogonal to the (co)monad towers.

ghylo deliberately omitted — the v2 biaffine-zoo plan rejected free-range
generic g-schemes.

All wired through Schemes.* factories reusing the shared foldLayered engine;
ZooTowersSpec pins each scheme's degeneracy-to-base law + a real-behaviour
case + stack-safety.

schemes/test now forks with -Xss8m: the 10^6 stack-safety specs take a bounded
512-frame on-stack prefix before the heap walk, which overflows specs2's
small-stack parallel pool threads under the heavier suite. Fork keeps the
realistic 10^6 tests AND parallel execution rather than shrinking either.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t engine

The M-class needed no engine work: Machines.foldLayeredM (the Monad[M]-lifted
walk, tailRecM-driven, stack-safe, Or-shaped for graft/short-circuit) already
existed but had zero callers. This adds only the public surface, with no code
explosion:

- Two function-wrapping citizens carry the WHOLE M-zoo: FoldM (read side:
  cataM/paraM/histoM/hyloM/chronoM) and BuildM (build side: anaM/apoM/futuM).
  The recursion index isn't erased by the consolidation — it rides each
  citizen's phantom type param XI (cataM = FoldM[..., Nothing], paraM =
  FoldM[..., F[(S,A)]], ...), so the X-as-index thesis holds per factory.
- The pure layer decorations (Attr.decorate, Coattr.expand, the para zip, the
  apo Either) compose with M for free, so each *M factory is a one-liner over
  foldLayeredM.
- Contract documented on the family: M must be single-pass / linear /
  sequential (Id, Eval, State, IO) — a branching/replaying M corrupts the
  engine's mutable walk state.

ghylo-style free-range generics stay rejected (v2 biaffine plan); the *M
schemes are named, consistent with the rest of the zoo.

SchemesMSpec pins each scheme's M=Id agreement with its pure twin, Option
short-circuiting (a real effect the pure scheme can't express), and empirical
Eval stack-safety folding/building 10^6-deep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…↔Plated bridge

Project/Embed/Basis (the S↔F pattern-functor correspondence) move from the
schemes module into core (optics package), so core's Plated can derive from
them. A top-level `export optics.{Basis, Embed, Project}` in the schemes package
keeps every scheme citizen referring to them unqualified — no per-file churn.

New: Plated.fromBasis[F, S] (using Traverse[F], Project[F,S], Embed[F,S]) — the
same Basis that drives cata/ana now also drives core's Plated recursion
combinators (children/universe/transform/rewrite) and the MultiFocus[PSVec]
carrier. A scheme's single layer IS this self-traversal; Plated is its
non-recursive face. Children are built copy-free via ObjArrBuilder +
PSVec.unsafeWrap (fresh per call, honouring fromChildrenVec's contract).

PlatedBridgeSpec pins it end-to-end: one Basis[BinF, Bin] feeds both
Plated.children/universe/transform AND Schemes.cata, and they agree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-traversal

fLayer was a read-only Forget[F] optic. Re-carrier it on core's
MultiFocus[F][X, A] = (X, F[A]) — the Traversal/AlgLens/Grate carrier — with
to(s) = ((), project(s)), from((_, fs)) = embed(fs), X = Unit (the F-shape rides
inside the foci, so embed needs no extra leftover).

Now that the layer optic shares the carrier of Plated.plate and Traversal.each,
it composes with the rest of core and gains the write half for free: .foldMap
(read foci), .modify / .replace (rewrite immediate children), .modifyA / .all
(effectful) — where the Forget spelling was read-only. fLayer is one layer;
Plated.fromBasis (Step 1) is its recursive face.

SchemesSpec's fLayer cases move to the MultiFocus carrier and add a .modify case
proving the read+write upgrade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
para is unconditionally a Getter; this promotes it to a core GetReplaceLens
(Optic[S,S,A,A,Tuple2]) so a recursion scheme reads AND writes, and composes
with hand-written/derived Lenses on the fused Tuple2 path.

The lawfulness boundary, handled honestly: a fold result is not in general a
recoverable component of S, so the store-comonad "auto-put" (re-embed the
retained subterms, X = F[(S,A)]) makes get-put definitional but leaves put-get
conditional on the algebra having a coherent inverse. Rather than ship a Lens
that is only conditionally lawful, paraLens takes the coherent put-direction as
a parameter — get/enplace then obey the ordinary Lens laws. (The automatic
decorated optic stays read-only, a follow-up enrichment of fLayer.)

ParaLensSpec pins all three Lens laws on a coherent instance (leftmost-leaf:
get = a left-spine paramorphism, enplace = rewrite that leaf) and shows it
composing under a core Lens (Box → leftmost leaf).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BiAffine (the build-seam carrier — Done = finished/graft, Step = keep-going)
shipped its forgetful instances + Graft but, by design, NOT its composition row:
its own scaladoc said "AssociativeFunctor[BiAffine] ... deliberately NOT shipped
here". That made it the one carrier that couldn't compose — latent, like
foldLayeredM was.

This adds AssociativeFunctor[BiAffine], a mechanical build-side mirror of
Affine.assoc (Done ↔ Miss, Step ↔ Hit, identical Z), so the generic
Optic.andThen now resolves for BiAffine-carried optics: biaffine.andThen(
biaffine) type-checks and runs, with Done short-circuiting (a finished outer
slot ends the composition) and Step threading the focus through inner and
recombining the one-layer contexts.

This is the keystone the brainstorm's candidate #4 needs — "composing decorated
schemes = composing their Xs" — and the prerequisite for re-carriering the
build-side schemes (apo/futu) onto BiAffine so they actually consume it.

BiAffineSpec gains a composition section reaching all three arms (outer Done,
Step∘Step, Step∘Done) with round-trip + short-circuit checks; CompositionMatrixSpec
(121) confirms no resolution regression. Cross-carrier Composer bridges into
BiAffine remain follow-up, alongside those scheme citizens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add Composer[Tuple2, BiAffine] (tuple2biaffine) and Composer[Either, BiAffine]
(either2biaffine), the build-side mirrors of Affine's tuple2affine/either2affine:
a Lens lifts to an always-Step; a Prism maps Right→Step, Left→Done. With
BiAffine.assoc (same-carrier) these complete BiAffine's composition-matrix row,
so lens/prism compose into BiAffine-carried decorations (e.g. the apo scatter).

BiAffineSpec gains round-trip checks for both bridges via .morph[BiAffine];
CompositionMatrixSpec (121) and the full aggregate confirm no resolution
regression from the new givens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
apo's per-slot residual is now a genuine BiAffine-carried optic. Schemes.apoScatter
(Apo.scatter) = Optic[Either[S,A], Unit, A, Unit, BiAffine]{ X = (S, Unit) }:
Left(s) → Done(s) (the O(1) graft, Fst[X] = S), Right(a) → Step((), a) (keep
unfolding). The X is refined/exposed so Fst[X] reduces at use sites.

Apo's engine now constructs AND consumes its decision through this optic — every
slot goes residual → scatter.to → Done/Step → engine — so apo speaks the carrier
BiAffine was written for. The shared pure foldLayeredOr engine still recurses over
an Either at its boundary (it's shared with elgot/cozygo); the Done/Step decision
collapses onto that boundary at the last step.

As a real Optic[…, BiAffine] value, apoScatter composes via BiAffine.assoc (proven)
and the either2biaffine bridge. ApoScatterSpec pins Done/Step semantics, three
composed arms (Step∘Step, outer-Done short-circuit, inner-Done), and that the
re-carriered scheme still builds with grafts intact; full apo behaviour suite
(ZooExtendedSpec) unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…'s factoring

Two surface-preserving dedups the f1a3266 audit didn't reach (it predates the
M-family and BiAffine work):

1. M-lifting adapters. The 8 *M factories re-inlined the pure side's layer
   wiring lifted into M. Mirror it: Coattr.expandM / Attr.decorateM (M-twins of
   expand/decorate) + three private lifters in Schemes (liftProject / liftCoalg /
   embedM) for the project/coalg/embed wiring. Each *M factory now reads as terse
   as its pure twin, and the Right/pure/Either wrapping lives in one place.

2. Machines.buildLayered — foldLayered with the combine fixed to embed, the shape
   every non-grafting unfold shares. ana/futu/cozygo/comutu and the unfold halves
   of meta/metaChrono drop their repeated `(_, fr) => E.embed(fr)` (6 sites).

No public surface change; SchemesMSpec / ZooSpec / ZooExtendedSpec (96) and the
full aggregate (325) pass unchanged. Left the 10 function-wrapping citizens and
apoScatter's engine round-trip alone (merging would cost named-type surface /
needs the shared-engine re-carrier respectively).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hylo) is gone

schemes-laws existed solely to law-check the untyped PSVec hylo fusion
(Schemes.hylo/ana/cata), which the typed-recursion-schemes work deletes.
The branch predates the module, so the rebase could not remove it; this
reconciliation commit does: module directory, build.sbt definition +
aggregate + mutationAll entry, CLAUDE.md rows, and the regenerated
ci.yml cache paths. Typed-scheme laws live in schemes' own test suite
(SchemesFLawsSpec / SchemesFMSpec / zoo laws). A typed-API HyloLaws
rewrite is the natural follow-up if the law artifact should return.
- docs/research/2026-06-15-typed-schemes-bibliography.md: anchor paper
  (O'Connor's Multiplate, arXiv:1103.2841) + its relevant references and
  citations, the recursion-schemes canon, and the Scala-ecosystem
  implementations, each mapped to what PR #24's surface claims.
- docs/plans/2026-06-15-001-cleanup-typed-schemes-merge-plan.md: the
  merge-blocking cleanup (schemes.md teaches the retired 2-arg algebra API
  and a removed zoo.Gather type — the docs build is the red CI gate) and
  the non-blocking polish items, sequenced.
…aws tree

- Schemes.scala / ChronoSpec / ParaLensSpec / SchemesMSpec: fix or inline
  the Scaladoc links that could not resolve ([[Optic]], [[FusionSpec]],
  [[Traversal.each]], [[Coattr]]/[[Attr]], [[Lens]], [[Machines.foldLayeredM]]).
- schemes-laws/: remove the empty untracked directory tree (can be
  recreated when the D5 discipline law specs get their own module).
The page taught the retired surface — node-supplied 2-arg algebras
((node, folded) => ...), a removed zoo.Gather type, 4-type-arg cata,
Forget[M]-carried FoldM consumed via .run — which failed the docs build
(10 mdoc errors, the red CI gate). Rewritten against the shipped API:

- algebras are node-blind 1-arg (F[A] => A / F[(S, A)] => A);
- zygo shown as the named citizen constructor Schemes.zygo[...](aux)(alg);
- the M family reads via .get / .reverseGet yielding M[...], hyloM the
  fused effectful spelling (no Kleisli andThen exists on the citizens);
- fLayer rides MultiFocus[F] (read+write), not Forget[F];
- the zoo table drops the Gather/Scatter vocabulary (named citizens on
  the BiAffine carrier; assoc + cross-carrier bridges shipped);
- the BiAffine section no longer claims the matrix row and elgot/coelgot
  are follow-ups — both shipped;
- lens-composition example uses the unambiguous spelling: Getter.andThen
  is 3-way ambiguous for Direct-carried citizens (recorded as cleanup C8).

sbt scalafmtCheckAll scalafmtSbtCheck docs/mdoc docs/laikaSite all green.
…ography pointers (C2)

- CHANGELOG [Unreleased]/Added: cats-eo-schemes (the typed zoo, fusion,
  M-family, paraLens, the removal of the untyped path), core BiAffine
  (+ assoc matrix row, cross-carrier bridges, Graft, apoScatter), and
  Basis-in-core with the Plated.fromBasis derivation.
- plans 2026-06-09-002 / 2026-06-11-001: point their reference sections
  at the new bibliography so the branch's citations live in one place.
…d citizens; drop the BiAffine clone

C8 — Getter.andThen overload tie:
A Direct-carried read-only citizen (the schemes' cata/ana/hylo —
Optic[A, Unit, C, Unit, Direct]) matched three Getter andThen overloads
at once (the any-carrier member, the re-homed read-only override, the
trait's same-carrier inline andThen); dotty called the ranking a
three-way draw and getter.andThen(cata) did not compile. Fixed with one
new most-specific member on Getter: andThen[C, D](Optic[A, Unit, C,
Unit, Direct])(using DummyImplicit): Getter[S, C] — the DummyImplicit
makes its parameter-list shape comparable with the other overloads so
the strictly-more-specific parameter type wins outright, returning the
concrete Getter so ascribed compositions type-check. Pinned by
GetterAndThenResolutionSpec (fused-Getter route, other-carrier PickFold
route, citizen route); the full CompositionMatrixSpec still passes.

BiAffine dropped (review comment from kryptt):
data/BiAffine.scala was a field-identical clone of Affine (Done/Step ↔
Miss/Hit) whose map/fold/traverse/assoc/composer instances mirrored
Affine's line for line. The build-seam reading of the arms — Miss =
finished slot (no coalgebra call), Hit = keep going — is the decoration
vocabulary, so it lives on Affine directly:
- new Graft[Affine] instance (done = Miss, step = Hit), the injection
  vocabulary the schemes' build-side citizens construct and consume;
- graft-finality laws folded into AffineLaws/AffineTests (finished arm
  is focus-free, map-inert, folds empty; step carries its focus);
- Apo.scatter / Schemes.apoScatter re-carriered onto Affine;
- tests: BiAffineSpec -> AffineBuildSeamSpec, ApoScatterSpec updated;
- BiAffineLaws/BiAffineTests deleted (folded into the Affine rule set);
- docs + CHANGELOG updated; the BiAffine name is left free for a
  genuinely two-sided-failure carrier if one is ever needed.

All gates: root/test 536 green, scalafmt/scalafix clean, mdoc 0 errors.
…catch); re-pin benchmarks at the merge candidate

C7's JMH re-pin on temurin@21 caught a real regression the dedup audit
(f1a3266) introduced: Para's combine re-projected each node and
materialized its child layer into a List per node (F.toList) —
1 409 788 B/op vs the pinned 557 945, pushing para past droste
(1.26x). Fixed by restoring the positional pairing as
Machines.foldLayeredSlot (slot-threading sibling of foldLayered whose
combine receives the machine's expanded layer + filled slot buffer;
heapWalkSlot is the cold-path twin) and rebuildLayerPaired over them.
Para now pairs off the layer the machine already holds — no re-project,
no per-node List: 557 947 B/op, back to the pinned 0.50x of droste.

Also caught by the re-pin:
- eoRefoldCross now FUSES: 361 387 B/op, byte-identical to hylo (the
  C8 overload fix routes ana.cross(cata) to the fused composite —
  decision 9's design intent; FusionSpec's no-intermediate-S witness
  covers it). The materializing contrast is the hand-written
  eoRefoldManual (885 589).
- benchmarks module had drifted off the shipped API (2-arg algebras,
  removed Gather/Scatter vocabulary, removed proto package): fixed
  SchemesBench/fixtures to 1-arg node-blind algebras, dropped the
  generic-route row with the vocabulary, deleted ProtoFusionBench
  (proto spike carrier is gone).

site/docs/benchmarks.md tables re-pinned to the merge-candidate sweep
(temurin@21, -f 1 -i 5 -wi 3); the two changed rows and their bullets
re-scoped (para regression + fix noted; cross-fusion bullet rewritten;
droste histo/futu re-measured).
…ed upcast, formatting

The concurrent force-push rebased the branch onto a newer main (#26,
Unfold): Miss dropped its second type parameter (Miss[A] extends
Affine[A, Nothing], re-typing subsumed by subtyping, widenB gone).
Adapts the C8/BiAffine-drop commit's touched files to that arity —
Graft[Affine] constructors, Apo.scatter, the spec toys (widenB check
rewritten as the allocation-free upcast it now is) — plus scalafmt on
the drifted files.
…t the phantom ns regression

The C7 fix restored para's positional subterm pairing through a slot-level
engine entry that handed the raw Array[Slot[N, R]] to the combine -
contradicting Machines' documented invariant (the raw Slot buffer never
leaves the engine) - and Para's own scaladoc still described the audited
re-project route.

- Machines: new `foldLayeredPaired` - the typed sibling of `foldLayered`,
  whose combine receives the node's layer with each child paired with its
  folded result (`F[(N, R)]`): para's shape, with no re-project and no
  per-node List. `foldLayeredSlot` and `rebuildLayerPaired` drop to
  `private`, so the slot buffer stays inside the file and the two
  `foldLayered*` drivers are the typed surface. Same walk, same per-node
  work - the pinned para B/op is unchanged by construction.
- Para: wired to `foldLayeredPaired`; scaladoc states the actual route.
- docs(benchmarks): the zoo table drops the ns/op column - B/op is the gate
  metric, and the mixed single-fork ns column showed a para "time
  regression" that focused runs do not reproduce. Stale results rewritten
  to the re-pinned numbers: para 0.50x, apo 0.68x, histo 1.54x, futu 1.25x,
  graft 280 vs 240, and `ana.cross(cata)` fusing (byte-identical to `hylo`)
  with the materialising spelling named as the manual pair. The
  Gather/Scatter generic-route bullet and the removed `proto` spike
  reference are gone.
…itness)

ParaRouteSpec counts `project` calls over a 7-node tree: a para fold must
peel each node exactly once (7), because the retained subterms come off the
layer the machine already peeled. The audited route - re-`project`ing each
node, or materializing its children into a List - peels every node twice
and allocates an extra layer per node, which is what let para's B/op climb
past droste unnoticed. Second case pins that the algebra really reads the
subterms (a left-leaf-weighted sum a plain cata cannot express).
@kryptt
kryptt force-pushed the feat/typed-recursion-schemes branch from b2122a3 to d703be5 Compare October 1, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experimental Exploratory / spike work — not intended for merge as-is help wanted Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant