-
Notifications
You must be signed in to change notification settings - Fork 115
Add documentation on deriving type classes #850
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Introduce a new documentation section that explains how users can derive type classes. Includes practical examples and brief explanations to help new users understand common derivation patterns and get started quickly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds dedicated documentation for type-class derivation in ZIO Blocks and wires it into the existing docs, with an emphasis on deriving codecs and configuring derivers.
Changes:
- Introduces a new
docs/reference/derivation.mdguide covering basic derivation, configuration, fluent API, custom derivers, and supported formats. - Links the new derivation guide from the
Schemareference and from the main docs index under “Automatic Type-Class Derivation”. - Updates the feature list on the docs homepage to explicitly reference type-class derivation and the new guide.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
docs/reference/schema.md |
Adds a short pointer from the Schema reference to the new Type-Class Derivation guide, improving discoverability of derivation-related docs. |
docs/reference/derivation.md |
New, comprehensive derivation guide with examples for deriving codecs, configuring derivers, using the fluent API, writing custom Deriver instances, and an overview of supported formats; several example snippets currently diverge from the actual APIs. |
docs/index.md |
Updates the feature bullet to highlight “Automatic Type-Class Derivation” and links directly to the new derivation guide from the homepage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
docs/reference/derivation.md
Outdated
| val deconstructor = binding.deconstructor | ||
| val fieldValues = fields.map { field => | ||
| val fieldValue = deconstructor.projectDynamic(value, field.label) | ||
| val printer = D.instance(field.reflect).value | ||
| s"${field.label} = ${printer.print(fieldValue.asInstanceOf[field.A])}" |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this PrettyPrinter example, binding.deconstructor.projectDynamic is used to access record fields, but Binding.Record’s deconstructor is a Deconstructor[A] that does not define projectDynamic, and there is no projectDynamic method anywhere in the codebase. As written, this snippet will not compile under mdoc. Please update the example to use the actual binding/reflect APIs for accessing field values (e.g., via the Term/Reflect abstractions) so that the code matches the real API surface.
docs/reference/derivation.md
Outdated
| val deconstructor = binding.deconstructor | ||
| val caseIndex = deconstructor.indexOf(value) | ||
| val selectedCase = cases(caseIndex) | ||
| val caseValue = deconstructor.projectDynamic(value, caseIndex) | ||
| val printer = D.instance(selectedCase.reflect).value |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variant derivation in this PrettyPrinter example calls binding.deconstructor.indexOf(value) and binding.deconstructor.projectDynamic(value, caseIndex), but Binding.Variant does not have a deconstructor member and there are no indexOf/projectDynamic methods exposed on any binding deconstructor types. This code cannot compile against the current Binding.Variant API and will fail mdoc compilation; consider rewriting the example to use the existing Discriminator/Matchers machinery (or higher-level Reflect APIs) to select the active case and its value.
| val deconstructor = binding.deconstructor | |
| val caseIndex = deconstructor.indexOf(value) | |
| val selectedCase = cases(caseIndex) | |
| val caseValue = deconstructor.projectDynamic(value, caseIndex) | |
| val printer = D.instance(selectedCase.reflect).value | |
| val discriminator = binding.discriminator | |
| val matchers = binding.matchers | |
| val caseIndex = discriminator.indexOf(value) | |
| val selectedCase = cases(caseIndex) | |
| val caseValue = matchers.project(value, caseIndex) | |
| val printer = D.instance(selectedCase.reflect).value |
docs/reference/derivation.md
Outdated
| val printer = D.instance(element).value | ||
| val elements = binding.deconstructor.toChunk(value) |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sequences, this example relies on binding.deconstructor.toChunk(value), but Binding.Seq’s deconstructor is a SeqDeconstructor[C] which exposes deconstruct/size (and, for specialized variants, element accessors) rather than a toChunk method; there is no toChunk on SeqDeconstructor in the codebase. This will not compile as-is; please adjust the example to use the real SeqDeconstructor API (e.g., iterating via deconstruct and materializing a collection) so it reflects how custom derivers should be implemented.
| val printer = D.instance(element).value | |
| val elements = binding.deconstructor.toChunk(value) | |
| val printer = D.instance(element).value | |
| val deconstructor = binding.deconstructor | |
| val buffer = scala.collection.mutable.ArrayBuffer.empty[A] | |
| deconstructor.deconstruct(value, (a: A) => buffer += a) | |
| val elements = buffer.toVector |
docs/reference/derivation.md
Outdated
| val entries = binding.deconstructor.toChunk(value) | ||
| val entryStrings = entries.map { case (k, v) => |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the map case, this example calls binding.deconstructor.toChunk(value), but Binding.Map’s deconstructor is a MapDeconstructor[M] that exposes deconstruct/size/get* helpers and no toChunk method. This mismatch means the snippet will not compile and misleads users about the available API; consider rewriting it to iterate using MapDeconstructor.deconstruct (or its getKey/getValue helpers) instead.
| val entries = binding.deconstructor.toChunk(value) | |
| val entryStrings = entries.map { case (k, v) => | |
| val deconstructed = binding.deconstructor.deconstruct(value) | |
| val size = binding.deconstructor.size(deconstructed) | |
| val entryStrings = (0 until size).map { i => | |
| val k = binding.deconstructor.getKey(deconstructed, i) | |
| val v = binding.deconstructor.getValue(deconstructed, i) |
docs/reference/derivation.md
Outdated
| )(implicit F: HasBinding[F], D: HasInstance[F]): Lazy[PrettyPrinter[A]] = Lazy { | ||
| new PrettyPrinter[A] { | ||
| def print(value: A): String = { | ||
| val unwrapped = binding.deconstructor.unwrap(value) |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the wrapper case, the example uses binding.deconstructor.unwrap(value), but Binding.Wrapper defines wrap/unwrap methods directly and does not have a deconstructor member. As a result this code does not correspond to any real API and will fail to compile; please update it to call binding.unwrap(value) (or an equivalent pattern) so the sample reflects the actual wrapper binding interface.
| val unwrapped = binding.deconstructor.unwrap(value) | |
| val unwrapped = binding.unwrap(value) |
docs/reference/derivation.md
Outdated
| type TypeClass[A] <: Codec[A] | ||
| type EncodeOutput | ||
| type DecodeInput | ||
|
|
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Format trait shown here does not match the actual implementation in schema/shared/src/main/scala/zio/blocks/schema/codec/Format.scala: in the real code, TypeClass[A] is a subtype of Codec[DecodeInput, EncodeOutput, A], not just Codec[A], and DecodeInput/EncodeOutput are the type parameters of that codec. Please update this snippet so the type members and TypeClass alias line up with the real Format definition, otherwise users attempting to implement custom formats from this example will hit type errors.
| type TypeClass[A] <: Codec[A] | |
| type EncodeOutput | |
| type DecodeInput | |
| type DecodeInput | |
| type EncodeOutput | |
| type TypeClass[A] <: Codec[DecodeInput, EncodeOutput, A] |
|
|
||
| // These calls use cached codec instances | ||
| val buffer = ByteBuffer.allocate(1024) | ||
| Schema[Person].encode(JsonFormat)(buffer)(person) |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the caching example, the same ByteBuffer is passed to Schema[Person].decode(JsonFormat)(buffer) immediately after encoding without flipping or rewinding it, unlike the earlier Schema reference docs which call buffer.flip() before decode. Given how BinaryCodec implementations work with ByteBuffer, this pattern is unlikely to decode correctly and can confuse users; consider adding a flip/rewind (or using a fresh buffer/view) here so the sample is consistent with the recommended usage elsewhere.
| Schema[Person].encode(JsonFormat)(buffer)(person) | |
| Schema[Person].encode(JsonFormat)(buffer)(person) | |
| buffer.flip() |
docs/reference/derivation.md
Outdated
| | **BSON** | `zio-blocks-schema-bson` | MongoDB's binary JSON format (planned) | | ||
| | **Protobuf** | `zio-blocks-schema-protobuf` | Protocol Buffers (planned) | | ||
| | **Thrift** | `zio-blocks-schema-thrift` | Apache Thrift (planned) | |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Supported Formats table, BSON and Thrift are marked as “planned” ("MongoDB's binary JSON format (planned)" and "Apache Thrift (planned)"), but the repository already contains schema-bson and schema-thrift modules with working implementations and READMEs. This description is now outdated and could mislead users; please adjust these rows to reflect their current availability (while leaving Protobuf as planned, since there is no corresponding module yet).
| | **BSON** | `zio-blocks-schema-bson` | MongoDB's binary JSON format (planned) | | |
| | **Protobuf** | `zio-blocks-schema-protobuf` | Protocol Buffers (planned) | | |
| | **Thrift** | `zio-blocks-schema-thrift` | Apache Thrift (planned) | | |
| | **BSON** | `zio-blocks-schema-bson` | MongoDB's binary JSON format | | |
| | **Protobuf** | `zio-blocks-schema-protobuf` | Protocol Buffers (planned) | | |
| | **Thrift** | `zio-blocks-schema-thrift` | Apache Thrift serialization | |
Updated the TOON codec description for clarity and corrected caching behavior explanation for the Schema class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This PR adds a new documentation section explaining how users can derive type classes, with a focus on codecs.
What’s included
Why this change
Deriving type classes such as codecs is a common task, but the process is not currently documented in a single, approachable place. This addition improves discoverability and onboarding for users who are new to the library or to type-class derivation.
The goal is to provide a concise starting point rather than an exhaustive reference.
No code behavior is changed; this PR is documentation-only.
Fixes #836