cats-eo

An existential optics library for Scala 3, built on cats.

One Optic[S, T, A, B, F] trait, parameterised over a carrier F[_, _], unifies every optic family. Composition crosses families through Composer[F, G] bridges rather than N² hand- written .andThen overloads.

Current version: 0.1.

60-second example

import eo.optics.Optic.*
import eo.generics.lens
import eo.docs.{Address, Person, Zip}

val street =
  lens[Person](_.address)
    .andThen(lens[Address](_.street))
val alice = Person("Alice", Address("Main St", Zip(12345, "6789")))
// alice: Person = Person(
//   name = "Alice",
//   address = Address(
//     street = "Main St",
//     zip = Zip(code = 12345, extension = "6789")
//   )
// )

street.get(alice)
// res0: String = "Main St"
street.replace("Broadway")(alice)
// res1: Person = Person(
//   name = "Alice",
//   address = Address(
//     street = "Broadway",
//     zip = Zip(code = 12345, extension = "6789")
//   )
// )
street.modify(_.toUpperCase)(alice)
// res2: Person = Person(
//   name = "Alice",
//   address = Address(
//     street = "MAIN ST",
//     zip = Zip(code = 12345, extension = "6789")
//   )
// )

No .copy chains, no setter lambdas, no GenLens boilerplate. The lens macro works on plain case classes, Scala 3 enums, and union types alike.

Keep reading