@zio.dev/zio-blocks 0.0.28 → 0.0.30

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.
Files changed (35) hide show
  1. package/guides/compile-time-resource-safety-with-scope.md +997 -0
  2. package/guides/query-dsl-extending.md +1 -1
  3. package/guides/query-dsl-fluent-builder.md +203 -203
  4. package/guides/query-dsl-reified-optics.md +1 -1
  5. package/guides/query-dsl-sql.md +1 -1
  6. package/guides/zio-schema-migration.md +6 -6
  7. package/index.md +68 -16
  8. package/package.json +1 -1
  9. package/path-interpolator.md +70 -9
  10. package/reference/allows.md +96 -0
  11. package/reference/codec.md +8 -8
  12. package/reference/combinators.md +345 -0
  13. package/reference/context.md +639 -67
  14. package/reference/docs.md +1 -1
  15. package/reference/dynamic-schema.md +39 -42
  16. package/reference/http-model.md +1716 -0
  17. package/reference/json-differ.md +320 -0
  18. package/reference/json-patch.md +1 -1
  19. package/reference/media-type.md +2 -2
  20. package/reference/resource-management/defer-handle.md +246 -0
  21. package/reference/resource-management/finalization.md +286 -0
  22. package/reference/resource-management/finalizer.md +167 -0
  23. package/reference/resource-management/index.md +44 -0
  24. package/reference/resource-management/resource.md +1607 -0
  25. package/reference/resource-management/scope.md +3026 -0
  26. package/reference/resource-management/unscoped.md +125 -0
  27. package/reference/resource-management/wire.md +878 -0
  28. package/reference/schema-evolution/as.md +4 -4
  29. package/reference/schema-evolution/into.md +2 -2
  30. package/reference/schema-expr.md +2 -2
  31. package/reference/streams.md +989 -0
  32. package/reference/type-class-derivation.md +31 -31
  33. package/ringbuffer.md +249 -0
  34. package/sidebars.js +19 -2
  35. package/scope.md +0 -1423
@@ -0,0 +1,125 @@
1
+ ---
2
+ id: unscoped
3
+ title: "Unscoped"
4
+ ---
5
+
6
+ `Unscoped[A]` is a marker typeclass for types that can safely escape a scope without tracking. Types with an `Unscoped` instance are considered "safe data"—they don't hold resources and can be freely extracted from a scope. Here's the definition:
7
+
8
+ ```scala
9
+ trait Unscoped[A]
10
+ ```
11
+
12
+ The `Unscoped` typeclass distinguishes between two categories of types:
13
+
14
+ 1. **Unscoped types** (have an instance): Primitives, strings, collections, value types, and pure data. These can leave a scope without risk.
15
+ 2. **Scoped types** (no instance): Resources like streams, connections, handles. These must remain tracked within a scope.
16
+
17
+ When the `$` operator is used to access a scoped value, if the result type has an `Unscoped` instance, it returns the value directly (unwrapped). Otherwise, it returns the value still wrapped in `$`.
18
+
19
+ ## Motivation / Use Case
20
+
21
+ The exact problem: A `scoped` block automatically closes all resources when it exits. If you accidentally returned a resource (like a database connection or file handle) from the block, it would be closed—but you might try to use it later, causing a **use-after-close** crash. Here's an example (this would fail without `Unscoped`):
22
+
23
+ ```scala
24
+ import zio.blocks.scope.{Scope, Resource}
25
+
26
+ final class Database {
27
+ def query(sql: String) = s"result: $sql"
28
+ }
29
+
30
+ // Without Unscoped constraint, this compiles (BAD):
31
+ // val db: Database = Scope.global.scoped { scope =>
32
+ // val db = allocate(Resource(new Database()))
33
+ // db // BUG: returns the resource itself, not data extracted from it
34
+ // }
35
+ // db.query("SELECT 1") // CRASH: use-after-close (scope already closed it)
36
+ ```
37
+
38
+ The solution: `Unscoped` makes this a **compile error** instead of a runtime bug. When a `scoped` block returns a value, that value's type must have an `Unscoped` instance—meaning the type checker verifies you're only extracting *computed results* (like `Int`, `String`, or aggregate data), not resources themselves.
39
+
40
+ You can still extract computed results by using the `$` operator to unwrap scoped values *within* the scope:
41
+
42
+ ```scala
43
+ import zio.blocks.scope.{Scope, Resource}
44
+
45
+ Scope.global.scoped { scope =>
46
+ import scope._
47
+
48
+ val intValue = allocate(Resource(42))
49
+ // Extract the Int value (not the Resource), computed inside the scope
50
+ val n: Int = $(intValue)(x => x + 1)
51
+
52
+ val text = allocate(Resource("hello"))
53
+ // Extract the String value (not the Resource), computed inside the scope
54
+ val s: String = $(text)(x => x.toUpperCase)
55
+
56
+ (n, s) // Tuple of pure data: safe to return
57
+ }
58
+ ```
59
+
60
+ ## Returning Unscoped Data from Scopes
61
+
62
+ Extract computed results that don't hold resources:
63
+
64
+ ```scala
65
+ import zio.blocks.scope.{Scope, Resource, Unscoped}
66
+ import scala.concurrent.duration.{Duration, FiniteDuration}
67
+
68
+ case class ProcessingResult(count: Int, elapsed: FiniteDuration)
69
+
70
+ object ProcessingResult {
71
+ implicit val unscoped: Unscoped[ProcessingResult] = new Unscoped[ProcessingResult] {}
72
+ }
73
+
74
+ def processData(): ProcessingResult = Scope.global.scoped { scope =>
75
+ import scope._
76
+
77
+ val startTime = java.time.Instant.now()
78
+ val input = allocate(Resource(Seq(1, 2, 3, 4, 5)))
79
+ val count = $(input)(_.length)
80
+
81
+ val endTime = java.time.Instant.now()
82
+ val elapsed = java.time.Duration.between(startTime, endTime).toNanos
83
+
84
+ ProcessingResult(count, FiniteDuration(elapsed, java.util.concurrent.TimeUnit.NANOSECONDS))
85
+ }
86
+
87
+ val result = processData()
88
+ println(result)
89
+ ```
90
+
91
+ Only create instances for **pure data types** that don't hold resources. Never create instances for types that contain connections, streams, handles, or any resource-like fields.
92
+
93
+ ## Predefined Instances
94
+
95
+ All built-in instances follow a simple principle: **if a type cannot hold resources, it gets an `Unscoped` instance**. Collections inherit this property from their elements — `List[Int]` is unscoped because `Int` is unscoped.
96
+
97
+ **Primitive and atomic values** (cannot hold resources by nature):
98
+ - `Int`, `Long`, `Short`, `Byte`, `Char`, `Boolean`, `Float`, `Double`, `Unit`
99
+ - `String`, `BigInt`, `BigDecimal`
100
+ - `java.util.UUID`
101
+
102
+ **Collections with conditional instances** (safe when elements/entries are unscoped):
103
+ - Sequences: `Array[A]`, `List[A]`, `Vector[A]`, `Seq[A]`, `IndexedSeq[A]`, `Iterable[A]`
104
+ - Sets: `Set[A]`
105
+ - Maps: `Map[K, V]` (when both `K` and `V` are unscoped)
106
+ - Wrappers: `Option[A]`, `Either[A, B]`, `Tuple2[A, B]` through `Tuple4[A, B, C, D]`
107
+ - ZIO types: `zio.blocks.chunk.Chunk[A]`
108
+
109
+ **Standard library time types** (immutable, cannot hold resources):
110
+ - `java.time.Instant`, `LocalDate`, `LocalTime`, `LocalDateTime`, `ZonedDateTime`, `OffsetDateTime`
111
+ - `java.time.Duration`, `Period`, `ZoneId`, `ZoneOffset`
112
+ - `scala.concurrent.duration.Duration`, `FiniteDuration`
113
+
114
+ All other types (resources, handles, connections) must be manually defined if needed.
115
+
116
+
117
+ ## Thread Safety
118
+
119
+ `Unscoped` instances themselves are immutable and thread-safe. However, the types they mark must be truly immutable for safe concurrent use. For example, `Array[Int]` is mutable—if shared across threads without synchronization, it could cause data races.
120
+
121
+ ## Integration
122
+
123
+ - [`Scope.$`](./scope.md) — the operator that uses `Unscoped`
124
+ - [`Resource`](./resource.md) — types that provide `Unscoped` may be wrapped in resources
125
+ - [`Scope`](./scope.md) — manages the lifecycle of resources