@zio.dev/zio-blocks 0.0.33 → 0.0.51
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/guides/compile-time-resource-safety-with-scope.md +16 -17
- package/guides/getting-started-with-mux.md +1507 -0
- package/guides/query-dsl-extending.md +161 -102
- package/guides/query-dsl-fluent-builder.md +217 -157
- package/guides/query-dsl-reified-optics.md +12 -10
- package/guides/query-dsl-sql.md +246 -165
- package/guides/telemetry-guide.md +1069 -0
- package/guides/zio-schema-migration.md +29 -22
- package/index.md +292 -50
- package/package.json +1 -1
- package/plans/config-follow-up-prs.md +188 -0
- package/plans/config-pr-assessment-roadmap.md +310 -0
- package/reference/MuxDataFlow.jsx +250 -0
- package/reference/async.md +651 -0
- package/reference/chunk.md +3533 -308
- package/reference/codegen/case-class.md +436 -0
- package/reference/codegen/emitter-config.md +383 -0
- package/reference/codegen/examples.md +664 -0
- package/reference/codegen/field.md +316 -0
- package/reference/codegen/index.md +317 -0
- package/reference/codegen/scala-emitter.md +392 -0
- package/reference/codegen/scala-file.md +276 -0
- package/reference/codegen/sealed-trait.md +408 -0
- package/reference/codegen/type-definition.md +340 -0
- package/reference/codegen/type-ref.md +201 -0
- package/reference/combinators.md +347 -117
- package/reference/config.md +158 -0
- package/reference/context.md +4 -4
- package/reference/datastar.md +346 -0
- package/reference/docs.md +1461 -345
- package/reference/endpoint/auth-type.md +146 -0
- package/reference/endpoint/endpoint.md +297 -0
- package/reference/endpoint/http-codec.md +249 -0
- package/reference/endpoint/index.md +825 -0
- package/reference/endpoint/path-codec.md +237 -0
- package/reference/endpoint/route-pattern.md +196 -0
- package/reference/endpoint/route-tree.md +111 -0
- package/reference/endpoint/segment-codec.md +212 -0
- package/reference/html.md +1120 -0
- package/reference/htmx/attribute-values.md +359 -0
- package/reference/htmx/hx-encoding.md +111 -0
- package/reference/htmx/hx-params.md +204 -0
- package/reference/htmx/hx-swap.md +276 -0
- package/reference/htmx/hx-sync.md +251 -0
- package/reference/htmx/hx-target.md +314 -0
- package/reference/htmx/hx-trigger.md +457 -0
- package/reference/htmx/hx-url-update.md +239 -0
- package/reference/htmx/index.md +855 -0
- package/reference/http-model/index.md +47 -0
- package/reference/http-model/model.md +1481 -0
- package/reference/http-model/schema.md +747 -0
- package/reference/maybe.md +826 -0
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +823 -0
- package/reference/openapi.md +1351 -0
- package/reference/resource-management/defer-handle.md +1 -1
- package/reference/resource-management/resource.md +31 -2
- package/reference/resource-management/scope.md +28 -12
- package/reference/resource-management/wire.md +3 -7
- package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
- package/reference/ringbuffer/MpscDiagram.jsx +618 -0
- package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
- package/reference/ringbuffer/SpscDiagram.jsx +677 -0
- package/reference/ringbuffer/advanced.mdx +109 -0
- package/reference/ringbuffer/index.mdx +145 -0
- package/reference/ringbuffer/mpmc.mdx +151 -0
- package/reference/ringbuffer/mpsc.mdx +132 -0
- package/reference/ringbuffer/spmc.mdx +108 -0
- package/reference/ringbuffer/spsc.mdx +344 -0
- package/reference/{allows.md → schema/allows.md} +4 -4
- package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
- package/reference/{binding.md → schema/binding.md} +2 -3
- package/reference/schema/built-in-codecs/avro.md +451 -0
- package/reference/schema/built-in-codecs/bson.md +480 -0
- package/reference/schema/built-in-codecs/csv.md +564 -0
- package/reference/schema/built-in-codecs/index.md +77 -0
- package/reference/schema/built-in-codecs/json/index.md +295 -0
- package/reference/schema/built-in-codecs/json/json-config.md +217 -0
- package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
- package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
- package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
- package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
- package/reference/schema/built-in-codecs/messagepack.md +508 -0
- package/reference/schema/built-in-codecs/thrift.md +433 -0
- package/reference/schema/built-in-codecs/toon.md +1078 -0
- package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
- package/reference/schema/built-in-codecs/yaml.md +552 -0
- package/reference/{codec.md → schema/codec.md} +10 -10
- package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
- package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
- package/reference/schema/format.md +92 -0
- package/reference/schema/index.md +50 -0
- package/reference/schema/migration.md +297 -0
- package/reference/{modifier.md → schema/modifier.md} +58 -7
- package/reference/{optics.md → schema/optics.md} +2 -2
- package/reference/{patch.md → schema/patch.md} +1 -1
- package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
- package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
- package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
- package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
- package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
- package/reference/{schema.md → schema/schema.md} +12 -0
- package/reference/{structural-types.md → schema/structural-types.md} +1 -1
- package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
- package/reference/smithy.md +533 -0
- package/reference/sql/db-codec-deriver.md +71 -0
- package/reference/sql/db-codec.md +687 -0
- package/reference/sql/db-con.md +271 -0
- package/reference/sql/db-connection.md +153 -0
- package/reference/sql/db-param-writer.md +77 -0
- package/reference/sql/db-param.md +66 -0
- package/reference/sql/db-result-reader.md +146 -0
- package/reference/sql/db-tx.md +82 -0
- package/reference/sql/db-value.md +41 -0
- package/reference/sql/ddl.md +85 -0
- package/reference/sql/frag.md +254 -0
- package/reference/sql/index.md +341 -0
- package/reference/sql/repo.md +600 -0
- package/reference/sql/sql-dialect.md +73 -0
- package/reference/sql/sql-logger.md +62 -0
- package/reference/sql/sql-name-mapper.md +70 -0
- package/reference/sql/table-metadata.md +134 -0
- package/reference/sql/table.md +448 -0
- package/reference/sql/transactor-zio.md +399 -0
- package/reference/sql/transactor.md +353 -0
- package/reference/sql-zio.md +112 -0
- package/reference/streams/concurrent-operators.md +106 -0
- package/reference/streams/index.md +653 -0
- package/reference/streams/pipeline.md +718 -0
- package/reference/streams/reader.md +1284 -0
- package/reference/streams/scala-2-compatibility.md +55 -0
- package/reference/streams/sink.md +1426 -0
- package/reference/streams/stream.md +2526 -0
- package/reference/streams/writer.md +1045 -0
- package/reference/streams/zero-boxing.md +275 -0
- package/reference/telemetry.md +693 -0
- package/reference/typeid.md +5 -19
- package/sidebars.js +238 -43
- package/reference/formats.md +0 -694
- package/reference/http-model.md +0 -1716
- package/reference/streams.md +0 -989
- package/ringbuffer.md +0 -249
- /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
- /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
- /package/reference/{lazy.md → schema/lazy.md} +0 -0
- /package/reference/{reflect.md → schema/reflect.md} +0 -0
- /package/reference/{registers.md → schema/registers.md} +0 -0
- /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
- /package/reference/{syntax.md → schema/syntax.md} +0 -0
- /package/reference/{validation.md → schema/validation.md} +0 -0
|
@@ -3,13 +3,20 @@ id: path-interpolator
|
|
|
3
3
|
title: "Path Interpolator"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
:::note
|
|
7
|
+
To use the path interpolator `p"..."`, you must import the schema package: `import zio.blocks.schema._`
|
|
8
|
+
:::
|
|
9
|
+
|
|
6
10
|
The path interpolator `p"..."` is a compile-time string interpolator for constructing `DynamicOptic` instances in ZIO Blocks. It provides a clean, concise syntax for building optic paths that navigate through complex data structures, with all parsing and validation happening at compile time for zero runtime overhead.
|
|
7
11
|
|
|
8
12
|
**Why use the path interpolator?**
|
|
9
13
|
|
|
10
14
|
Instead of manually constructing optics like this:
|
|
11
15
|
|
|
16
|
+
|
|
12
17
|
```scala
|
|
18
|
+
import zio.blocks.schema._
|
|
19
|
+
|
|
13
20
|
DynamicOptic(Vector(
|
|
14
21
|
DynamicOptic.Node.Field("users"),
|
|
15
22
|
DynamicOptic.Node.Elements,
|
|
@@ -20,6 +27,8 @@ DynamicOptic(Vector(
|
|
|
20
27
|
You can write:
|
|
21
28
|
|
|
22
29
|
```scala
|
|
30
|
+
import zio.blocks.schema._
|
|
31
|
+
|
|
23
32
|
p".users[*].email"
|
|
24
33
|
```
|
|
25
34
|
|
|
@@ -46,9 +55,11 @@ val path = p".users[0].name"
|
|
|
46
55
|
|
|
47
56
|
## Syntax Reference
|
|
48
57
|
|
|
58
|
+
This section documents the complete path syntax with examples for each component type.
|
|
59
|
+
|
|
49
60
|
### Field Access
|
|
50
61
|
|
|
51
|
-
Access fields in records using dot notation. The leading dot is optional
|
|
62
|
+
Access fields in records using dot notation. The leading dot is optional:
|
|
52
63
|
|
|
53
64
|
```scala
|
|
54
65
|
// With leading dot
|
|
@@ -64,7 +75,7 @@ p".user.address.street"
|
|
|
64
75
|
// Equivalent to: Field("user") → Field("address") → Field("street")
|
|
65
76
|
```
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
Field names can also include special characters and keywords:
|
|
68
79
|
|
|
69
80
|
```scala
|
|
70
81
|
p"._private" // Fields starting with underscore
|
|
@@ -77,7 +88,7 @@ p".true" // Keywords as field names (true, false, null)
|
|
|
77
88
|
|
|
78
89
|
Access sequence elements by index, multiple indices, or ranges.
|
|
79
90
|
|
|
80
|
-
|
|
91
|
+
To access a single element by index:
|
|
81
92
|
|
|
82
93
|
```scala
|
|
83
94
|
p"[0]" // AtIndex(0)
|
|
@@ -85,7 +96,7 @@ p"[42]" // AtIndex(42)
|
|
|
85
96
|
p"[2147483647]" // AtIndex(Int.MaxValue)
|
|
86
97
|
```
|
|
87
98
|
|
|
88
|
-
|
|
99
|
+
To access multiple elements at specific indices:
|
|
89
100
|
|
|
90
101
|
```scala
|
|
91
102
|
p"[0,1,2]" // AtIndices(Seq(0, 1, 2))
|
|
@@ -93,7 +104,7 @@ p"[0, 2, 5]" // AtIndices(Seq(0, 2, 5)) - spaces allowed
|
|
|
93
104
|
p"[5,2,8,1]" // Order preserved
|
|
94
105
|
```
|
|
95
106
|
|
|
96
|
-
|
|
107
|
+
To select a range of consecutive elements:
|
|
97
108
|
|
|
98
109
|
```scala
|
|
99
110
|
p"[0:5]" // AtIndices(Seq(0, 1, 2, 3, 4))
|
|
@@ -105,14 +116,14 @@ p"[10:5]" // AtIndices(Seq.empty) - inverted range
|
|
|
105
116
|
|
|
106
117
|
### Element Selectors
|
|
107
118
|
|
|
108
|
-
Select all elements in a sequence using wildcard syntax
|
|
119
|
+
Select all elements in a sequence using wildcard syntax:
|
|
109
120
|
|
|
110
121
|
```scala
|
|
111
122
|
p"[*]" // Elements - all elements
|
|
112
123
|
p"[:*]" // Elements - alternative syntax
|
|
113
124
|
```
|
|
114
125
|
|
|
115
|
-
|
|
126
|
+
To navigate nested sequences:
|
|
116
127
|
|
|
117
128
|
```scala
|
|
118
129
|
p"[*][*]" // Nested sequences: all elements of all elements
|
|
@@ -123,7 +134,7 @@ p"[*][0]" // First element of each sequence
|
|
|
123
134
|
|
|
124
135
|
Access map values by key, where keys can be strings, integers, booleans, or characters.
|
|
125
136
|
|
|
126
|
-
|
|
137
|
+
To use string keys:
|
|
127
138
|
|
|
128
139
|
```scala
|
|
129
140
|
p"""{"host"}""" // AtMapKey(String("host"))
|
|
@@ -133,7 +144,7 @@ p"""{"🎉"}""" // Emoji keys
|
|
|
133
144
|
p"""{""}""" // Empty string key
|
|
134
145
|
```
|
|
135
146
|
|
|
136
|
-
|
|
147
|
+
To use integer keys:
|
|
137
148
|
|
|
138
149
|
```scala
|
|
139
150
|
p"{42}" // AtMapKey(Int(42))
|
|
@@ -143,14 +154,14 @@ p"{2147483647}" // AtMapKey(Int.MaxValue)
|
|
|
143
154
|
p"{-2147483648}" // AtMapKey(Int.MinValue)
|
|
144
155
|
```
|
|
145
156
|
|
|
146
|
-
|
|
157
|
+
To use boolean keys:
|
|
147
158
|
|
|
148
159
|
```scala
|
|
149
160
|
p"{true}" // AtMapKey(Boolean(true))
|
|
150
161
|
p"{false}" // AtMapKey(Boolean(false))
|
|
151
162
|
```
|
|
152
163
|
|
|
153
|
-
|
|
164
|
+
To use character keys:
|
|
154
165
|
|
|
155
166
|
```scala
|
|
156
167
|
p"{'a'}" // AtMapKey(Char('a'))
|
|
@@ -158,7 +169,7 @@ p"{' '}" // AtMapKey(Char(' '))
|
|
|
158
169
|
p"{'9'}" // AtMapKey(Char('9'))
|
|
159
170
|
```
|
|
160
171
|
|
|
161
|
-
|
|
172
|
+
To use multiple keys of the same or mixed types:
|
|
162
173
|
|
|
163
174
|
```scala
|
|
164
175
|
p"""{"foo", "bar", "baz"}""" // AtMapKeys(Seq(...))
|
|
@@ -172,7 +183,7 @@ p"""{"s", 'c', 42, true}""" // All supported types
|
|
|
172
183
|
|
|
173
184
|
### Map Selectors
|
|
174
185
|
|
|
175
|
-
Select all keys or all values in a map
|
|
186
|
+
Select all keys or all values in a map:
|
|
176
187
|
|
|
177
188
|
```scala
|
|
178
189
|
p"{*}" // MapValues - all values
|
|
@@ -180,7 +191,7 @@ p"{:*}" // MapValues - alternative syntax
|
|
|
180
191
|
p"{*:}" // MapKeys - all keys
|
|
181
192
|
```
|
|
182
193
|
|
|
183
|
-
|
|
194
|
+
To apply map selectors to nested maps:
|
|
184
195
|
|
|
185
196
|
```scala
|
|
186
197
|
p"{*}{*}" // Nested maps: all values of all values
|
|
@@ -189,7 +200,7 @@ p"{*:}{*:}" // All keys of all keys
|
|
|
189
200
|
|
|
190
201
|
### Variant Case Access
|
|
191
202
|
|
|
192
|
-
Navigate into a specific variant case using angle brackets
|
|
203
|
+
Navigate into a specific variant case using angle brackets:
|
|
193
204
|
|
|
194
205
|
```scala
|
|
195
206
|
p"<Left>" // Case("Left")
|
|
@@ -198,7 +209,7 @@ p"<Some>" // Case("Some")
|
|
|
198
209
|
p"<None>" // Case("None")
|
|
199
210
|
```
|
|
200
211
|
|
|
201
|
-
|
|
212
|
+
Variant case names can include special characters and keywords:
|
|
202
213
|
|
|
203
214
|
```scala
|
|
204
215
|
p"<_Empty>" // Cases starting with underscore
|
|
@@ -206,7 +217,7 @@ p"<Case1>" // Cases with digits
|
|
|
206
217
|
p"<café>" // Unicode case names
|
|
207
218
|
```
|
|
208
219
|
|
|
209
|
-
|
|
220
|
+
To navigate nested variant cases:
|
|
210
221
|
|
|
211
222
|
```scala
|
|
212
223
|
p"<A><B><C>" // Nested variants
|
|
@@ -216,7 +227,7 @@ p"<A><B><C>" // Nested variants
|
|
|
216
227
|
|
|
217
228
|
Search for values matching a schema pattern anywhere in a data structure using the `#` prefix.
|
|
218
229
|
|
|
219
|
-
|
|
230
|
+
To search for nominal types by name:
|
|
220
231
|
|
|
221
232
|
```scala
|
|
222
233
|
p"#Person" // Find all values of type Person
|
|
@@ -224,7 +235,7 @@ p"#User" // Find all values of type User
|
|
|
224
235
|
p"#Address" // Find all values of type Address
|
|
225
236
|
```
|
|
226
237
|
|
|
227
|
-
|
|
238
|
+
To search for primitive types:
|
|
228
239
|
|
|
229
240
|
```scala
|
|
230
241
|
p"#string" // Find all string values
|
|
@@ -233,7 +244,7 @@ p"#boolean" // Find all boolean values
|
|
|
233
244
|
p"#uuid" // Find all UUID values
|
|
234
245
|
```
|
|
235
246
|
|
|
236
|
-
|
|
247
|
+
To search for records with specific field structures:
|
|
237
248
|
|
|
238
249
|
```scala
|
|
239
250
|
p"#record { name: string }" // Find records with a string 'name' field
|
|
@@ -241,13 +252,13 @@ p"#record { name: string, age: int }" // Find records with both fields
|
|
|
241
252
|
p"#record { items: list(Person) }" // Nested schema
|
|
242
253
|
```
|
|
243
254
|
|
|
244
|
-
|
|
255
|
+
To search for variants with specific case structures:
|
|
245
256
|
|
|
246
257
|
```scala
|
|
247
258
|
p"#variant { Left: int, Right: string }" // Find Either-like variants
|
|
248
259
|
```
|
|
249
260
|
|
|
250
|
-
|
|
261
|
+
To search for collection types:
|
|
251
262
|
|
|
252
263
|
```scala
|
|
253
264
|
p"#list(string)" // Find lists of strings
|
|
@@ -256,13 +267,13 @@ p"#map(string, int)" // Find maps from string to int
|
|
|
256
267
|
p"#option(Person)" // Find optional Person values
|
|
257
268
|
```
|
|
258
269
|
|
|
259
|
-
|
|
270
|
+
To match any value regardless of type:
|
|
260
271
|
|
|
261
272
|
```scala
|
|
262
273
|
p"#_" // Find any value (matches everything)
|
|
263
274
|
```
|
|
264
275
|
|
|
265
|
-
|
|
276
|
+
To combine path navigation with schema search:
|
|
266
277
|
|
|
267
278
|
```scala
|
|
268
279
|
p".users#Person" // Search for Person in users field
|
|
@@ -284,7 +295,7 @@ String and character literals support standard escape sequences:
|
|
|
284
295
|
| `\"` | `"` | Double quote |
|
|
285
296
|
| `\\` | `\` | Backslash |
|
|
286
297
|
|
|
287
|
-
|
|
298
|
+
Here are some examples of escape sequences in use:
|
|
288
299
|
|
|
289
300
|
```scala
|
|
290
301
|
p"""{"foo\nbar"}""" // String key with newline
|
|
@@ -300,6 +311,8 @@ Combine different path elements to navigate complex nested structures.
|
|
|
300
311
|
|
|
301
312
|
### Field → Sequence
|
|
302
313
|
|
|
314
|
+
Access sequence elements by index or range through a field:
|
|
315
|
+
|
|
303
316
|
```scala
|
|
304
317
|
p".items[0]" // First item
|
|
305
318
|
p".items[*]" // All items
|
|
@@ -309,6 +322,8 @@ p".items[0:5]" // Items 0 through 4
|
|
|
309
322
|
|
|
310
323
|
### Field → Map
|
|
311
324
|
|
|
325
|
+
Access map values by key through a field:
|
|
326
|
+
|
|
312
327
|
```scala
|
|
313
328
|
p""".config{"host"}""" // Map lookup
|
|
314
329
|
p".settings{42}" // Integer key
|
|
@@ -318,6 +333,8 @@ p".lookup{*:}" // All map keys
|
|
|
318
333
|
|
|
319
334
|
### Field → Variant
|
|
320
335
|
|
|
336
|
+
Navigate into variant cases through a field:
|
|
337
|
+
|
|
321
338
|
```scala
|
|
322
339
|
p".result<Success>" // Variant case
|
|
323
340
|
p".response<Ok>" // HTTP response variant
|
|
@@ -325,6 +342,8 @@ p".response<Ok>" // HTTP response variant
|
|
|
325
342
|
|
|
326
343
|
### Nested Structures
|
|
327
344
|
|
|
345
|
+
Combine different path elements to build complex navigation through nested data:
|
|
346
|
+
|
|
328
347
|
```scala
|
|
329
348
|
// Record in sequence
|
|
330
349
|
p".users[0].name"
|
|
@@ -345,6 +364,8 @@ p".response<Ok>.body"
|
|
|
345
364
|
|
|
346
365
|
### Deeply Nested Paths
|
|
347
366
|
|
|
367
|
+
Chain multiple path operators for deeply nested data structures:
|
|
368
|
+
|
|
348
369
|
```scala
|
|
349
370
|
// Complex nested navigation
|
|
350
371
|
p""".root.children[*].metadata{"tags"}[0]"""
|
|
@@ -359,35 +380,47 @@ p""".a[0]{"k"}<V>.b[*]{*}.c{*:}"""
|
|
|
359
380
|
|
|
360
381
|
## Root and Empty Paths
|
|
361
382
|
|
|
383
|
+
An empty path refers to the root of the data structure:
|
|
384
|
+
|
|
362
385
|
```scala
|
|
363
386
|
p"" // Empty path = root
|
|
364
|
-
// Equivalent to: DynamicOptic.root
|
|
365
|
-
// Equivalent to: DynamicOptic(Vector.empty)
|
|
366
387
|
```
|
|
367
388
|
|
|
368
389
|
## Compile-Time Safety
|
|
369
390
|
|
|
370
391
|
The path interpolator **rejects runtime interpolation** to prevent unsafe dynamic path construction.
|
|
371
392
|
|
|
372
|
-
|
|
393
|
+
### Examples of Safety Checks
|
|
394
|
+
|
|
395
|
+
Runtime interpolation will fail to compile:
|
|
373
396
|
|
|
374
397
|
```scala
|
|
398
|
+
import zio.blocks.schema._
|
|
399
|
+
|
|
375
400
|
val fieldName = "email"
|
|
376
401
|
val path = p".$fieldName"
|
|
377
402
|
// Error: Path interpolator does not support runtime arguments.
|
|
378
|
-
//
|
|
403
|
+
// error:
|
|
404
|
+
// Path interpolator does not support runtime arguments. Use only literal strings like p".field[0]"
|
|
405
|
+
// val path = p".$fieldName"
|
|
406
|
+
// ^^^^^^^^^^^^^
|
|
379
407
|
```
|
|
380
408
|
|
|
381
|
-
|
|
409
|
+
Array indices also cannot be interpolated at runtime:
|
|
382
410
|
|
|
383
411
|
```scala
|
|
412
|
+
import zio.blocks.schema._
|
|
413
|
+
|
|
384
414
|
val idx = 5
|
|
385
415
|
val path = p"[$idx]"
|
|
386
416
|
// Error: Path interpolator does not support runtime arguments.
|
|
387
|
-
//
|
|
417
|
+
// error:
|
|
418
|
+
// Path interpolator does not support runtime arguments. Use only literal strings like p".field[0]"
|
|
419
|
+
// val path = p"[$idx]"
|
|
420
|
+
// ^^^^^^^^^
|
|
388
421
|
```
|
|
389
422
|
|
|
390
|
-
|
|
423
|
+
Instead, use only literal strings at compile time:
|
|
391
424
|
|
|
392
425
|
```scala
|
|
393
426
|
val path = p".users[0].email" // ✓ Works
|
|
@@ -395,31 +428,46 @@ val path = p".users[0].email" // ✓ Works
|
|
|
395
428
|
|
|
396
429
|
### Parse Error Examples
|
|
397
430
|
|
|
398
|
-
Invalid syntax is caught at compile time:
|
|
431
|
+
Invalid syntax is caught at compile time. Here are examples of common errors:
|
|
399
432
|
|
|
400
433
|
```scala
|
|
434
|
+
import zio.blocks.schema._
|
|
435
|
+
|
|
401
436
|
// Unterminated string
|
|
402
437
|
p"""{"foo"""
|
|
403
|
-
// Error: Unterminated string literal
|
|
438
|
+
// Error: Unterminated string literal
|
|
404
439
|
|
|
405
|
-
// Invalid escape sequence
|
|
440
|
+
// Invalid escape sequence
|
|
406
441
|
p"""{"foo\x"}"""
|
|
407
|
-
// Error: Invalid escape sequence
|
|
442
|
+
// Error: Invalid escape sequence
|
|
408
443
|
|
|
409
444
|
// Unexpected character
|
|
410
445
|
p".field@"
|
|
411
|
-
// Error: Unexpected character '@'
|
|
446
|
+
// Error: Unexpected character '@'
|
|
412
447
|
|
|
413
448
|
// Invalid identifier
|
|
414
449
|
p"."
|
|
415
|
-
// Error: Invalid identifier
|
|
450
|
+
// Error: Invalid identifier
|
|
451
|
+
// error:
|
|
452
|
+
// Unterminated string literal starting at position 1
|
|
453
|
+
// error:
|
|
454
|
+
// Invalid escape sequence '\x' at position 6
|
|
455
|
+
// error:
|
|
456
|
+
// Unexpected character '@' at position 6. Expected field, index, map, or variant accessor
|
|
457
|
+
// error:
|
|
458
|
+
// Invalid identifier at position 1
|
|
459
|
+
// error:
|
|
460
|
+
// Conflicting definitions:
|
|
461
|
+
// val path: zio.blocks.schema.DynamicOptic in object MdocApp at line 14 and
|
|
462
|
+
// val path: zio.blocks.schema.DynamicOptic in object MdocApp at line 114
|
|
463
|
+
//
|
|
416
464
|
```
|
|
417
465
|
|
|
418
466
|
## Performance
|
|
419
467
|
|
|
420
468
|
**Zero Runtime Overhead**
|
|
421
469
|
|
|
422
|
-
All path parsing and validation occurs at **compile time**. The interpolator generates the exact same bytecode as manual `DynamicOptic` construction:
|
|
470
|
+
All path parsing and validation occurs at **compile time**. The interpolator generates the exact same bytecode as manual `DynamicOptic` construction. Here's the comparison:
|
|
423
471
|
|
|
424
472
|
```scala
|
|
425
473
|
// These produce identical bytecode:
|
|
@@ -436,8 +484,12 @@ There is **no runtime parsing**, **no reflection**, and **no performance penalty
|
|
|
436
484
|
|
|
437
485
|
## Examples
|
|
438
486
|
|
|
487
|
+
This section shows practical examples of using the path interpolator with realistic data structures.
|
|
488
|
+
|
|
439
489
|
### Accessing Nested Fields
|
|
440
490
|
|
|
491
|
+
To access deeply nested fields in a data structure:
|
|
492
|
+
|
|
441
493
|
```scala
|
|
442
494
|
import zio.blocks.schema._
|
|
443
495
|
|
|
@@ -447,14 +499,18 @@ case class Person(name: String, age: Int, address: Address)
|
|
|
447
499
|
// Access nested street field
|
|
448
500
|
val streetPath = p".address.street"
|
|
449
501
|
|
|
450
|
-
// Use with DynamicValue
|
|
451
|
-
val person =
|
|
502
|
+
// Use with DynamicValue - example (requires actual DynamicValue instance)
|
|
503
|
+
val person: DynamicValue = ???
|
|
452
504
|
val street = person.get(streetPath)
|
|
453
505
|
```
|
|
454
506
|
|
|
455
507
|
### Working with Collections
|
|
456
508
|
|
|
509
|
+
To work with sequences and access elements by index or range:
|
|
510
|
+
|
|
457
511
|
```scala
|
|
512
|
+
import zio.blocks.schema._
|
|
513
|
+
|
|
458
514
|
case class User(id: Int, email: String, tags: Seq[String])
|
|
459
515
|
case class Company(name: String, users: Seq[User])
|
|
460
516
|
|
|
@@ -470,7 +526,11 @@ val specificUsersPath = p".users[0,2,5]"
|
|
|
470
526
|
|
|
471
527
|
### Map Lookups
|
|
472
528
|
|
|
529
|
+
To work with map data structures and access values by key:
|
|
530
|
+
|
|
473
531
|
```scala
|
|
532
|
+
import zio.blocks.schema._
|
|
533
|
+
|
|
474
534
|
case class Config(
|
|
475
535
|
settings: Map[String, String],
|
|
476
536
|
ports: Map[Int, String]
|
|
@@ -491,13 +551,21 @@ val allPortsPath = p"ports{*:}"
|
|
|
491
551
|
|
|
492
552
|
### Variant Case Handling
|
|
493
553
|
|
|
554
|
+
To navigate variant cases in your data structures:
|
|
555
|
+
|
|
494
556
|
```scala
|
|
557
|
+
import zio.blocks.schema._
|
|
558
|
+
|
|
495
559
|
sealed trait Result[+A]
|
|
496
560
|
case class Success[A](value: A) extends Result[A]
|
|
497
561
|
case class Failure(error: String) extends Result[Nothing]
|
|
498
562
|
|
|
499
563
|
case class Response(result: Result[User])
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Use paths to navigate into specific cases:
|
|
500
567
|
|
|
568
|
+
```scala
|
|
501
569
|
// Navigate into Success case
|
|
502
570
|
val successValuePath = p".result<Success>.value"
|
|
503
571
|
|
|
@@ -507,7 +575,11 @@ val errorPath = p".result<Failure>.error"
|
|
|
507
575
|
|
|
508
576
|
### Real-World Example: API Response
|
|
509
577
|
|
|
578
|
+
To extract specific data from complex nested API response structures:
|
|
579
|
+
|
|
510
580
|
```scala
|
|
581
|
+
import zio.blocks.schema._
|
|
582
|
+
|
|
511
583
|
case class Metadata(tags: Seq[String], version: Int)
|
|
512
584
|
case class Item(id: String, data: String, metadata: Metadata)
|
|
513
585
|
case class ApiResponse(
|
|
@@ -531,8 +603,12 @@ val apiKeyPath = p"""config{"api_key"}"""
|
|
|
531
603
|
|
|
532
604
|
## Before & After Comparison
|
|
533
605
|
|
|
606
|
+
This comparison shows how the path interpolator simplifies optic path construction compared to manual approaches.
|
|
607
|
+
|
|
534
608
|
### Manual Construction (Before)
|
|
535
609
|
|
|
610
|
+
Manual path construction requires verbose imports and careful construction:
|
|
611
|
+
|
|
536
612
|
```scala
|
|
537
613
|
import zio.blocks.schema.DynamicOptic
|
|
538
614
|
import zio.blocks.schema.DynamicOptic.Node
|
|
@@ -582,7 +658,7 @@ val path2 = p""".root.children[*].metadata{"tags"}[0]"""
|
|
|
582
658
|
val path3 = p"""data{"foo", "bar", 42}"""
|
|
583
659
|
```
|
|
584
660
|
|
|
585
|
-
|
|
661
|
+
The path interpolator provides significant benefits:
|
|
586
662
|
|
|
587
663
|
- **90% less code** for typical paths
|
|
588
664
|
- **Easier to read** and understand intent
|
|
@@ -592,9 +668,15 @@ val path3 = p"""data{"foo", "bar", 42}"""
|
|
|
592
668
|
|
|
593
669
|
## Practical Usage Patterns
|
|
594
670
|
|
|
671
|
+
These patterns demonstrate effective ways to use the path interpolator in real-world scenarios.
|
|
672
|
+
|
|
595
673
|
### Building Paths Dynamically (at Compile Time)
|
|
596
674
|
|
|
675
|
+
While runtime variables are not supported, you can compose literal paths at compile time:
|
|
676
|
+
|
|
597
677
|
```scala
|
|
678
|
+
import zio.blocks.schema._
|
|
679
|
+
|
|
598
680
|
// You can't use runtime variables, but you can compose literal paths:
|
|
599
681
|
val basePath = p".data.items"
|
|
600
682
|
val emailPath = basePath(p"[*].email")
|
|
@@ -603,26 +685,31 @@ val emailPath = basePath(p"[*].email")
|
|
|
603
685
|
|
|
604
686
|
### Working with DynamicValue
|
|
605
687
|
|
|
688
|
+
To navigate and extract values from dynamic data:
|
|
689
|
+
|
|
606
690
|
```scala
|
|
607
691
|
import zio.blocks.schema._
|
|
608
692
|
|
|
609
|
-
val data: DynamicValue =
|
|
693
|
+
val data: DynamicValue = ???
|
|
610
694
|
|
|
611
695
|
// Navigate and extract
|
|
612
696
|
val value = data.get(p".users[0].email")
|
|
613
697
|
|
|
614
|
-
// Update at path
|
|
615
|
-
val
|
|
698
|
+
// Update at path (using appropriate constructor)
|
|
699
|
+
val ageValue: DynamicValue = ???
|
|
700
|
+
val updated = data.set(p".users[0].age", ageValue)
|
|
616
701
|
```
|
|
617
702
|
|
|
618
703
|
### Integration with Schema Optics
|
|
619
704
|
|
|
705
|
+
To use path interpolators with schema-based optics:
|
|
706
|
+
|
|
620
707
|
```scala
|
|
621
708
|
import zio.blocks.schema._
|
|
622
709
|
|
|
623
|
-
case class
|
|
624
|
-
object
|
|
625
|
-
implicit val schema: Schema[
|
|
710
|
+
case class UserRecord(name: String, email: String)
|
|
711
|
+
object UserRecord extends CompanionOptics[UserRecord] {
|
|
712
|
+
implicit val schema: Schema[UserRecord] = Schema.derived
|
|
626
713
|
|
|
627
714
|
// Use path interpolator for complex lenses
|
|
628
715
|
val email = $(_.email)
|
|
@@ -634,23 +721,22 @@ val dynamicPath = p".email"
|
|
|
634
721
|
|
|
635
722
|
## Tips and Best Practices
|
|
636
723
|
|
|
637
|
-
1. **Use the leading dot for clarity**: While optional, `p".field"` is more explicit than `p"field"
|
|
724
|
+
1. **Use the leading dot for clarity**: While optional, `p".field"` is more explicit than `p"field"`:
|
|
638
725
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
3. **Compose paths when needed**: Break complex paths into reusable components
|
|
642
|
-
```scala
|
|
726
|
+
```scala mdoc:compile-only
|
|
643
727
|
val userPath = p".users[0]"
|
|
644
728
|
val emailPath = userPath(p".email")
|
|
645
729
|
```
|
|
646
730
|
|
|
647
|
-
4. **Use raw strings for map keys**: Triple-quoted strings avoid escape hell
|
|
648
|
-
|
|
731
|
+
4. **Use raw strings for map keys**: Triple-quoted strings avoid escape hell:
|
|
732
|
+
|
|
733
|
+
```scala mdoc:compile-only
|
|
649
734
|
p"""config{"api.key"}""" // Better than p"config{\"api.key\"}"
|
|
650
735
|
```
|
|
651
736
|
|
|
652
|
-
5. **Document complex paths**: Add comments explaining what nested paths navigate
|
|
653
|
-
|
|
737
|
+
5. **Document complex paths**: Add comments explaining what nested paths navigate:
|
|
738
|
+
|
|
739
|
+
```scala mdoc:compile-only
|
|
654
740
|
// Get the first tag from each user's metadata
|
|
655
741
|
val tagsPath = p".users[*].metadata.tags[0]"
|
|
656
742
|
```
|
|
@@ -670,29 +756,36 @@ These limitations ensure compile-time safety and zero runtime overhead.
|
|
|
670
756
|
|
|
671
757
|
```scala
|
|
672
758
|
val optic = DynamicOptic.root.field("users").elements.field("email")
|
|
759
|
+
// optic: DynamicOptic = DynamicOptic(
|
|
760
|
+
// IndexedSeq(Field("users"), Elements, Field("email"))
|
|
761
|
+
// )
|
|
673
762
|
println(optic) // Output: .users[*].email
|
|
763
|
+
// .users[*].email
|
|
674
764
|
|
|
675
765
|
// The output can be copy-pasted into p"..."
|
|
676
766
|
val same = p".users[*].email"
|
|
767
|
+
// same: DynamicOptic = DynamicOptic(
|
|
768
|
+
// Vector(Field("users"), Elements, Field("email"))
|
|
769
|
+
// )
|
|
677
770
|
```
|
|
678
771
|
|
|
679
772
|
**Examples:**
|
|
680
773
|
|
|
681
|
-
| DynamicOptic Construction |
|
|
682
|
-
|
|
683
|
-
| `DynamicOptic.root.field("name")` |
|
|
684
|
-
| `DynamicOptic.root.field("address").field("street")` |
|
|
685
|
-
| `DynamicOptic.root.caseOf("Some")`
|
|
686
|
-
| `DynamicOptic.root.at(0)`
|
|
687
|
-
| `DynamicOptic.root.atIndices(0, 2, 5)`
|
|
688
|
-
| `DynamicOptic.elements`
|
|
689
|
-
| `DynamicOptic.root.atKey("host")`
|
|
690
|
-
| `DynamicOptic.root.atKey(80)`
|
|
691
|
-
| `DynamicOptic.mapValues`
|
|
692
|
-
| `DynamicOptic.mapKeys`
|
|
693
|
-
| `DynamicOptic.wrapped`
|
|
694
|
-
| `DynamicOptic.root.searchSchema(SchemaRepr.Nominal("Person"))`
|
|
695
|
-
| `DynamicOptic.root.searchSchema(SchemaRepr.Primitive("string"))`
|
|
774
|
+
| DynamicOptic Construction | Interpolator Syntax |
|
|
775
|
+
|------------------------------------------------------|---------------------|
|
|
776
|
+
| `DynamicOptic.root.field("name")` | `p".name"` |
|
|
777
|
+
| `DynamicOptic.root.field("address").field("street")` | `p".address.street"` |
|
|
778
|
+
| `DynamicOptic.root.caseOf("Some")` | `p"<Some>"` |
|
|
779
|
+
| `DynamicOptic.root.at(0)` | `p"[0]"` |
|
|
780
|
+
| `DynamicOptic.root.atIndices(0, 2, 5)` | `p"[0,2,5]"` |
|
|
781
|
+
| `DynamicOptic.elements` | `p"[*]"` |
|
|
782
|
+
| `DynamicOptic.root.atKey("host")` | `p"{"host"}"` |
|
|
783
|
+
| `DynamicOptic.root.atKey(80)` | `p"{80}"` |
|
|
784
|
+
| `DynamicOptic.mapValues` | `p"{*}"` |
|
|
785
|
+
| `DynamicOptic.mapKeys` | `p"{*:}"` |
|
|
786
|
+
| `DynamicOptic.wrapped` | `p".~"` |
|
|
787
|
+
| `DynamicOptic.root.searchSchema(SchemaRepr.Nominal("Person"))` | `p"#Person"` |
|
|
788
|
+
| `DynamicOptic.root.searchSchema(SchemaRepr.Primitive("string"))` | `p"#string"` |
|
|
696
789
|
|
|
697
790
|
## Summary
|
|
698
791
|
|