cats-eo is an existential optics library for Scala 3, layered on
cats. One Optic[S, T, A, B, F] trait
parameterised over a carrier F[_, _] unifies every optic family;
cross-family composition goes through Composer[F, G] bridges instead
of N² hand-written .andThen overloads.
libraryDependencies += "dev.constructive" %% "cats-eo" % "0.18.0"
// Optional submodules:
libraryDependencies += "dev.constructive" %% "cats-eo-laws" % "0.18.0" % Test
libraryDependencies += "dev.constructive" %% "cats-eo-generics" % "0.18.0"
libraryDependencies += "dev.constructive" %% "cats-eo-schemes" % "0.18.0"
libraryDependencies += "dev.constructive" %% "cats-eo-schemes-laws" % "0.18.0" % Test
libraryDependencies += "dev.constructive" %% "cats-eo-circe" % "0.18.0"
libraryDependencies += "dev.constructive" %% "cats-eo-avro" % "0.18.0"
libraryDependencies += "dev.constructive" %% "cats-eo-jsoniter" % "0.18.0"
libraryDependencies += "dev.constructive" %% "cats-eo-zio" % "0.18.0"
libraryDependencies += "dev.constructive" %% "cats-eo-kyo" % "0.18.0" // JDK 25Requires Scala 3.8.x on JDK 17, 21, or 25 — except cats-eo-kyo,
which requires JDK 25 (kyo 1.0.0-RC5+ ships Java-25-only bytecode).
import dev.constructive.eo.optics.Lens
import dev.constructive.eo.optics.Optic.*
import dev.constructive.eo.generics.lens
case class Address(street: String, zip: Int)
case class Person(name: String, address: Address)
// Hand-written Lens:
val streetL = Lens[Address, String](_.street, (a, s) => a.copy(street = s))
// Macro-derived Lens, composed across two case classes:
val personStreet = lens[Person](_.address).andThen(streetL)
val alice = Person("Alice", Address("Main St", 12345))
personStreet.get(alice) // "Main St"
personStreet.replace("Broadway")(alice) // address.street := "Broadway"
personStreet.modify(_.toUpperCase)(alice) // address.street := "MAIN ST"Iso— a bijective one-focus optic; the carrier isForgetful.Lens— a one-focus optic into an always-present field of a product.Prism— a one-focus optic into a sum-type branch.Optional— a one-focus optic where the focus may be absent.AffineFold— a read-onlyOptional; partial projection without a write side.Getter— a read-only one-focus projection.Modify— a write-only optic; modify without observing.Fold— N foci summarised through aMonoid.Traversal— N foci, each individually modifiable, via.eachover theMultiFocus[PSVec]carrier.MultiFocus— classifier-shaped update where the wholeF[A]is visible (adaptive KNN, one-vs-rest), plus Kaleidoscope-style aggregation universals (.collectMap/.collectList).Grate— the dual ofLens; lifts a function on the focus to a function on the whole structure.Review— the reverse-only half of aPrism; build, never observe.Unfold— the build-only many optic (embed: F[B] => T); assemble one whole from anF-layer of parts — the algebra of a recursion scheme.JsonPrism— cursor-backed JSON optic with observable-by-defaultIorfailures; multi-field foci via.fields.JsonTraversal—.eachtraversal across JSON arrays, with the same.fieldsmulti-field flavour.AvroPrism— schema-aware Avro optic overIndexedRecord, with the sameIorfailure surface asJsonPrismplus a.union[Branch]macro for Avro union types. Triple input — parsed record, binary wire bytes, or Avro JSON.AvroFieldsPrism— multi- field flavour ofAvroPrism.AvroTraversal—.eachtraversal across Avro arrays.AvroFieldsTraversal— multi-field flavour ofAvroTraversal.JsoniterPrism— AST-free JSON-bytes optic over jsoniter-scala, with the same cursor sugar andIorfailure surface asJsonPrism.- Recursion schemes —
cata / ana / hylo and the full typed zoo (para, apo, histo, futu,
zygo, …) as composable optics (
cats-eo-schemes, laws incats-eo-schemes-laws). - ZIO integration —
ZEnvironmentservice lens,Ref/TRef/TMapfocus ops,ZLayerprojection throughCanGet,Chunkelement optics, and optional sub-packages for zio-schema (AccessorBuilderoptics, the untypedDynamicValuekit,BinaryCodecbyte faces), zio-json (AST optics +JsonCursorbridge), and zio-prelude (ZValidation) (cats-eo-zio). - Kyo integration —
TypeMapservice lens,Env/Layer/Varfocus ops,Record.iso/Record.lensfor kyo Records, and the optional kyo-schema bridge:Focus→ optics, codec byte-face prisms, and optics over the untypedStructure.Valuetree (cats-eo-kyo, JDK 25).
Every optic ships a discipline-checked law set in cats-eo-laws, so
downstream projects can checkAll custom instances the same way they do
for cats typeclasses.
- Getting started: https://eo.constructive.dev/getting-started.html
- Macro-derived optics (
generics): https://eo.constructive.dev/generics.html - Recursion schemes (
schemes): https://eo.constructive.dev/schemes.html - circe integration: https://eo.constructive.dev/integrations/circe.html
- Avro integration: https://eo.constructive.dev/integrations/avro.html
- jsoniter-scala integration: https://eo.constructive.dev/integrations/jsoniter.html
- ZIO integration: https://eo.constructive.dev/integrations/zio.html
- Kyo integration (Records, kyo-schema bridge): https://eo.constructive.dev/integrations/kyo.html
- Cookbook (recipes): https://eo.constructive.dev/cookbook.html
- Benchmarks vs Monocle:
BENCHMARKS.md(generated each sweep; B/op is the comparable metric, ns/op is directional) - Composition gap analysis (research):
docs/research/2026-04-23-composition-gap-analysis.md
0.1.0 is the first public release. Binary compatibility is guaranteed
within the 0.1.x series via MiMa starting at 0.1.1 (the 0.1.0
publish has no prior version to compare against). Pre-1.0, expect
surface refinements as we learn from real users; each non-bugfix release
will list breaking changes in CHANGELOG.md.
Apache License 2.0. See LICENSE.
- cats — the typeclass foundation every carrier instance plugs into.
- Monocle — the prior art the optic-family vocabulary descends from, and the benchmark baseline.
- Hearth — the macro-commons
library that powers
cats-eo-generics. - kindlings — the
Hearth-powered codec-derivation library backing
cats-eo-circe(kindlings-circe-derivation) andcats-eo-avro(kindlings-avro-derivation). - discipline — the rule-set
harness behind
cats-eo-laws.