@zio.dev/zio-blocks 0.0.26 → 0.0.28
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/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 +1 -1
- package/guides/zio-schema-migration.md +1195 -0
- package/index.md +21 -12
- package/package.json +1 -1
- package/reference/allows.md +1377 -0
- package/reference/binding-resolver.md +469 -0
- package/reference/binding.md +1 -1
- package/reference/codec.md +10 -10
- package/reference/docs.md +1 -1
- package/reference/dynamic-optic.md +5 -0
- package/reference/dynamic-schema.md +602 -0
- package/reference/dynamic-value.md +5 -0
- package/reference/json-patch.md +803 -0
- package/reference/json.md +1 -1
- package/reference/media-type.md +2 -2
- package/reference/modifier.md +9 -9
- package/reference/patch.md +4 -0
- package/reference/schema-error.md +569 -0
- package/reference/schema-evolution/as.md +587 -0
- package/reference/schema-evolution/index.md +50 -0
- package/reference/schema-evolution/into.md +1027 -0
- package/reference/schema-expr.md +2 -2
- package/reference/schema.md +29 -0
- package/reference/structural-types.md +369 -0
- package/reference/type-class-derivation.md +329 -324
- package/reference/validation.md +1 -1
- package/reference/xml.md +1304 -0
- package/scope.md +241 -17
- package/sidebars.js +21 -1
- package/reference/schema-evolution.md +0 -540
package/scope.md
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
id: scope
|
|
3
|
+
title: "Scope"
|
|
4
|
+
---
|
|
2
5
|
|
|
3
6
|
`zio.blocks.scope` is a **compile-time safe, zero-cost** resource management library for **Scala 3** (and Scala 2.13). It prevents a large class of lifetime bugs by tagging allocated values with an *unnameable*, scope-specific type and restricting how those values may be used.
|
|
4
7
|
|
|
@@ -153,6 +156,58 @@ Scope.global.scoped { scope =>
|
|
|
153
156
|
}
|
|
154
157
|
```
|
|
155
158
|
|
|
159
|
+
##### N-ary `$`: accessing multiple scoped values at once
|
|
160
|
+
|
|
161
|
+
When a result depends on **two or more** scoped values simultaneously, use the N-ary overloads (`N = 2..5`):
|
|
162
|
+
|
|
163
|
+
```scala
|
|
164
|
+
$(sa1, sa2)((v1, v2) => v1.method(v2.result()))
|
|
165
|
+
$(sa1, sa2, sa3)((v1, v2, v3) => v1.query(v2.key()) + v3.tag())
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The same receiver-only grammar applies to every parameter: each `vi` may only appear as a method receiver (e.g., `vi.method()`). Feeding the *result* of one parameter to a method of another is permitted:
|
|
169
|
+
|
|
170
|
+
```scala
|
|
171
|
+
Scope.global.scoped { scope =>
|
|
172
|
+
import scope.*
|
|
173
|
+
val db: $[Database] = Resource.from[Database].allocate
|
|
174
|
+
val cache: $[Cache] = Resource.from[Cache].allocate
|
|
175
|
+
|
|
176
|
+
// d1 and d2 are both receivers; d2.key() produces a plain String arg
|
|
177
|
+
val result: String = $(db, cache)((d1, d2) => d1.query(d2.key()))
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Rejected at compile time (same rules as N=1, applied to each parameter independently):
|
|
182
|
+
|
|
183
|
+
```scala
|
|
184
|
+
$(db, cache)((d1, d2) => d2) // d2 returned directly
|
|
185
|
+
$(db, cache)((d1, d2) => store(d1)) // d1 passed as argument
|
|
186
|
+
$(db, cache)((d1, d2) => d1.method(d2)) // d2 as bare arg (not a receiver)
|
|
187
|
+
$(db, cache)((d1, d2) => () => d2.query()) // d2 captured in closure
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The error messages name the offending parameter:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
Parameter 2 ('d2') cannot be passed as an argument to a function or method.
|
|
194
|
+
Scoped values may only be used as a method receiver (e.g., d2.method()).
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Infix syntax** (`scope $ sa`) is only available for N=1. For N≥2, use unqualified syntax after `import scope.*`:
|
|
198
|
+
|
|
199
|
+
```scala
|
|
200
|
+
$(db, cache)((d, c) => d.query(c.key())) // ✓ unqualified
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**For N>5**, extract each value in sequence (all results are `Unscoped` strings/values and can be freely combined):
|
|
204
|
+
|
|
205
|
+
```scala
|
|
206
|
+
val q1 = $(db1)(_.query("a"))
|
|
207
|
+
val q2 = $(db2)(_.query("b"))
|
|
208
|
+
q1 + q2
|
|
209
|
+
```
|
|
210
|
+
|
|
156
211
|
---
|
|
157
212
|
|
|
158
213
|
### 3) `Resource[A]`: acquisition + finalization
|
|
@@ -417,16 +472,52 @@ A `scoped { ... }` block can only return pure data (or `Nothing`). Resources and
|
|
|
417
472
|
|
|
418
473
|
**Pragmatic safety.** The type-level tagging prevents *accidental* scope misuse in normal code, but it is not a security boundary. A determined developer can bypass it via `leak` (which emits a compiler warning), unsafe casts (`asInstanceOf`), or storing scoped references in mutable state (`var`).
|
|
419
474
|
|
|
420
|
-
### Closed-scope
|
|
475
|
+
### Closed-scope safety (runtime)
|
|
476
|
+
|
|
477
|
+
If a scope reference escapes its `scoped { }` block and an operation is attempted after closing, Scope throws `IllegalStateException` with a detailed, actionable error message:
|
|
478
|
+
|
|
479
|
+
- **`allocate`** on a closed scope:
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
── Scope Error ─────────────────────────────────────────────────────────────────
|
|
483
|
+
|
|
484
|
+
Cannot allocate resource: scope is already closed.
|
|
485
|
+
|
|
486
|
+
Scope: Scope.Child
|
|
487
|
+
|
|
488
|
+
What happened:
|
|
489
|
+
A call to allocate was made on a scope whose finalizers have
|
|
490
|
+
already run. The resource was never acquired.
|
|
491
|
+
|
|
492
|
+
Common causes:
|
|
493
|
+
• A scope reference escaped a scoped { } block (e.g. stored in a
|
|
494
|
+
field, captured in a Future or passed to another thread).
|
|
495
|
+
• close() was called on an OpenScope before all
|
|
496
|
+
allocations inside it completed.
|
|
497
|
+
|
|
498
|
+
Fix:
|
|
499
|
+
Call allocate only inside a live scoped { } block, or before
|
|
500
|
+
calling close() on an OpenScope.
|
|
421
501
|
|
|
422
|
-
|
|
502
|
+
// Correct usage:
|
|
503
|
+
Scope.global.scoped { scope =>
|
|
504
|
+
import scope.*
|
|
505
|
+
val db = allocate(Resource(new Database))
|
|
506
|
+
$(db)(_.query("SELECT 1"))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
- **`open()`** on a closed scope gives the same treatment, explaining that no child scope was created and directing the user to call `open()` only on a live scope.
|
|
513
|
+
|
|
514
|
+
- **`$`** on a closed scope explains that the resource may have already been released and accessing it would be undefined behaviour.
|
|
423
515
|
|
|
424
|
-
|
|
425
|
-
- `$` does not run the lambda when closed
|
|
426
|
-
- `defer` on a closed scope is ignored
|
|
427
|
-
- `scoped` creates a born-closed child if the parent is already closed
|
|
516
|
+
The following operations on a closed scope do **not** throw:
|
|
428
517
|
|
|
429
|
-
|
|
518
|
+
- `defer` — silently ignored (no-op)
|
|
519
|
+
- `scoped` — runs normally but creates a born-closed child scope
|
|
520
|
+
- `lower` — zero-cost cast, no closed check needed
|
|
430
521
|
|
|
431
522
|
### Thread ownership rule (JVM)
|
|
432
523
|
|
|
@@ -780,6 +871,112 @@ val appResource: Resource[App] =
|
|
|
780
871
|
|
|
781
872
|
---
|
|
782
873
|
|
|
874
|
+
## Common runtime errors (and what they mean)
|
|
875
|
+
|
|
876
|
+
These `IllegalStateException`s are thrown when a scope operation is attempted on a closed scope. Each message identifies the scope type, explains what went wrong, lists common causes, and shows a correct usage example.
|
|
877
|
+
|
|
878
|
+
### `allocate` on a closed scope
|
|
879
|
+
|
|
880
|
+
```
|
|
881
|
+
── Scope Error ─────────────────────────────────────────────────────────────────
|
|
882
|
+
|
|
883
|
+
Cannot allocate resource: scope is already closed.
|
|
884
|
+
|
|
885
|
+
Scope: Scope.Child
|
|
886
|
+
|
|
887
|
+
What happened:
|
|
888
|
+
A call to allocate was made on a scope whose finalizers have
|
|
889
|
+
already run. The resource was never acquired.
|
|
890
|
+
|
|
891
|
+
Common causes:
|
|
892
|
+
• A scope reference escaped a scoped { } block (e.g. stored in a
|
|
893
|
+
field, captured in a Future or passed to another thread).
|
|
894
|
+
• close() was called on an OpenScope before all
|
|
895
|
+
allocations inside it completed.
|
|
896
|
+
|
|
897
|
+
Fix:
|
|
898
|
+
Call allocate only inside a live scoped { } block, or before
|
|
899
|
+
calling close() on an OpenScope.
|
|
900
|
+
|
|
901
|
+
// Correct usage:
|
|
902
|
+
Scope.global.scoped { scope =>
|
|
903
|
+
import scope.*
|
|
904
|
+
val db = allocate(Resource(new Database))
|
|
905
|
+
$(db)(_.query("SELECT 1"))
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
### `open()` on a closed scope
|
|
912
|
+
|
|
913
|
+
```
|
|
914
|
+
── Scope Error ─────────────────────────────────────────────────────────────────
|
|
915
|
+
|
|
916
|
+
Cannot open child scope: scope is already closed.
|
|
917
|
+
|
|
918
|
+
Scope: Scope.Child
|
|
919
|
+
|
|
920
|
+
What happened:
|
|
921
|
+
A call to open() was made on a scope whose finalizers have
|
|
922
|
+
already run. No child scope was created.
|
|
923
|
+
|
|
924
|
+
Common causes:
|
|
925
|
+
• A scope reference escaped a scoped { } block and open()
|
|
926
|
+
was called after the block exited.
|
|
927
|
+
• close() was called on the parent OpenScope before
|
|
928
|
+
open() was called on it.
|
|
929
|
+
|
|
930
|
+
Fix:
|
|
931
|
+
Call open() only on a live (not yet closed) scope.
|
|
932
|
+
|
|
933
|
+
// Correct usage:
|
|
934
|
+
Scope.global.scoped { scope =>
|
|
935
|
+
import scope.*
|
|
936
|
+
val child = open()
|
|
937
|
+
$(child)(_.scope.allocate(Resource(new Database)))
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### `$` on a closed scope
|
|
944
|
+
|
|
945
|
+
```
|
|
946
|
+
── Scope Error ─────────────────────────────────────────────────────────────────
|
|
947
|
+
|
|
948
|
+
Cannot access scoped value: scope is already closed.
|
|
949
|
+
|
|
950
|
+
Scope: Scope.Child
|
|
951
|
+
|
|
952
|
+
What happened:
|
|
953
|
+
The $ operator was called on a scope whose finalizers have
|
|
954
|
+
already run. The underlying resource may have been released.
|
|
955
|
+
Accessing it would be undefined behavior.
|
|
956
|
+
|
|
957
|
+
Common causes:
|
|
958
|
+
• A $[A] value or its owning scope escaped a scoped { }
|
|
959
|
+
block (e.g. captured in a Future, stored in a field, or
|
|
960
|
+
passed to another thread).
|
|
961
|
+
• close() was called on an OpenScope that still has
|
|
962
|
+
live $[A] values being accessed.
|
|
963
|
+
|
|
964
|
+
Fix:
|
|
965
|
+
Ensure all $ calls occur strictly within the scoped { }
|
|
966
|
+
block that owns the value, and that the scope has not been closed.
|
|
967
|
+
|
|
968
|
+
// Correct usage:
|
|
969
|
+
Scope.global.scoped { scope =>
|
|
970
|
+
import scope.*
|
|
971
|
+
val db = allocate(Resource(new Database))
|
|
972
|
+
$(db)(_.query("SELECT 1")) // $ used inside the block
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
---
|
|
979
|
+
|
|
783
980
|
## Common compile errors (and what they mean)
|
|
784
981
|
|
|
785
982
|
This module produces two kinds of compile-time feedback:
|
|
@@ -789,17 +986,33 @@ This module produces two kinds of compile-time feedback:
|
|
|
789
986
|
|
|
790
987
|
### Unsafe use inside `$`
|
|
791
988
|
|
|
792
|
-
Typical messages
|
|
989
|
+
All messages name the offending parameter by its 1-based index and source name, and end with the receiver-only reminder. Typical messages:
|
|
793
990
|
|
|
794
991
|
```
|
|
795
|
-
|
|
992
|
+
Parameter 1 ('d') cannot be passed as an argument to a function or method.
|
|
993
|
+
Scoped values may only be used as a method receiver (e.g., d.method()).
|
|
796
994
|
```
|
|
797
995
|
|
|
798
|
-
|
|
996
|
+
```
|
|
997
|
+
Parameter 1 ('d') must only be used as a method receiver.
|
|
998
|
+
It cannot be returned, stored, passed as an argument, or captured.
|
|
999
|
+
Scoped values may only be used as a method receiver (e.g., d.method()).
|
|
1000
|
+
```
|
|
799
1001
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
1002
|
+
```
|
|
1003
|
+
Parameter 1 ('d') cannot be captured in a nested lambda, def, or anonymous class.
|
|
1004
|
+
Scoped values may only be used as a method receiver (e.g., d.method()).
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
```
|
|
1008
|
+
Parameter 2 ('cache') cannot be passed as an argument to a function or method.
|
|
1009
|
+
Scoped values may only be used as a method receiver (e.g., cache.method()).
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
```
|
|
1013
|
+
$ requires a lambda literal, e.g. $(x)(a => a.method()).
|
|
1014
|
+
Method references and variables are not supported.
|
|
1015
|
+
```
|
|
803
1016
|
|
|
804
1017
|
### Not a class (`Wire.shared/unique` on a trait / abstract)
|
|
805
1018
|
|
|
@@ -1013,8 +1226,15 @@ def scoped[A](f: (child: Scope.Child[this.type]) => A)(using Unscoped[A]): A
|
|
|
1013
1226
|
def allocate[A](resource: Resource[A]): $[A]
|
|
1014
1227
|
def allocate[A <: AutoCloseable](value: => A): $[A]
|
|
1015
1228
|
|
|
1229
|
+
// N=1 (infix available: `scope $ sa`)
|
|
1016
1230
|
infix transparent inline def $[A, B](sa: $[A])(inline f: A => B): B | $[B]
|
|
1017
1231
|
|
|
1232
|
+
// N=2..5 (unqualified syntax: `$(sa1, sa2)(f)` after `import scope.*`)
|
|
1233
|
+
transparent inline def $[A1, A2, B](sa1: $[A1], sa2: $[A2])(inline f: (A1, A2) => B): B | $[B]
|
|
1234
|
+
transparent inline def $[A1, A2, A3, B](sa1: $[A1], sa2: $[A2], sa3: $[A3])(inline f: (A1, A2, A3) => B): B | $[B]
|
|
1235
|
+
transparent inline def $[A1, A2, A3, A4, B](sa1: $[A1], sa2: $[A2], sa3: $[A3], sa4: $[A4])(inline f: (A1, A2, A3, A4) => B): B | $[B]
|
|
1236
|
+
transparent inline def $[A1, A2, A3, A4, A5, B](sa1: $[A1], sa2: $[A2], sa3: $[A3], sa4: $[A4], sa5: $[A5])(inline f: (A1, A2, A3, A4, A5) => B): B | $[B]
|
|
1237
|
+
|
|
1018
1238
|
def lower[A](value: parent.$[A]): $[A]
|
|
1019
1239
|
|
|
1020
1240
|
override def defer(f: => Unit): DeferHandle
|
|
@@ -1026,9 +1246,11 @@ inline def leak[A](inline sa: $[A]): A
|
|
|
1026
1246
|
|
|
1027
1247
|
Notes:
|
|
1028
1248
|
|
|
1029
|
-
- `$` requires a **lambda literal** and enforces safe receiver-only usage.
|
|
1249
|
+
- `$` (all arities) requires a **lambda literal** and enforces safe receiver-only usage at compile time.
|
|
1030
1250
|
- `$` returns `B` if `Unscoped[B]` exists; otherwise returns `$[B]`.
|
|
1031
|
-
-
|
|
1251
|
+
- N=1 is `infix`; N≥2 are not — use unqualified syntax after `import scope.*`.
|
|
1252
|
+
- For N>5, call `$` once per resource and combine the resulting plain (Unscoped) values.
|
|
1253
|
+
- If the scope is closed, `$`, `allocate`, and `open` throw `IllegalStateException` with a detailed error message. `defer` and `lower` are unaffected.
|
|
1032
1254
|
|
|
1033
1255
|
Syntax enrichments available after `import scope.*` inside a scope:
|
|
1034
1256
|
|
|
@@ -1191,7 +1413,9 @@ object Unscoped:
|
|
|
1191
1413
|
## Practical guidance (summary)
|
|
1192
1414
|
|
|
1193
1415
|
- Allocate in a scope: `resource.allocate` (inside `Scope.global.scoped { scope => import scope.* ... }`)
|
|
1194
|
-
-
|
|
1416
|
+
- Access one scoped value: `$(sa)(v => v.method())` — parameter can only be a receiver
|
|
1417
|
+
- Access two or more scoped values simultaneously: `$(sa1, sa2)((v1, v2) => v1.method(v2.result()))` (N=2..5)
|
|
1418
|
+
- For N>5: call `$` once per resource, combine the plain results
|
|
1195
1419
|
- Return only `Unscoped` data from `scoped` blocks
|
|
1196
1420
|
- Use `lower` to use parent values inside a child
|
|
1197
1421
|
- If `$` returns `$[Resource[A]]`, call `.allocate` on it (scoped resource chaining)
|
package/sidebars.js
CHANGED
|
@@ -7,13 +7,20 @@ const sidebars = {
|
|
|
7
7
|
link: { type: "doc", id: "index" },
|
|
8
8
|
items: [
|
|
9
9
|
"reference/schema",
|
|
10
|
+
"reference/allows",
|
|
10
11
|
"reference/reflect",
|
|
11
12
|
"reference/binding",
|
|
13
|
+
"reference/binding-resolver",
|
|
12
14
|
"reference/registers",
|
|
13
15
|
"reference/typeid",
|
|
16
|
+
"reference/allows",
|
|
14
17
|
"reference/modifier",
|
|
15
18
|
"reference/dynamic-value",
|
|
19
|
+
"reference/dynamic-schema",
|
|
20
|
+
"reference/lazy",
|
|
21
|
+
"reference/structural-types",
|
|
16
22
|
"reference/optics",
|
|
23
|
+
"reference/patch",
|
|
17
24
|
"reference/schema-expr",
|
|
18
25
|
"reference/dynamic-optic",
|
|
19
26
|
"reference/type-class-derivation",
|
|
@@ -21,12 +28,24 @@ const sidebars = {
|
|
|
21
28
|
"reference/formats",
|
|
22
29
|
"path-interpolator",
|
|
23
30
|
"reference/chunk",
|
|
31
|
+
"reference/schema-error",
|
|
24
32
|
"reference/validation",
|
|
25
|
-
|
|
33
|
+
{
|
|
34
|
+
type: "category",
|
|
35
|
+
label: "Schema Evolution",
|
|
36
|
+
link: { type: "doc", id: "reference/schema-evolution/index" },
|
|
37
|
+
items: [
|
|
38
|
+
"reference/schema-evolution/into",
|
|
39
|
+
"reference/schema-evolution/as",
|
|
40
|
+
]
|
|
41
|
+
},
|
|
26
42
|
"reference/context",
|
|
43
|
+
"scope",
|
|
27
44
|
"reference/docs",
|
|
28
45
|
"reference/json",
|
|
46
|
+
"reference/json-patch",
|
|
29
47
|
"reference/json-schema",
|
|
48
|
+
"reference/xml",
|
|
30
49
|
"reference/syntax",
|
|
31
50
|
"reference/media-type",
|
|
32
51
|
]
|
|
@@ -35,6 +54,7 @@ const sidebars = {
|
|
|
35
54
|
type: "category",
|
|
36
55
|
label: "Guides",
|
|
37
56
|
items: [
|
|
57
|
+
"guides/zio-schema-migration",
|
|
38
58
|
"guides/query-dsl-reified-optics",
|
|
39
59
|
"guides/query-dsl-sql",
|
|
40
60
|
"guides/query-dsl-extending",
|