@zio.dev/zio-blocks 0.0.51 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adr/2026-07-18-data-migration.md +123 -0
- package/guides/async-getting-started.md +687 -0
- package/guides/compile-time-resource-safety-with-scope.md +6 -0
- package/guides/getting-started-with-mux.md +0 -112
- package/guides/query-dsl-extending.md +1 -1
- package/guides/query-dsl-fluent-builder.md +1 -1
- package/guides/query-dsl-reified-optics.md +1 -1
- package/guides/query-dsl-sql.md +395 -1
- package/guides/sql-checked-interpolation.md +173 -0
- package/guides/sql-transactions.md +286 -0
- package/guides/telemetry-guide.md +131 -70
- package/guides/zio-schema-migration.md +6 -6
- package/index.md +200 -559
- package/package.json +1 -1
- package/reference/async.md +1379 -531
- package/reference/chunk.md +3 -3
- package/reference/codegen/index.md +1 -1
- package/reference/combinators.md +4 -4
- package/reference/config/config-decoder.md +460 -0
- package/reference/config/config-source.md +489 -0
- package/reference/config/errors.md +278 -0
- package/reference/config/flags.md +369 -0
- package/reference/config/formats.md +314 -0
- package/reference/config/index.md +304 -0
- package/reference/config/rollout.md +336 -0
- package/reference/context.md +6 -49
- package/reference/data-migration.md +269 -0
- package/reference/datastar/attributes.md +302 -0
- package/reference/datastar/events.md +234 -0
- package/reference/datastar/index.md +256 -0
- package/reference/datastar/signals.md +230 -0
- package/reference/datastar/sse.md +295 -0
- package/reference/datastar.md +2 -2
- package/reference/docs.md +2 -2
- package/reference/endpoint/bulk-creation.md +96 -0
- package/reference/endpoint/endpoint.md +1 -0
- package/reference/endpoint/index.md +9 -89
- package/reference/endpoint/path-codec.md +12 -24
- package/reference/endpoint/route-pattern.md +4 -6
- package/reference/endpoint/segment-codec.md +19 -32
- package/reference/html.md +313 -9
- package/reference/htmx/index.md +4 -52
- package/reference/htmx/response-headers.md +240 -0
- package/reference/http-model/headers.md +735 -0
- package/reference/http-model/index.md +3 -1
- package/reference/http-model/model.md +107 -71
- package/reference/http-model/schema-codecs.md +522 -0
- package/reference/http-model/schema.md +6 -3
- package/reference/http-model/server-sent-event.md +341 -0
- package/reference/jwt.md +195 -0
- package/reference/maybe.md +128 -11
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +7 -2
- package/reference/openapi.md +3 -3
- package/reference/projection.md +654 -0
- package/reference/resource-management/index.md +1 -1
- package/reference/resource-management/resource.md +2 -98
- package/reference/resource-management/scope.md +1 -209
- package/reference/resource-management/wire.md +4 -50
- package/reference/ringbuffer/advanced.mdx +1 -1
- package/reference/ringbuffer/index.mdx +3 -3
- package/reference/ringbuffer/mpmc.mdx +38 -4
- package/reference/ringbuffer/mpsc.mdx +36 -4
- package/reference/ringbuffer/spmc.mdx +1 -1
- package/reference/ringbuffer/spsc.mdx +87 -15
- package/reference/schema/allows.md +0 -96
- package/reference/schema/binding.md +2 -2
- package/reference/schema/built-in-codecs/avro.md +2 -2
- package/reference/schema/built-in-codecs/bson.md +50 -20
- package/reference/schema/built-in-codecs/csv.md +2 -2
- package/reference/schema/built-in-codecs/index.md +3 -3
- package/reference/schema/built-in-codecs/json/index.md +2 -2
- package/reference/schema/built-in-codecs/json/json.md +1 -0
- package/reference/schema/built-in-codecs/messagepack.md +3 -3
- package/reference/schema/built-in-codecs/thrift.md +2 -2
- package/reference/schema/built-in-codecs/toon.md +3 -3
- package/reference/schema/built-in-codecs/yaml.md +2 -2
- package/reference/schema/codec.md +11 -11
- package/reference/schema/dynamic-optic.md +48 -3
- package/reference/schema/dynamic-schema.md +3 -3
- package/reference/schema/index.md +2 -0
- package/reference/schema/path-interpolator.md +2 -0
- package/reference/schema/reflect-transformer.md +140 -0
- package/reference/schema/schema-evolution/as.md +4 -4
- package/reference/schema/schema-evolution/into.md +2 -2
- package/reference/schema/schema-expr.md +2 -2
- package/reference/schema/schema-search.md +263 -0
- package/reference/schema/schema.md +10 -2
- package/reference/schema/type-class-derivation.md +1 -1
- package/reference/smithy.md +502 -3
- package/reference/sql/db-codec-deriver.md +3 -3
- package/reference/sql/db-codec.md +22 -22
- package/reference/sql/db-con.md +4 -4
- package/reference/sql/db-connection.md +1 -1
- package/reference/sql/db-param.md +1 -1
- package/reference/sql/db-result-reader.md +4 -2
- package/reference/sql/db-tx.md +46 -14
- package/reference/sql/ddl.md +1 -1
- package/reference/sql/frag.md +44 -10
- package/reference/sql/index.md +7 -7
- package/reference/sql/repo.md +15 -15
- package/reference/sql/sql-dialect.md +1 -1
- package/reference/sql/sql-logger.md +1 -1
- package/reference/sql/sql-name-mapper.md +3 -3
- package/reference/sql/table-metadata.md +3 -3
- package/reference/sql/table.md +10 -10
- package/reference/sql/transactor-zio.md +1 -1
- package/reference/sql/transactor.md +21 -11
- package/reference/sql-zio.md +2 -2
- package/reference/streams/core/index.md +32 -0
- package/reference/streams/{pipeline.md → core/pipeline.md} +210 -74
- package/reference/streams/{sink.md → core/sink.md} +331 -353
- package/reference/streams/{stream.md → core/stream.md} +919 -209
- package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
- package/reference/streams/execution-and-compatibility/index.md +35 -0
- package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
- package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
- package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
- package/reference/streams/index.md +140 -67
- package/reference/streams/primitives/index.md +30 -0
- package/reference/streams/primitives/reader.md +1992 -0
- package/reference/streams/{writer.md → primitives/writer.md} +254 -98
- package/reference/telemetry/common/any-value.md +90 -0
- package/reference/telemetry/common/attribute-key.md +87 -0
- package/reference/telemetry/common/attributes.md +118 -0
- package/reference/telemetry/common/index.md +39 -0
- package/reference/telemetry/common/instrumentation-scope.md +24 -0
- package/reference/telemetry/common/resource.md +34 -0
- package/reference/telemetry/index.md +311 -0
- package/reference/telemetry/logging/index.md +197 -0
- package/reference/telemetry/logging/log-enrichment.md +72 -0
- package/reference/telemetry/logging/log-formatter.md +100 -0
- package/reference/telemetry/logging/log-record-processor.md +56 -0
- package/reference/telemetry/logging/log-record.md +44 -0
- package/reference/telemetry/logging/log-writer.md +64 -0
- package/reference/telemetry/logging/logger-provider.md +142 -0
- package/reference/telemetry/logging/logger.md +83 -0
- package/reference/telemetry/logging/severity.md +62 -0
- package/reference/telemetry/metrics/index.md +150 -0
- package/reference/telemetry/metrics/instruments.md +183 -0
- package/reference/telemetry/metrics/labeled-instruments.md +74 -0
- package/reference/telemetry/metrics/meter-provider.md +76 -0
- package/reference/telemetry/metrics/meter.md +98 -0
- package/reference/telemetry/metrics/metric-data.md +57 -0
- package/reference/telemetry/otel/custom-exporter.md +216 -0
- package/reference/telemetry/otel/index.md +212 -0
- package/reference/telemetry/tracing/index.md +155 -0
- package/reference/telemetry/tracing/sampler.md +89 -0
- package/reference/telemetry/tracing/span-builder.md +57 -0
- package/reference/telemetry/tracing/span-context.md +39 -0
- package/reference/telemetry/tracing/span-data.md +32 -0
- package/reference/telemetry/tracing/span-kind.md +55 -0
- package/reference/telemetry/tracing/span-processor.md +53 -0
- package/reference/telemetry/tracing/span-status.md +47 -0
- package/reference/telemetry/tracing/span.md +117 -0
- package/reference/telemetry/tracing/tracer-provider.md +91 -0
- package/reference/telemetry/tracing/tracer.md +52 -0
- package/reference/typeid.md +0 -64
- package/sidebars.js +365 -185
- package/undocumented-report.md +528 -270
- package/reference/config.md +0 -158
- package/reference/streams/concurrent-operators.md +0 -106
- package/reference/streams/reader.md +0 -1284
- package/reference/streams/scala-2-compatibility.md +0 -55
- package/reference/streams/zero-boxing.md +0 -275
- package/reference/telemetry.md +0 -693
|
@@ -26,7 +26,7 @@ Rather than writing custom encoders or relying on code generation from .thrift f
|
|
|
26
26
|
Add the module to your `build.sbt`:
|
|
27
27
|
|
|
28
28
|
```sbt
|
|
29
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-thrift" % "0.0.
|
|
29
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-thrift" % "0.0.56"
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
**Note:** This module is JVM-only and is not available for Scala.js.
|
|
@@ -323,7 +323,7 @@ object Person {
|
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
val codec = Person.schema.derive(ThriftFormat)
|
|
326
|
-
// codec: ThriftCodec[Person] = zio.blocks.schema.thrift.ThriftCodecDeriver$$anon$3@
|
|
326
|
+
// codec: ThriftCodec[Person] = zio.blocks.schema.thrift.ThriftCodecDeriver$$anon$3@65cebe3a
|
|
327
327
|
```
|
|
328
328
|
|
|
329
329
|
### Primitive Type Support
|
|
@@ -28,13 +28,13 @@ Rather than hand-writing TOON parsing logic or relying on external libraries wit
|
|
|
28
28
|
Add the module to your `build.sbt`:
|
|
29
29
|
|
|
30
30
|
```sbt
|
|
31
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-toon" % "0.0.
|
|
31
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-toon" % "0.0.56"
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
For Scala.js, use `%%%` instead of `%%`:
|
|
35
35
|
|
|
36
36
|
```sbt
|
|
37
|
-
libraryDependencies += "dev.zio" %%% "zio-blocks-schema-toon" % "0.0.
|
|
37
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema-toon" % "0.0.56"
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
Supported Scala versions: 2.13.x and 3.x
|
|
@@ -325,7 +325,7 @@ object Person {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
val codec = Person.schema.derive(ToonFormat)
|
|
328
|
-
// codec: ToonCodec[Person] = zio.blocks.schema.toon.ToonCodecDeriver$$anon$5@
|
|
328
|
+
// codec: ToonCodec[Person] = zio.blocks.schema.toon.ToonCodecDeriver$$anon$5@346da013
|
|
329
329
|
val person = Person("Alice", "alice@example.com")
|
|
330
330
|
// person: Person = Person(name = "Alice", email = "alice@example.com")
|
|
331
331
|
val bytes = codec.encode(person)
|
|
@@ -27,13 +27,13 @@ Rather than hand-writing YAML parsing logic or relying on external libraries wit
|
|
|
27
27
|
Add the module to your `build.sbt`:
|
|
28
28
|
|
|
29
29
|
```sbt
|
|
30
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-yaml" % "0.0.
|
|
30
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-yaml" % "0.0.56"
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
For Scala.js, use `%%%` instead of `%%`:
|
|
34
34
|
|
|
35
35
|
```sbt
|
|
36
|
-
libraryDependencies += "dev.zio" %%% "zio-blocks-schema-yaml" % "0.0.
|
|
36
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema-yaml" % "0.0.56"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Supported Scala versions: 2.13.x and 3.x
|
|
@@ -3,7 +3,7 @@ id: codec
|
|
|
3
3
|
title: "Codec"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
`Codec[DecodeInput, EncodeOutput, Value]` is the base abstraction for encoding and decoding values between a specific input representation and a specific output representation. It forms the foundation of ZIO Blocks' multi-format serialization system, enabling a single `Schema[A]` to derive codecs for JSON, Avro, TOON, MessagePack, Thrift, and other formats
|
|
6
|
+
`Codec[DecodeInput, EncodeOutput, Value]` is the base abstraction for encoding and decoding values between a specific input representation and a specific output representation. It forms the foundation of ZIO Blocks' multi-format serialization system, enabling a single `Schema[A]` to derive codecs for JSON, Avro, TOON, MessagePack, Thrift, and other formats integrated through `Codec`/`Format`. BSON's `BsonCodec` is not a subtype of `codec.Codec` because it operates on native `BsonReader`, `BsonWriter`, and `BsonValue` APIs. It nevertheless participates in shared type-class derivation through `Schema[A].derive(BsonCodecDeriver)`; `BsonSchemaCodec` remains a compatibility facade.
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
@@ -48,26 +48,26 @@ val result: Either[SchemaError, Person] = Person.codec.decode(bytes)
|
|
|
48
48
|
To include the base schema module with JSON support, add the following dependency to your `build.sbt`:
|
|
49
49
|
|
|
50
50
|
```scala
|
|
51
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
51
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
Additional format modules are separate artifacts:
|
|
55
55
|
|
|
56
56
|
```scala
|
|
57
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-avro" % "0.0.
|
|
58
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-bson" % "0.0.
|
|
59
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-csv" % "0.0.
|
|
60
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-messagepack" % "0.0.
|
|
61
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-thrift" % "0.0.
|
|
62
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-toon" % "0.0.
|
|
63
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-xml" % "0.0.
|
|
64
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema-yaml" % "0.0.
|
|
57
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-avro" % "0.0.56"
|
|
58
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-bson" % "0.0.56"
|
|
59
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-csv" % "0.0.56"
|
|
60
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-messagepack" % "0.0.56"
|
|
61
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-thrift" % "0.0.56"
|
|
62
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-toon" % "0.0.56"
|
|
63
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-xml" % "0.0.56"
|
|
64
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema-yaml" % "0.0.56"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
For cross-platform projects (Scala.js):
|
|
68
68
|
|
|
69
69
|
```scala
|
|
70
|
-
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.
|
|
70
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.56"
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
Supported Scala versions: 2.13.x and 3.x.
|
|
@@ -398,7 +398,7 @@ val serialized: DynamicValue = opticSchema.toDynamicValue(path)
|
|
|
398
398
|
|
|
399
399
|
## Search Optics
|
|
400
400
|
|
|
401
|
-
A **Search optic** recursively traverses a data structure to find **all occurrences** matching a type or schema. It produces a `Traversal[S, A]` that collects matches in depth-first, left-to-right order.
|
|
401
|
+
A **Search optic** recursively traverses a data structure to find **all occurrences** matching a type or schema. It produces a `Traversal[S, A]` that collects matches in depth-first, left-to-right order. This section covers the two DSLs for building a search path; for the `SearchTraversal` engine that executes one — `fold`/`modify`/`check`, composing a search with other optics, and the `SchemaMatch` predicate behind `#Pattern` matching — see [Schema Search and Update](./schema-search.md).
|
|
402
402
|
|
|
403
403
|
### Motivation
|
|
404
404
|
|
|
@@ -483,6 +483,51 @@ val modified = data.modify(p"#string")(dv =>
|
|
|
483
483
|
)
|
|
484
484
|
```
|
|
485
485
|
|
|
486
|
+
### The `SchemaRepr` Pattern Type
|
|
487
|
+
|
|
488
|
+
Every `#Pattern` string above parses into a `SchemaRepr` — a plain, immutable ADT you can also build by hand, independent of the interpolator. `DynamicOptic#searchSchema(schemaRepr)` takes one directly, and `Node.SchemaSearch(schemaRepr)` is the node it produces, exactly the node `#Pattern` syntax builds under the hood:
|
|
489
|
+
|
|
490
|
+
```scala
|
|
491
|
+
import zio.blocks.schema._
|
|
492
|
+
|
|
493
|
+
val pattern: SchemaRepr = SchemaRepr.Record(
|
|
494
|
+
IndexedSeq("name" -> SchemaRepr.Primitive("string"), "age" -> SchemaRepr.Primitive("int"))
|
|
495
|
+
)
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
`SchemaRepr#toString` renders the same syntax `#Pattern` strings use, so a pattern built by hand and one parsed from a literal print identically:
|
|
499
|
+
|
|
500
|
+
```scala
|
|
501
|
+
pattern.toString
|
|
502
|
+
// res13: String = "record { name: string, age: int }"
|
|
503
|
+
|
|
504
|
+
val path: DynamicOptic = DynamicOptic.root.searchSchema(pattern)
|
|
505
|
+
// path: DynamicOptic = DynamicOptic(
|
|
506
|
+
// IndexedSeq(
|
|
507
|
+
// SchemaSearch(
|
|
508
|
+
// Record(Vector(("name", Primitive("string")), ("age", Primitive("int"))))
|
|
509
|
+
// )
|
|
510
|
+
// )
|
|
511
|
+
// )
|
|
512
|
+
path == p"#record { name: string, age: int }"
|
|
513
|
+
// res14: Boolean = true
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
The eight cases cover every shape the grammar recognizes:
|
|
517
|
+
|
|
518
|
+
| Case | Renders as | Matches |
|
|
519
|
+
|------|------------|---------|
|
|
520
|
+
| `Nominal(name)` | the bare name, e.g. `Person` | a value of that Scala type — only against a `Reflect` tree, see below |
|
|
521
|
+
| `Primitive(name)` | the bare name, e.g. `string` | a primitive of that kind (case-insensitive) |
|
|
522
|
+
| `Record(fields)` | `record { name: string, ... }` | a record with at least those fields, by subset match |
|
|
523
|
+
| `Variant(cases)` | `variant { Case: type, ... }` | a variant whose active case matches one of those entries |
|
|
524
|
+
| `Sequence(element)` | `list(...)` | a sequence whose elements all match — parses from `list(...)`, `set(...)`, or `vector(...)`, all three producing the same case |
|
|
525
|
+
| `Map(key, value)` | `map(key, value)` | a map whose entries all match both patterns |
|
|
526
|
+
| `Optional(inner)` | `option(...)` | `Null`, or a value matching the inner pattern |
|
|
527
|
+
| `Wildcard` | `_` | anything |
|
|
528
|
+
|
|
529
|
+
Parsing a `#Pattern` string is itself layered: the path interpolator recognizes the `#` prefix and hands the substring after it to a small recursive-descent parser, which reports a specific error (unexpected character, an empty `record {}`/`variant {}`, an unterminated expression) at the exact offset where parsing failed — that's what turns a typo like `#record { name: strnig }` into a compile-time interpolator error rather than a pattern that silently matches nothing.
|
|
530
|
+
|
|
486
531
|
### Supported Patterns
|
|
487
532
|
|
|
488
533
|
Search optics support both **type-based** and **schema-based** patterns:
|
|
@@ -493,7 +538,7 @@ Search optics support both **type-based** and **schema-based** patterns:
|
|
|
493
538
|
| `#int`, `#string`, `#boolean` | Specific primitive types (case-insensitive) | `#int`, `#string`, `#boolean` |
|
|
494
539
|
| `#record { ... }` | Records with matching fields | `#record { name: string }` |
|
|
495
540
|
| `#variant { ... }` | Variant cases with matching content | `#variant { Error: string }` |
|
|
496
|
-
| `#list(...)`
|
|
541
|
+
| `#list(...)`, `#set(...)`, `#vector(...)` | Sequences with a matching element type, regardless of which of the three keywords appears | `#list(string)` |
|
|
497
542
|
| `#map(...)` | Maps with key and value types | `#map(string, int)` |
|
|
498
543
|
| `#option(...)` | Optional values with inner type | `#option(int)` |
|
|
499
544
|
|
|
@@ -520,7 +565,7 @@ val allInts = nested.get(p"#int").toChunk
|
|
|
520
565
|
|
|
521
566
|
### Known Limitation: Nominal Matching in Untyped Contexts
|
|
522
567
|
|
|
523
|
-
`Nominal` pattern matching (e.g., `#Person`) returns `false`
|
|
568
|
+
`Nominal` pattern matching (e.g., `#Person`) returns `false` against a `DynamicValue` or `Json` value because these untyped structures carry no type identity. The one exception is searching a `Schema`/`Reflect` tree itself (as `DynamicSchema#get` does) rather than a value — there, `Nominal` matches by comparing against the tree's own `TypeId`, since a `Reflect` node still carries the type identity a plain `DynamicValue`/`Json` value never does. For searching values, use one of these approaches instead:
|
|
524
569
|
|
|
525
570
|
- **Use the typed API**: `optic(_.searchFor[Person])`
|
|
526
571
|
- **Or use structural patterns**: `p"#record { name: string, age: int }"`
|
|
@@ -83,7 +83,7 @@ dynamic
|
|
|
83
83
|
// value = Primitive(
|
|
84
84
|
// primitiveType = String(None),
|
|
85
85
|
// typeId = String,
|
|
86
|
-
// primitiveBinding = zio.blocks.schema.binding.NoBinding$$anon$1@
|
|
86
|
+
// primitiveBinding = zio.blocks.schema.binding.NoBinding$$anon$1@b3c65fe,
|
|
87
87
|
// doc = Doc(blocks = IndexedSeq(), metadata = Map()),
|
|
88
88
|
// modifiers = List(),
|
|
89
89
|
// storedDefaultValue = None,
|
|
@@ -97,7 +97,7 @@ dynamic
|
|
|
97
97
|
// value = Primitive(
|
|
98
98
|
// primitiveType = Int(None),
|
|
99
99
|
// typeId = Int,
|
|
100
|
-
// primitiveBinding = zio.blocks.schema.binding.NoBinding$$anon$1@
|
|
100
|
+
// primitiveBinding = zio.blocks.schema.binding.NoBinding$$anon$1@b3c65fe,
|
|
101
101
|
// doc = Doc(blocks = IndexedSeq(), metadata = Map()),
|
|
102
102
|
// modifiers = List(),
|
|
103
103
|
// storedDefaultValue = None,
|
|
@@ -115,7 +115,7 @@ dynamic
|
|
|
115
115
|
// value = Primitive(
|
|
116
116
|
// primitiveType = String(None),
|
|
117
117
|
// typeId = String,
|
|
118
|
-
// primitiveBinding = zio.blocks.schema.binding.NoBinding$$anon$1@
|
|
118
|
+
// primitiveBinding = zio.blocks.schema.binding.NoBinding$$anon$1@b3c65fe,
|
|
119
119
|
// doc = Doc(blocks = IndexedSeq(), metadata = Map()),
|
|
120
120
|
// modifiers = List(),
|
|
121
121
|
// storedDefaultValue = None,
|
|
@@ -12,6 +12,7 @@ ZIO Blocks Schema is the core type system and serialization framework that provi
|
|
|
12
12
|
- [`Schema`](./schema.md) — Primary data type containing reified structure of a Scala data type
|
|
13
13
|
- [`Reflect`](./reflect.md) — Foundational data structure containing reified structural information
|
|
14
14
|
- [`Binding`](./binding.md) — Operational machinery for constructing and deconstructing values
|
|
15
|
+
- [`ReflectTransformer`](./reflect-transformer.md) — Generic mechanism for rewriting a Reflect tree's binding type, powering schema (de)serialization and derivation
|
|
15
16
|
- [`Registers`](./registers.md) — Register-based design for zero-allocation construction and deconstruction
|
|
16
17
|
- [`Structural Types`](./structural-types.md) — Duck typing with ZIO Blocks schemas
|
|
17
18
|
|
|
@@ -24,6 +25,7 @@ ZIO Blocks Schema is the core type system and serialization framework that provi
|
|
|
24
25
|
- [`Optics`](./optics.md) — Reflective optics for type-safe, composable access to nested data structures
|
|
25
26
|
- [`DynamicOptic`](./dynamic-optic.md) — Runtime path through nested data structures
|
|
26
27
|
- [`Path Interpolator`](./path-interpolator.md) — Compile-time string interpolator `p"..."` for constructing `DynamicOptic` instances
|
|
28
|
+
- [`Schema Search and Update`](./schema-search.md) — Executing type/schema-pattern searches with `SearchTraversal`, and rewriting schema metadata with `Reflect.Updater`/`Term.Updater`
|
|
27
29
|
- [`SchemaExpr`](./schema-expr.md) — Schema-aware expressions for evaluation and query language translation
|
|
28
30
|
- [`Patch`](./patch.md) — Type-safe, serializable transformations of data structures
|
|
29
31
|
- [`Modifier`](./modifier.md) — Mechanism to attach metadata and configuration to schema elements
|
|
@@ -267,6 +267,8 @@ p"#map(string, int)" // Find maps from string to int
|
|
|
267
267
|
p"#option(Person)" // Find optional Person values
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
+
`set(...)` and `vector(...)` work as synonyms for `list(...)` — all three parse to the same `SchemaRepr.Sequence` pattern, since the pattern only cares about the element type, not which collection it comes from.
|
|
271
|
+
|
|
270
272
|
To match any value regardless of type:
|
|
271
273
|
|
|
272
274
|
```scala
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: reflect-transformer
|
|
3
|
+
title: "ReflectTransformer"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`ReflectTransformer[F, G]` rewrites a `Reflect[F, A]` tree into a `Reflect[G, A]` tree, changing the binding-metadata type constructor from `F` to `G` at every node while leaving the shape — field names, type IDs, docs, modifiers, defaults, examples — untouched. `Reflect#transform` walks the tree and recurses into fields, cases, elements, and keys/values for you; a `ReflectTransformer` only has to say how to rebuild the metadata-bearing leaf of each node kind once its children are already transformed.
|
|
7
|
+
|
|
8
|
+
Three things in the codebase are built on it: stripping bindings to serialize a [`Schema`](./schema.md) as a [`DynamicSchema`](./dynamic-schema.md), attaching bindings back with `RebindTransformer`, and [type class derivation](./type-class-derivation.md), which pairs each node's binding with a derived instance. This page covers all three, plus `OnlyMetadata`, the base class most transformers use instead of implementing the full interface.
|
|
9
|
+
|
|
10
|
+
## Design & Structure
|
|
11
|
+
|
|
12
|
+
`ReflectTransformer` declares one method per [`Reflect`](./reflect.md) node kind — seven of the eight; `Deferred` is handled separately (see [Cycle Safety](#cycle-safety-and-the-transform-cache)) because it has no metadata of its own to transform. `transformRecord` is representative of all seven — each takes the node's already-transformed children plus its untouched shape fields, and returns a `Lazy` of the rebuilt node:
|
|
13
|
+
|
|
14
|
+
```scala
|
|
15
|
+
trait ReflectTransformer[-F[_, _], G[_, _]] {
|
|
16
|
+
def transformRecord[A](
|
|
17
|
+
path: DynamicOptic,
|
|
18
|
+
fields: IndexedSeq[Term[G, A, ?]],
|
|
19
|
+
typeId: TypeId[A],
|
|
20
|
+
metadata: F[BindingType.Record, A],
|
|
21
|
+
doc: Doc,
|
|
22
|
+
modifiers: Seq[Modifier.Reflect],
|
|
23
|
+
storedDefaultValue: Option[DynamicValue],
|
|
24
|
+
storedExamples: collection.immutable.Seq[DynamicValue]
|
|
25
|
+
): Lazy[Reflect.Record[G, A]]
|
|
26
|
+
|
|
27
|
+
// ...one more method per remaining node kind, same shape
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The other six follow the identical pattern — a `path`, the already-transformed children, the shape fields, and the still-untransformed `metadata: F[...]` — differing only in which children they carry and which `Reflect` node they return:
|
|
32
|
+
|
|
33
|
+
| Node kind | Method | Transformed children it receives |
|
|
34
|
+
| ---------- | -------- | ----------------------------------- |
|
|
35
|
+
| `Record` | `transformRecord` | `fields: IndexedSeq[Term[G, A, ?]]` |
|
|
36
|
+
| `Variant` | `transformVariant` | `cases: IndexedSeq[Term[G, A, ? <: A]]` |
|
|
37
|
+
| `Sequence` | `transformSequence` | `element: Reflect[G, A]` |
|
|
38
|
+
| `Map` | `transformMap` | `key: Reflect[G, Key]`, `value: Reflect[G, Value]` |
|
|
39
|
+
| `Dynamic` | `transformDynamic` | none — `DynamicValue` has no children |
|
|
40
|
+
| `Primitive` | `transformPrimitive` | none |
|
|
41
|
+
| `Wrapper` | `transformWrapper` | `wrapped: Reflect[G, B]` |
|
|
42
|
+
|
|
43
|
+
Every result is wrapped in [`Lazy`](./lazy.md) because a transformer built for a recursive schema must be able to build a node before its own children are fully forced.
|
|
44
|
+
|
|
45
|
+
## `OnlyMetadata` — The Common Case
|
|
46
|
+
|
|
47
|
+
Most transformers only need to change the metadata and leave every field, case, and element exactly as it already was. `ReflectTransformer.OnlyMetadata[F, G]` implements all seven `transformX` methods for you in terms of one method:
|
|
48
|
+
|
|
49
|
+
```scala
|
|
50
|
+
abstract class OnlyMetadata[F[_, _], G[_, _]] extends ReflectTransformer[F, G] {
|
|
51
|
+
def transformMetadata[K, A](f: F[K, A]): Lazy[G[K, A]]
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each `transformX` implementation `OnlyMetadata` provides is one line: transform the metadata, then rebuild the same node with the same children and shape, swapping in the new metadata. `HasBinding[F]` — the type class that extracts a `Binding[T, A]` out of an arbitrary `F[T, A]` — is itself a `ReflectTransformer.OnlyMetadata[F, Binding]` whose `transformMetadata` calls the one method a `HasBinding[F]` instance supplies.
|
|
56
|
+
|
|
57
|
+
## Stripping Bindings: `ReflectTransformer.noBinding`
|
|
58
|
+
|
|
59
|
+
`ReflectTransformer.noBinding[F]()` is a predefined, stateless transformer that discards whatever metadata `F` holds and replaces it with the singleton `NoBinding` value, regardless of what `F` was. It is what `Reflect#noBinding` and, through it, `Schema#toDynamicSchema` use to turn a bound, operational schema into a pure-data one safe to serialize:
|
|
60
|
+
|
|
61
|
+
```scala
|
|
62
|
+
import zio.blocks.schema._
|
|
63
|
+
|
|
64
|
+
case class Person(name: String, age: Int)
|
|
65
|
+
object Person {
|
|
66
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Reflect.Bound[Person] -> Reflect.Unbound[Person], via ReflectTransformer.noBinding()
|
|
70
|
+
val unbound: Reflect.Unbound[Person] = Person.schema.reflect.noBinding
|
|
71
|
+
|
|
72
|
+
// The public entry point most code uses instead
|
|
73
|
+
val dynamicSchema: DynamicSchema = Person.schema.toDynamicSchema
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Because `ReflectTransformer.noBinding` never inspects the metadata it's replacing, one singleton instance (cast per call) serves every `F`, with no allocation per use.
|
|
77
|
+
|
|
78
|
+
## Attaching Bindings: `RebindTransformer`
|
|
79
|
+
|
|
80
|
+
`RebindTransformer` is the transformer in the opposite direction — `NoBinding` to `Binding` — that [`DynamicSchema#rebind`](./dynamic-schema.md) constructs internally to turn a deserialized, pure-data schema back into an operational one. It is `private[schema]`; the public surface is `DynamicSchema#rebind`, which builds one for you from a [`BindingResolver`](./binding-resolver.md):
|
|
81
|
+
|
|
82
|
+
```scala
|
|
83
|
+
import zio.blocks.schema._
|
|
84
|
+
import zio.blocks.schema.binding._
|
|
85
|
+
|
|
86
|
+
case class Person(name: String, age: Int)
|
|
87
|
+
object Person {
|
|
88
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
val dynamicSchema: DynamicSchema = Person.schema.toDynamicSchema
|
|
92
|
+
|
|
93
|
+
val resolver = BindingResolver.defaults.bind(Binding.of[Person])
|
|
94
|
+
val rebound: Schema[Person] = dynamicSchema.rebind[Person](resolver)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Unlike `ReflectTransformer.noBinding`, `RebindTransformer` implements every `transformX` method individually rather than extending `OnlyMetadata` — each one must call a different resolver lookup (`resolveRecord`, `resolveVariant`, `resolveSeqFor`, `resolveMapFor`, `resolveDynamic`, `resolvePrimitive`, `resolveWrapper`) and fail in a way that names which kind of binding was missing.
|
|
98
|
+
|
|
99
|
+
### `RebindException`
|
|
100
|
+
|
|
101
|
+
A resolver that's missing a binding for some type in the tree makes `DynamicSchema#rebind` throw `RebindException` rather than return quietly, since a `Schema` with an unresolved node isn't safe to hand back:
|
|
102
|
+
|
|
103
|
+
```scala
|
|
104
|
+
import zio.blocks.schema._
|
|
105
|
+
import zio.blocks.schema.binding._
|
|
106
|
+
|
|
107
|
+
case class Person(name: String, age: Int)
|
|
108
|
+
object Person {
|
|
109
|
+
implicit val schema: Schema[Person] = Schema.derived
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
val dynamicSchema = Person.schema.toDynamicSchema
|
|
113
|
+
|
|
114
|
+
try dynamicSchema.rebind[Person](BindingResolver.empty)
|
|
115
|
+
catch {
|
|
116
|
+
case e: RebindException =>
|
|
117
|
+
// path: the DynamicOptic where the lookup failed
|
|
118
|
+
// typeId: the missing type
|
|
119
|
+
// expectedKind: "Record", "Variant", "Sequence", "Map", "Dynamic", "Primitive", or "Wrapper"
|
|
120
|
+
println(s"${e.expectedKind} binding missing for ${e.typeId.fullName} at ${e.path}")
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Writing a Full `ReflectTransformer`: Type Class Derivation
|
|
125
|
+
|
|
126
|
+
`OnlyMetadata` covers every transformer described so far, but the interface exists in full for a reason: automatic type class derivation implements every `transformX` method directly, because deriving an instance for a `Record` needs the already-derived instances of its *fields*, not just a metadata swap. `DerivationBuilder.derive` walks a bound schema with a custom `ReflectTransformer[Binding, BindingInstance[TC, ?, ?]]` that, at each node, either substitutes a user-supplied override or invokes a [`Deriver[TC]`](./type-class-derivation.md) with the node's already-transformed children, then pairs the result with the original binding. This is the pattern to reach for when a transformation genuinely depends on more than the metadata at a single node.
|
|
127
|
+
|
|
128
|
+
## Cycle Safety and the Transform Cache
|
|
129
|
+
|
|
130
|
+
A recursive schema — one whose `Reflect` tree refers back to itself — reaches that self-reference through a `Reflect.Deferred` node. `Deferred#transform` doesn't call any `transformX` method; it looks up the pair `(this Deferred, this transformer)` in a per-thread memoization map first, and only builds a new deferred node on a miss. This is what makes `Reflect#transform` terminate on a recursive schema, and what lets a `Deferred` reached through multiple paths share one transformed result instead of being rebuilt repeatedly.
|
|
131
|
+
|
|
132
|
+
The two public entry points — `Reflect#noBinding` and `DynamicSchema#rebind` — wrap their call in an internal scope that clears this cache once the outermost `Reflect#transform` finishes, so a transformer's captured state (override maps, resolvers) doesn't stay pinned on the thread once its call returns. Reaching either entry point is enough to get this for free; it isn't something you configure.
|
|
133
|
+
|
|
134
|
+
## See Also
|
|
135
|
+
|
|
136
|
+
- [Reflect](./reflect.md) — the tree `ReflectTransformer` rewrites, and its eight node kinds
|
|
137
|
+
- [Binding](./binding.md) — what `F`/`G` are in practice: `Binding` and `NoBinding`
|
|
138
|
+
- [BindingResolver](./binding-resolver.md) — the lookup `RebindTransformer` delegates to
|
|
139
|
+
- [DynamicSchema](./dynamic-schema.md) — `Schema#toDynamicSchema` and `DynamicSchema#rebind`, the public entry points
|
|
140
|
+
- [Type Class Derivation](./type-class-derivation.md) — the fullest real use of the interface
|
|
@@ -39,13 +39,13 @@ The bidirectional data flow looks like this:
|
|
|
39
39
|
`As` is part of `zio-blocks-schema`:
|
|
40
40
|
|
|
41
41
|
```scala
|
|
42
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
42
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
For Scala.js and Scala Native, use `%%%`:
|
|
46
46
|
|
|
47
47
|
```scala
|
|
48
|
-
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.
|
|
48
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.56"
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Supported Scala versions: 2.13.x and 3.x.
|
|
@@ -240,7 +240,7 @@ trait As[A, B] {
|
|
|
240
240
|
|
|
241
241
|
```scala
|
|
242
242
|
val revAs: As[LongBox, IntBox] = boxAs.reverse
|
|
243
|
-
// revAs: As[LongBox, IntBox] = zio.blocks.schema.As$$anon$1@
|
|
243
|
+
// revAs: As[LongBox, IntBox] = zio.blocks.schema.As$$anon$1@6ba3739b
|
|
244
244
|
|
|
245
245
|
revAs.into(LongBox(5L))
|
|
246
246
|
// res11: Either[SchemaError, IntBox] = Right(IntBox(5))
|
|
@@ -302,7 +302,7 @@ We import `As.reverseInto` and use it to obtain the reverse `Into[Int, String]`:
|
|
|
302
302
|
import As.reverseInto
|
|
303
303
|
|
|
304
304
|
val intToStr: Into[Int, String] = reverseInto[String, Int]
|
|
305
|
-
// intToStr: Into[Int, String] = zio.blocks.schema.AsLowPriorityImplicits$$Lambda
|
|
305
|
+
// intToStr: Into[Int, String] = zio.blocks.schema.AsLowPriorityImplicits$$Lambda/0x000000003aef9a10@fef590
|
|
306
306
|
intToStr.into(42)
|
|
307
307
|
// res14: Either[SchemaError, String] = Right("42")
|
|
308
308
|
```
|
|
@@ -69,13 +69,13 @@ Compare this to a manual implementation:
|
|
|
69
69
|
`Into` is part of the `zio-blocks-schema` module:
|
|
70
70
|
|
|
71
71
|
```scala
|
|
72
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
72
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
For Scala.js:
|
|
76
76
|
|
|
77
77
|
```scala
|
|
78
|
-
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.
|
|
78
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.56"
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
Supported Scala versions: 2.13.x and 3.x.
|
|
@@ -76,13 +76,13 @@ val result: Either[OpticCheck, Seq[Boolean]] = combined.eval(alice)
|
|
|
76
76
|
## Installation
|
|
77
77
|
|
|
78
78
|
```scala
|
|
79
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.
|
|
79
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.56"
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
For cross-platform (Scala.js):
|
|
83
83
|
|
|
84
84
|
```scala
|
|
85
|
-
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.
|
|
85
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-schema" % "0.0.56"
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
Supported Scala versions: 2.13.x and 3.x.
|