Mix this trait into a companion object to build optics for S from a selector lambda: $(_.title), or the same with a name, optic(_.title). A macro reads the lambda at compile time and builds the optic from the implicit Schema[S]. The kind of optic follows from the path: a Lens when every step is a field, a Prism when every step is when, a Traversal when any step is each, eachKey, eachValue, atIndices, atKeys or searchFor, and an Optional otherwise.
A path is a chain of these steps:
.field: a field of a record;
.when[C]: the case C of a variant;
.at(i), .atIndices(i, j), .each: elements of a sequence;
.atKey(k), .atKeys(k1, k2), .eachKey, .eachValue: keys and values of a map;
.wrapped[B]: the value of type B inside a wrapper;
.searchFor[T]: every value at or below this point that decodes as a T (needs an implicit Schema[T]; modify skips map keys).
On Scala 3, a path can also select a tuple element by index, as in $(_(0)), and when also takes a case of a union type. The step methods compile only inside $ and optic, and any other expression in the lambda is a compile error.
The macro checks the path against the types, and the schema is checked when the optic is built: if the schema at a step has another shape than the step needs, for example a .field step where the schema is a wrapper built with Schema#transform, building the optic throws a RuntimeException such as Expected a record.
sealed trait Format
case class Paperback(pages: Int) extends Format
case class Ebook(sizeMb: Double) extends Format
case class Book(title: String, tags: List[String], format: Format)
object Book extends CompanionOptics[Book] {
implicit val schema: Schema[Book] = Schema.derived
val title: Lens[Book, String] = $(_.title)
val pages: Optional[Book, Int] = $(_.format.when[Paperback].pages)
val tags: Traversal[Book, String] = optic(_.tags.each)
}
val zionomicon = Book("Zionomicon", List("zio", "scala"), Paperback(870))
Book.pages.getOption(zionomicon) // Some(870)
Book.pages.toString // Optional(_.format.when[Paperback].pages)
transparent inline def $[A](inline path: S=>A)(using schema: Schema[S]): Any
Builds the optic that path describes (see CompanionOptics). The result has the exact optic type, such as Lens[S, String]. $ and optic are the same macro.
Builds the optic that path describes (see CompanionOptics). The result has the exact optic type, such as Lens[S, String]. $ and optic are the same macro.
transparent inline def optic[A](inline path: S=>A)(using schema: Schema[S]): Any
Builds the optic that path describes (see CompanionOptics). The result has the exact optic type, such as Lens[S, String]. $ and optic are the same macro.
Builds the optic that path describes (see CompanionOptics). The result has the exact optic type, such as Lens[S, String]. $ and optic are the same macro.
In a path, focuses on every value at or below this point that decodes as a B, found depth first. It needs an implicit Schema[B]. Map keys are read by fold and check but skipped by modify (see docs/dev/issues.md). Compiles only inside $ and optic.
In a path, focuses on every value at or below this point that decodes as a B, found depth first. It needs an implicit Schema[B]. Map keys are read by fold and check but skipped by modify (see docs/dev/issues.md). Compiles only inside $ and optic.
In a path, focuses on the value of type B inside a wrapper, such as a schema built with Schema#transform. On Scala 2 the expansion also needs import zio.blocks.schema.binding.Binding (a bug, see docs/dev/issues.md). Compiles only inside $ and optic.
In a path, focuses on the value of type B inside a wrapper, such as a schema built with Schema#transform. On Scala 2 the expansion also needs import zio.blocks.schema.binding.Binding (a bug, see docs/dev/issues.md). Compiles only inside $ and optic.