@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.
Files changed (150) hide show
  1. package/guides/compile-time-resource-safety-with-scope.md +16 -17
  2. package/guides/getting-started-with-mux.md +1507 -0
  3. package/guides/query-dsl-extending.md +161 -102
  4. package/guides/query-dsl-fluent-builder.md +217 -157
  5. package/guides/query-dsl-reified-optics.md +12 -10
  6. package/guides/query-dsl-sql.md +246 -165
  7. package/guides/telemetry-guide.md +1069 -0
  8. package/guides/zio-schema-migration.md +29 -22
  9. package/index.md +292 -50
  10. package/package.json +1 -1
  11. package/plans/config-follow-up-prs.md +188 -0
  12. package/plans/config-pr-assessment-roadmap.md +310 -0
  13. package/reference/MuxDataFlow.jsx +250 -0
  14. package/reference/async.md +651 -0
  15. package/reference/chunk.md +3533 -308
  16. package/reference/codegen/case-class.md +436 -0
  17. package/reference/codegen/emitter-config.md +383 -0
  18. package/reference/codegen/examples.md +664 -0
  19. package/reference/codegen/field.md +316 -0
  20. package/reference/codegen/index.md +317 -0
  21. package/reference/codegen/scala-emitter.md +392 -0
  22. package/reference/codegen/scala-file.md +276 -0
  23. package/reference/codegen/sealed-trait.md +408 -0
  24. package/reference/codegen/type-definition.md +340 -0
  25. package/reference/codegen/type-ref.md +201 -0
  26. package/reference/combinators.md +347 -117
  27. package/reference/config.md +158 -0
  28. package/reference/context.md +4 -4
  29. package/reference/datastar.md +346 -0
  30. package/reference/docs.md +1461 -345
  31. package/reference/endpoint/auth-type.md +146 -0
  32. package/reference/endpoint/endpoint.md +297 -0
  33. package/reference/endpoint/http-codec.md +249 -0
  34. package/reference/endpoint/index.md +825 -0
  35. package/reference/endpoint/path-codec.md +237 -0
  36. package/reference/endpoint/route-pattern.md +196 -0
  37. package/reference/endpoint/route-tree.md +111 -0
  38. package/reference/endpoint/segment-codec.md +212 -0
  39. package/reference/html.md +1120 -0
  40. package/reference/htmx/attribute-values.md +359 -0
  41. package/reference/htmx/hx-encoding.md +111 -0
  42. package/reference/htmx/hx-params.md +204 -0
  43. package/reference/htmx/hx-swap.md +276 -0
  44. package/reference/htmx/hx-sync.md +251 -0
  45. package/reference/htmx/hx-target.md +314 -0
  46. package/reference/htmx/hx-trigger.md +457 -0
  47. package/reference/htmx/hx-url-update.md +239 -0
  48. package/reference/htmx/index.md +855 -0
  49. package/reference/http-model/index.md +47 -0
  50. package/reference/http-model/model.md +1481 -0
  51. package/reference/http-model/schema.md +747 -0
  52. package/reference/maybe.md +826 -0
  53. package/reference/media-type.md +2 -2
  54. package/reference/mux.mdx +823 -0
  55. package/reference/openapi.md +1351 -0
  56. package/reference/resource-management/defer-handle.md +1 -1
  57. package/reference/resource-management/resource.md +31 -2
  58. package/reference/resource-management/scope.md +28 -12
  59. package/reference/resource-management/wire.md +3 -7
  60. package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
  61. package/reference/ringbuffer/MpscDiagram.jsx +618 -0
  62. package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
  63. package/reference/ringbuffer/SpscDiagram.jsx +677 -0
  64. package/reference/ringbuffer/advanced.mdx +109 -0
  65. package/reference/ringbuffer/index.mdx +145 -0
  66. package/reference/ringbuffer/mpmc.mdx +151 -0
  67. package/reference/ringbuffer/mpsc.mdx +132 -0
  68. package/reference/ringbuffer/spmc.mdx +108 -0
  69. package/reference/ringbuffer/spsc.mdx +344 -0
  70. package/reference/{allows.md → schema/allows.md} +4 -4
  71. package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
  72. package/reference/{binding.md → schema/binding.md} +2 -3
  73. package/reference/schema/built-in-codecs/avro.md +451 -0
  74. package/reference/schema/built-in-codecs/bson.md +480 -0
  75. package/reference/schema/built-in-codecs/csv.md +564 -0
  76. package/reference/schema/built-in-codecs/index.md +77 -0
  77. package/reference/schema/built-in-codecs/json/index.md +295 -0
  78. package/reference/schema/built-in-codecs/json/json-config.md +217 -0
  79. package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
  80. package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
  81. package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
  82. package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
  83. package/reference/schema/built-in-codecs/messagepack.md +508 -0
  84. package/reference/schema/built-in-codecs/thrift.md +433 -0
  85. package/reference/schema/built-in-codecs/toon.md +1078 -0
  86. package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
  87. package/reference/schema/built-in-codecs/yaml.md +552 -0
  88. package/reference/{codec.md → schema/codec.md} +10 -10
  89. package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
  90. package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
  91. package/reference/schema/format.md +92 -0
  92. package/reference/schema/index.md +50 -0
  93. package/reference/schema/migration.md +297 -0
  94. package/reference/{modifier.md → schema/modifier.md} +58 -7
  95. package/reference/{optics.md → schema/optics.md} +2 -2
  96. package/reference/{patch.md → schema/patch.md} +1 -1
  97. package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
  98. package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
  99. package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
  100. package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
  101. package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
  102. package/reference/{schema.md → schema/schema.md} +12 -0
  103. package/reference/{structural-types.md → schema/structural-types.md} +1 -1
  104. package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
  105. package/reference/smithy.md +533 -0
  106. package/reference/sql/db-codec-deriver.md +71 -0
  107. package/reference/sql/db-codec.md +687 -0
  108. package/reference/sql/db-con.md +271 -0
  109. package/reference/sql/db-connection.md +153 -0
  110. package/reference/sql/db-param-writer.md +77 -0
  111. package/reference/sql/db-param.md +66 -0
  112. package/reference/sql/db-result-reader.md +146 -0
  113. package/reference/sql/db-tx.md +82 -0
  114. package/reference/sql/db-value.md +41 -0
  115. package/reference/sql/ddl.md +85 -0
  116. package/reference/sql/frag.md +254 -0
  117. package/reference/sql/index.md +341 -0
  118. package/reference/sql/repo.md +600 -0
  119. package/reference/sql/sql-dialect.md +73 -0
  120. package/reference/sql/sql-logger.md +62 -0
  121. package/reference/sql/sql-name-mapper.md +70 -0
  122. package/reference/sql/table-metadata.md +134 -0
  123. package/reference/sql/table.md +448 -0
  124. package/reference/sql/transactor-zio.md +399 -0
  125. package/reference/sql/transactor.md +353 -0
  126. package/reference/sql-zio.md +112 -0
  127. package/reference/streams/concurrent-operators.md +106 -0
  128. package/reference/streams/index.md +653 -0
  129. package/reference/streams/pipeline.md +718 -0
  130. package/reference/streams/reader.md +1284 -0
  131. package/reference/streams/scala-2-compatibility.md +55 -0
  132. package/reference/streams/sink.md +1426 -0
  133. package/reference/streams/stream.md +2526 -0
  134. package/reference/streams/writer.md +1045 -0
  135. package/reference/streams/zero-boxing.md +275 -0
  136. package/reference/telemetry.md +693 -0
  137. package/reference/typeid.md +5 -19
  138. package/sidebars.js +238 -43
  139. package/reference/formats.md +0 -694
  140. package/reference/http-model.md +0 -1716
  141. package/reference/streams.md +0 -989
  142. package/ringbuffer.md +0 -249
  143. /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
  144. /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
  145. /package/reference/{lazy.md → schema/lazy.md} +0 -0
  146. /package/reference/{reflect.md → schema/reflect.md} +0 -0
  147. /package/reference/{registers.md → schema/registers.md} +0 -0
  148. /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
  149. /package/reference/{syntax.md → schema/syntax.md} +0 -0
  150. /package/reference/{validation.md → schema/validation.md} +0 -0
@@ -0,0 +1,826 @@
1
+ ---
2
+ id: maybe
3
+ title: "Maybe"
4
+ ---
5
+
6
+ `Maybe[A]` is a **low-allocation alternative to `Option[A]`** that uses `null` to represent the absence of a value. It is an opaque type alias for `A | Null`, allowing you to write nullable-like code with the safety and ergonomics of an Option-style API. Core types: `Maybe[A]`.
7
+
8
+ Here's the type definition and basic construction:
9
+
10
+ ```scala
11
+ opaque type Maybe[+A] = A | Null
12
+
13
+ val present: Maybe[Int] = Maybe.present(42)
14
+ val absent: Maybe[Int] = Maybe.absent
15
+ ```
16
+
17
+ ## Motivation
18
+
19
+ When working with optional values, you face a choice: `Option[A]` provides type safety and functional composition but allocates a wrapper object for every value. `Maybe[A]` provides an alternative with different trade-offs depending on your Scala version.
20
+
21
+ **On Scala 3:** `Maybe[A]` eliminates allocation overhead by leveraging union types and null semantics—every value is either the unwrapped value itself or `null`. This is ideal for performance-critical code where allocations impact throughput or latency. The type is an opaque alias for `A | Null`, giving you a dedicated API (`map`, `flatMap`, `filter`, etc.) with zero runtime wrapper overhead—just null checks.
22
+
23
+ **On Scala 2.13:** `Maybe[A]` is implemented as a sealed trait (`Present[A]` | `Absent`). Present values allocate a wrapper, so the allocation savings versus `Option` are less pronounced. However, the unified API and interoperability benefits still apply.
24
+
25
+ ### Why Maybe over Option?
26
+
27
+ - **Zero allocation**: Every `Maybe` is either the value itself or `null`—no wrapper objects
28
+ - **Familiar API**: All your favorite `Option` combinators (`map`, `flatMap`, `fold`, etc.)
29
+ - **Type safety**: The opaque type prevents accidentally mixing nullable and non-nullable values
30
+ - **Interoperable**: Seamless conversion to/from `Option` with `toOption` and `Maybe#fromOption`
31
+
32
+ ## Installation
33
+
34
+ Add the `zio-blocks-maybe` module to your build:
35
+
36
+ ```scala
37
+ libraryDependencies += "dev.zio" %% "zio-blocks-maybe" % "0.0.51"
38
+ ```
39
+
40
+ For Scala.js:
41
+
42
+ ```scala
43
+ libraryDependencies += "dev.zio" %%% "zio-blocks-maybe" % "0.0.51"
44
+ ```
45
+
46
+ Supported Scala versions: 2.13.x and 3.x
47
+
48
+ ## Overview
49
+
50
+ `Maybe[A]` provides a complete set of operations for working with optional values:
51
+
52
+ - **Constructors**: `Maybe.apply`, `Maybe.present`, `Maybe.absent`, `Maybe.fromOption`
53
+ - **Predicates**: `Maybe#isAbsent`, `isPresent`, `isEmpty`, `isDefined`, `nonEmpty`
54
+ - **Access**: `get`, `Maybe#getOrElse`, `orElse`, `orNull`
55
+ - **Transformations**: `map`, `flatMap`, `flatten`
56
+ - **Filtering**: `filter`, `filterNot`, `collect`
57
+ - **Logical**: `exists`, `forall`, `contains`
58
+ - **Conversions**: `toOption`, `toList`, `toSeq`, `iterator`, `toRight`, `toLeft`
59
+ - **Composition**: `Maybe#zip`, `unzip`, `unzip3`, `fold`, `foreach`
60
+
61
+ ## How It Works
62
+
63
+ The workflow is straightforward: create a `Maybe` from a value or `None`, transform and filter it using functional operations, extract the result with safe accessors, and fall back to defaults when needed:
64
+
65
+ ```
66
+ Value ─→ Maybe.apply (wrap) ─→ map / flatMap (transform)
67
+ ↓ ↓
68
+ [null] ←─────── isAbsent (test) ← filter / collect (refine)
69
+ ↓ ↓
70
+ absent ─→ orElse (fallback) ─→ get / getOrElse (extract)
71
+ ```
72
+
73
+ ### Typical Data Flow
74
+
75
+ 1. **Create** a `Maybe` using `Maybe.apply`, `Maybe.present`, `Maybe.fromOption`, or explicitly with `Maybe.absent` for no value
76
+ 2. **Transform** values using `map` or `flatMap` — operations on absent values short-circuit and remain absent
77
+ 3. **Filter** with `filter` or `collect` to refine the value or produce absence based on a predicate
78
+ 4. **Compose** with other `Maybe` values using `Maybe#zip` or flatMap chains to build complex workflows
79
+ 5. **Extract** the result using `get` (throws on absence), `Maybe#getOrElse` (provides a default), `toOption` (convert to `Option`), or `fold` (handle both branches)
80
+
81
+ ## Common Patterns
82
+
83
+ Here are key patterns for working effectively with `Maybe`:
84
+
85
+ ### Present and Absent States
86
+
87
+ Every `Maybe` is either present (holds a non-null value) or absent (is `null`). Test the state with predicates:
88
+
89
+ ```scala
90
+ import zio.blocks.maybe._
91
+
92
+ val value: Maybe[Int] = Maybe.present(42)
93
+
94
+ if (value.isPresent) {
95
+ println(value.get) // Safe: we know it's present
96
+ } else {
97
+ println("No value")
98
+ }
99
+ ```
100
+
101
+ ### Safe Extraction with Defaults
102
+
103
+ Use `Maybe#getOrElse` to provide a fallback when the value is absent, avoiding the exception risk of `get`:
104
+
105
+ ```scala
106
+ import zio.blocks.maybe._
107
+
108
+ val userId: Maybe[String] = Maybe.absent
109
+ val name = userId.getOrElse("anonymous")
110
+ ```
111
+
112
+ ### Chaining Transformations
113
+
114
+ Combine `map` and `flatMap` to thread operations through optional values. Absence propagates automatically:
115
+
116
+ ```scala
117
+ import zio.blocks.maybe._
118
+
119
+ val userId: Maybe[Int] = Maybe.present(123)
120
+
121
+ val greeting = userId
122
+ .map(id => s"User $id")
123
+ .map(msg => s"Hello, $msg!")
124
+
125
+ println(greeting) // Maybe[String] containing "Hello, User 123!"
126
+ ```
127
+
128
+ ### Filtering with Predicates
129
+
130
+ Use `filter` to keep a value only if it satisfies a condition, or `collect` with a partial function for both filtering and transformation:
131
+
132
+ ```scala
133
+ import zio.blocks.maybe._
134
+
135
+ val age: Maybe[Int] = Maybe.present(25)
136
+
137
+ val isAdult = age.filter(_ >= 18) // Maybe[Int] containing 25
138
+ val isChild = age.filter(_ < 18) // Maybe[Int] absent
139
+ ```
140
+
141
+ ### Combining Multiple Maybes
142
+
143
+ Use `Maybe#zip` to combine two `Maybe` values into a tuple, or chain multiple operations with `flatMap`:
144
+
145
+ ```scala
146
+ import zio.blocks.maybe._
147
+
148
+ val firstName: Maybe[String] = Maybe.present("Alice")
149
+ val lastName: Maybe[String] = Maybe.present("Smith")
150
+
151
+ val fullName = firstName.zip(lastName).map { case (f, l) => s"$f $l" }
152
+ ```
153
+
154
+ ### Converting to and from Option
155
+
156
+ Interoperate seamlessly with `Option` using `toOption` and `Maybe#fromOption`:
157
+
158
+ ```scala
159
+ import zio.blocks.maybe._
160
+
161
+ val opt: Option[String] = Some("value")
162
+ val m: Maybe[String] = Maybe.fromOption(opt)
163
+ val backToOpt = m.toOption
164
+ ```
165
+
166
+ ### Handling Errors with Either
167
+
168
+ Convert a `Maybe` to an `Either` to propagate errors in fail-fast computations:
169
+
170
+ ```scala
171
+ import zio.blocks.maybe._
172
+
173
+ val value: Maybe[Int] = Maybe.present(10)
174
+
175
+ val result: Either[String, Int] = value.toRight("Value not found")
176
+ ```
177
+
178
+ ## Integration Points
179
+
180
+ `Maybe[A]` integrates with the broader Scala and ZIO ecosystem:
181
+
182
+ - **Option interoperability**: Convert bidirectionally with `toOption` and `Maybe#fromOption`; a `Conversion[Option[A], Maybe[A]]` is provided for implicit conversions
183
+ - **Schema support**: Schema codecs use private unsafe methods (`unsafeIsAbsent`, `unsafeGet`, `unsafeWrap`) for efficient serialization and deserialization
184
+ - **For-comprehensions**: Supports `withFilter` for guard clauses in for-expressions
185
+ - **Collections**: Convert to `List`, `Seq`, or `Iterator` for bulk operations
186
+ - **Pattern matching**: Works naturally in match expressions, though testing with predicates is more common
187
+
188
+ ### Example: For-Comprehension with Guards
189
+
190
+ Combine multiple `Maybe` values with filter guards:
191
+
192
+ ```scala
193
+ import zio.blocks.maybe._
194
+
195
+ val maybeX: Maybe[Int] = Maybe.present(5)
196
+ val maybeY: Maybe[Int] = Maybe.present(10)
197
+
198
+ val result = for {
199
+ x <- maybeX
200
+ y <- maybeY
201
+ if x + y > 10
202
+ } yield x + y
203
+
204
+ println(result) // Maybe[Int] containing 15
205
+ ```
206
+
207
+ ## Operations Reference
208
+
209
+ All `Maybe` operations are organized by category. Each subsection documents a group of related methods with examples.
210
+
211
+ ### Constructors
212
+
213
+ Create `Maybe` values using these factory methods:
214
+
215
+ #### Maybe.apply
216
+
217
+ Wraps a value in `Maybe`, treating `null` as `Maybe.absent`:
218
+
219
+ ```scala
220
+ import zio.blocks.maybe._
221
+
222
+ val present = Maybe(42) // Maybe[Int] containing 42
223
+ val absent: Maybe[String] = Maybe(null) // Maybe[String] absent
224
+ ```
225
+
226
+ #### Maybe.present
227
+
228
+ Explicitly wraps a non-null value:
229
+
230
+ ```scala
231
+ import zio.blocks.maybe._
232
+
233
+ val value: Maybe[Int] = Maybe.present(100)
234
+ ```
235
+
236
+ #### Maybe.absent
237
+
238
+ Creates an absent value for any type:
239
+
240
+ ```scala
241
+ import zio.blocks.maybe._
242
+
243
+ val empty: Maybe[String] = Maybe.absent
244
+ ```
245
+
246
+ #### Maybe.empty
247
+
248
+ Alias for `Maybe.absent`:
249
+
250
+ ```scala
251
+ import zio.blocks.maybe._
252
+
253
+ val empty: Maybe[Double] = Maybe.empty
254
+ ```
255
+
256
+ #### Maybe.fromOption
257
+
258
+ Converts an `Option` to `Maybe`:
259
+
260
+ ```scala
261
+ import zio.blocks.maybe._
262
+
263
+ val fromSome = Maybe.fromOption(Some(5)) // Maybe[Int] containing 5
264
+ val fromNone = Maybe.fromOption(None) // Maybe[Nothing] absent
265
+ ```
266
+
267
+ ### State Testing
268
+
269
+ Test whether a `Maybe` is present or absent:
270
+
271
+ #### isPresent, isDefined, nonEmpty
272
+
273
+ All three are equivalent—return true if the value is non-null:
274
+
275
+ ```scala
276
+ import zio.blocks.maybe._
277
+
278
+ val value: Maybe[Int] = Maybe.present(42)
279
+ println(value.isPresent) // true
280
+ println(value.isDefined) // true
281
+ println(value.nonEmpty) // true
282
+ ```
283
+
284
+ #### isAbsent, isEmpty
285
+
286
+ Both return true if the value is `null`:
287
+
288
+ ```scala
289
+ import zio.blocks.maybe._
290
+
291
+ val empty: Maybe[Int] = Maybe.absent
292
+ println(empty.isAbsent) // true
293
+ println(empty.isEmpty) // true
294
+ ```
295
+
296
+ ### Access
297
+
298
+ Retrieve the value or provide a fallback:
299
+
300
+ #### get
301
+
302
+ Unwraps the value, throwing `NoSuchElementException` if absent:
303
+
304
+ ```scala
305
+ import zio.blocks.maybe._
306
+
307
+ val value: Maybe[Int] = Maybe.present(10)
308
+ println(value.get) // 10
309
+
310
+ try {
311
+ Maybe.absent[Int].get
312
+ } catch {
313
+ case e: NoSuchElementException => println(e.getMessage) // "Maybe.absent.get"
314
+ }
315
+ ```
316
+
317
+ #### getOrElse
318
+
319
+ Returns the value if present, or evaluates the default:
320
+
321
+ ```scala
322
+ import zio.blocks.maybe._
323
+
324
+ val value: Maybe[Int] = Maybe.absent
325
+ val withDefault: Int = value.getOrElse(99)
326
+ println(withDefault) // 99
327
+ ```
328
+
329
+ #### orElse
330
+
331
+ Returns the current `Maybe` if present, or another `Maybe`:
332
+
333
+ ```scala
334
+ import zio.blocks.maybe._
335
+
336
+ val first: Maybe[Int] = Maybe.absent
337
+ val second: Maybe[Int] = Maybe.present(42)
338
+ val result = first.orElse(second)
339
+ println(result.get) // 42
340
+ ```
341
+
342
+ #### orNull
343
+
344
+ Converts to nullable, returning the value or `null`:
345
+
346
+ ```scala
347
+ import zio.blocks.maybe._
348
+
349
+ val value: Maybe[String] = Maybe.absent
350
+ val nullable: String = value.orNull
351
+ println(nullable) // null
352
+ ```
353
+
354
+ ### Transformations
355
+
356
+ Transform values or short-circuit on absence:
357
+
358
+ #### map
359
+
360
+ Applies a function if present, remains absent otherwise:
361
+
362
+ ```scala
363
+ import zio.blocks.maybe._
364
+
365
+ val value: Maybe[Int] = Maybe.present(5)
366
+ val doubled: Maybe[Int] = value.map(_ * 2)
367
+ val absent: Maybe[String] = Maybe.absent.map(_ => "never runs")
368
+ println(doubled.get) // 10
369
+ println(absent.isAbsent) // true
370
+ ```
371
+
372
+ #### flatMap
373
+
374
+ Chains operations that return `Maybe`, flattening the result:
375
+
376
+ ```scala
377
+ import zio.blocks.maybe._
378
+
379
+ def safeDivide(a: Int, b: Int): Maybe[Int] =
380
+ if (b == 0) Maybe.absent else Maybe(a / b)
381
+
382
+ val result = Maybe.present(10).flatMap(safeDivide(_, 2))
383
+ println(result.get) // 5
384
+
385
+ val divideByZero = Maybe.present(10).flatMap(safeDivide(_, 0))
386
+ println(divideByZero.isAbsent) // true
387
+ ```
388
+
389
+ #### flatten
390
+
391
+ Unwraps a nested `Maybe`:
392
+
393
+ ```scala
394
+ import zio.blocks.maybe._
395
+
396
+ val nested: Maybe[Maybe[Int]] = Maybe.present(Maybe.present(42))
397
+ val flat: Maybe[Int] = nested.flatten
398
+ println(flat.get) // 42
399
+ ```
400
+
401
+ ### Filtering
402
+
403
+ Keep or discard values based on predicates:
404
+
405
+ #### filter
406
+
407
+ Keeps the value only if the predicate is true:
408
+
409
+ ```scala
410
+ import zio.blocks.maybe._
411
+
412
+ val value: Maybe[Int] = Maybe.present(10)
413
+ val even = value.filter(_ % 2 == 0) // Present: 10
414
+ val odd = value.filter(_ % 2 != 0) // Absent
415
+ println(even.get) // 10
416
+ println(odd.isAbsent) // true
417
+ ```
418
+
419
+ #### filterNot
420
+
421
+ Inverse of `filter`—keeps the value if the predicate is false:
422
+
423
+ ```scala
424
+ import zio.blocks.maybe._
425
+
426
+ val value: Maybe[Int] = Maybe.present(5)
427
+ val notOdd: Maybe[Int] = value.filterNot(_ % 2 != 0)
428
+ println(notOdd.isAbsent) // true
429
+ ```
430
+
431
+ #### collect
432
+
433
+ Combines filtering with transformation using a partial function:
434
+
435
+ ```scala
436
+ import zio.blocks.maybe._
437
+
438
+ val value: Maybe[Int] = Maybe.present(8)
439
+ val result = value.collect { case n if n % 2 == 0 => s"even: $n" }
440
+ println(result.get) // "even: 8"
441
+
442
+ val odd: Maybe[Int] = Maybe.present(7)
443
+ val noMatch: Maybe[String] = odd.collect { case n if n % 2 == 0 => s"even: $n" }
444
+ println(noMatch.isAbsent) // true
445
+ ```
446
+
447
+ ### Logical Predicates
448
+
449
+ Test conditions without extracting the value:
450
+
451
+ #### contains
452
+
453
+ Returns true if the value equals the given element:
454
+
455
+ ```scala
456
+ import zio.blocks.maybe._
457
+
458
+ val value: Maybe[Int] = Maybe.present(42)
459
+ println(value.contains(42)) // true
460
+ println(value.contains(41)) // false
461
+ println(Maybe.absent[Int].contains(42)) // false
462
+ ```
463
+
464
+ #### exists
465
+
466
+ Returns true if the value satisfies the predicate:
467
+
468
+ ```scala
469
+ import zio.blocks.maybe._
470
+
471
+ val value: Maybe[Int] = Maybe.present(10)
472
+ println(value.exists(_ > 5)) // true
473
+ println(value.exists(_ > 20)) // false
474
+ println(Maybe.absent[Int].exists(_ => true)) // false
475
+ ```
476
+
477
+ #### forall
478
+
479
+ Returns true if the value satisfies the predicate, or if absent (vacuous truth):
480
+
481
+ ```scala
482
+ import zio.blocks.maybe._
483
+
484
+ val value: Maybe[Int] = Maybe.present(10)
485
+ println(value.forall(_ > 5)) // true
486
+ println(value.forall(_ > 20)) // false
487
+ println(Maybe.absent[Int].forall(_ => false)) // true (absent = vacuously true)
488
+ ```
489
+
490
+ ### Iteration and Conversion
491
+
492
+ Convert `Maybe` to other types or iterate its contents:
493
+
494
+ #### foreach
495
+
496
+ Executes a side effect if present:
497
+
498
+ ```scala
499
+ import zio.blocks.maybe._
500
+
501
+ val value: Maybe[Int] = Maybe.present(42)
502
+ value.foreach(x => println(s"Value: $x"))
503
+
504
+ Maybe.absent[Int].foreach(_ => println("not called"))
505
+ ```
506
+
507
+ #### toOption
508
+
509
+ Converts to `Option[A]`:
510
+
511
+ ```scala
512
+ import zio.blocks.maybe._
513
+
514
+ val value: Maybe[Int] = Maybe.present(7)
515
+ val some: Option[Int] = value.toOption
516
+ val noneVal: Option[Int] = Maybe.absent[Int].toOption
517
+ println(some) // Some(7)
518
+ println(noneVal) // None
519
+ ```
520
+
521
+ #### toList
522
+
523
+ Converts to `List[A]`:
524
+
525
+ ```scala
526
+ import zio.blocks.maybe._
527
+
528
+ val value: Maybe[Int] = Maybe.present(5)
529
+ val list: List[Int] = value.toList
530
+ val emptyList: List[Int] = Maybe.absent[Int].toList
531
+ println(list) // List(5)
532
+ println(emptyList) // List()
533
+ ```
534
+
535
+ #### toSeq
536
+
537
+ Converts to `Seq[A]`:
538
+
539
+ ```scala
540
+ import zio.blocks.maybe._
541
+
542
+ val value: Maybe[String] = Maybe.present("hello")
543
+ val seq: Seq[String] = value.toSeq
544
+ println(seq) // Seq("hello")
545
+ ```
546
+
547
+ #### iterator
548
+
549
+ Creates an iterator over the value:
550
+
551
+ ```scala
552
+ import zio.blocks.maybe._
553
+
554
+ val value: Maybe[Int] = Maybe.present(42)
555
+ val it = value.iterator
556
+ println(it.toList) // List(42)
557
+
558
+ Maybe.absent[Int].iterator.toList // List()
559
+ ```
560
+
561
+ ### Either Conversion
562
+
563
+ Convert `Maybe` to `Either` for error handling:
564
+
565
+ #### toRight
566
+
567
+ Converts to `Either[X, A]` with a left error value:
568
+
569
+ ```scala
570
+ import zio.blocks.maybe._
571
+
572
+ val value: Maybe[Int] = Maybe.present(10)
573
+ val right: Either[String, Int] = value.toRight("not found")
574
+ println(right) // Right(10)
575
+
576
+ val absent: Maybe[Int] = Maybe.absent
577
+ val left: Either[String, Int] = absent.toRight("not found")
578
+ println(left) // Left("not found")
579
+ ```
580
+
581
+ #### toLeft
582
+
583
+ Converts to `Either[A, X]` with a right success value:
584
+
585
+ ```scala
586
+ import zio.blocks.maybe._
587
+
588
+ val value: Maybe[String] = Maybe.present("error")
589
+ val left: Either[String, Unit] = value.toLeft(())
590
+ println(left) // Left("error")
591
+
592
+ val absent: Maybe[String] = Maybe.absent
593
+ val right: Either[String, Unit] = absent.toLeft(())
594
+ println(right) // Right(())
595
+ ```
596
+
597
+ ### Composition
598
+
599
+ Combine multiple `Maybe` values:
600
+
601
+ #### zip
602
+
603
+ Combines two `Maybe` values into a tuple:
604
+
605
+ ```scala
606
+ import zio.blocks.maybe._
607
+
608
+ val x: Maybe[Int] = Maybe.present(1)
609
+ val y: Maybe[String] = Maybe.present("a")
610
+ val tuple = x.zip(y)
611
+ println(tuple.get) // (1, "a")
612
+
613
+ val absent: Maybe[String] = Maybe.absent
614
+ val zipped = x.zip(absent)
615
+ println(zipped.isAbsent) // true
616
+ ```
617
+
618
+ #### unzip
619
+
620
+ Splits a `Maybe[(A, B)]` into a tuple of `Maybe[A]` and `Maybe[B]`:
621
+
622
+ ```scala
623
+ import zio.blocks.maybe._
624
+
625
+ val tuple: Maybe[(Int, String)] = Maybe.present((42, "answer"))
626
+ val (x, y) = tuple.unzip
627
+ println(x.get) // 42
628
+ println(y.get) // "answer"
629
+
630
+ val absent: Maybe[(Int, String)] = Maybe.absent
631
+ val (ax, ay) = absent.unzip
632
+ println(ax.isAbsent && ay.isAbsent) // true
633
+ ```
634
+
635
+ #### unzip3
636
+
637
+ Splits a `Maybe[(A, B, C)]` into three `Maybe` values:
638
+
639
+ ```scala
640
+ import zio.blocks.maybe._
641
+
642
+ val triple: Maybe[(Int, String, Double)] = Maybe.present((1, "one", 1.0))
643
+ val (a, b, c) = triple.unzip3
644
+ println(a.get) // 1
645
+ println(b.get) // "one"
646
+ println(c.get) // 1.0
647
+ ```
648
+
649
+ ### Folding
650
+
651
+ Reduce a `Maybe` to a value by handling both branches:
652
+
653
+ #### fold
654
+
655
+ Applies one of two functions based on presence:
656
+
657
+ ```scala
658
+ import zio.blocks.maybe._
659
+
660
+ val value: Maybe[Int] = Maybe.present(10)
661
+ val result = value.fold(-1)((x: Int) => x * 2)
662
+ println(result) // 20
663
+
664
+ val absent: Maybe[Int] = Maybe.absent
665
+ val fallback = absent.fold(-1)((x: Int) => x * 2)
666
+ println(fallback) // -1
667
+ ```
668
+
669
+ ### For-Comprehensions
670
+
671
+ Use `withFilter` to support guard clauses:
672
+
673
+ #### withFilter
674
+
675
+ Enables guarded for-expressions:
676
+
677
+ ```scala
678
+ import zio.blocks.maybe._
679
+
680
+ val x: Maybe[Int] = Maybe.present(5)
681
+ val y: Maybe[Int] = Maybe.present(15)
682
+
683
+ val result = for {
684
+ a <- x
685
+ b <- y
686
+ if a + b > 15
687
+ } yield a + b
688
+
689
+ println(result.get) // 20
690
+ ```
691
+
692
+ ## Running Examples
693
+
694
+ End-to-end workflows combining multiple `Maybe` operations:
695
+
696
+ ### Example: Parsing and Transforming User Input
697
+
698
+ Build a pipeline that parses user input, validates it, and transforms the result:
699
+
700
+ ```scala
701
+ import zio.blocks.maybe._
702
+
703
+ case class User(id: Int, name: String, age: Int)
704
+
705
+ def parseId(input: String): Maybe[Int] =
706
+ Maybe.fromOption(input.toIntOption)
707
+
708
+ def validateAge(age: Int): Maybe[Int] =
709
+ Maybe.absent[Int].orElse(
710
+ if (age >= 0 && age <= 150) Maybe.present(age) else Maybe.absent
711
+ )
712
+
713
+ val input = "42"
714
+ val processedAge = parseId(input)
715
+ .flatMap(id => Maybe.present(User(id, "Alice", 30)))
716
+ .map(_.age)
717
+ .flatMap(validateAge)
718
+
719
+ println(processedAge.getOrElse(-1)) // 30
720
+ ```
721
+
722
+ ### Example: Chaining Optional Database Results
723
+
724
+ Work with nullable database results, filtering and transforming as needed:
725
+
726
+ ```scala
727
+ import zio.blocks.maybe._
728
+
729
+ case class Product(id: Int, name: String, price: Double, inStock: Boolean)
730
+
731
+ def findProduct(id: Int): Maybe[Product] =
732
+ if (id > 0) Maybe.present(Product(id, s"Product $id", 99.99, true))
733
+ else Maybe.absent
734
+
735
+ def applyDiscount(product: Product, percent: Int): Maybe[Product] =
736
+ if (percent >= 0 && percent <= 100)
737
+ Maybe.present(product.copy(price = product.price * (1 - percent / 100.0)))
738
+ else Maybe.absent
739
+
740
+ val discountedProduct = findProduct(123)
741
+ .filter(_.inStock)
742
+ .flatMap(applyDiscount(_, 20))
743
+ .map(p => s"${p.name} now costs $$${p.price}")
744
+
745
+ println(discountedProduct.getOrElse("Product not available"))
746
+ ```
747
+
748
+ ### Example: Combining Multiple Maybes with Fallbacks
749
+
750
+ Handle scenarios where multiple optional values need to be combined:
751
+
752
+ ```scala
753
+ import zio.blocks.maybe._
754
+
755
+ case class Config(host: Maybe[String], port: Maybe[Int], timeout: Maybe[Long])
756
+
757
+ def buildConnection(config: Config): String = {
758
+ val host = config.host.getOrElse("localhost")
759
+ val port = config.port.getOrElse(8080)
760
+ val timeout = config.timeout.getOrElse(5000L)
761
+ s"Connection to $host:$port with timeout $timeout ms"
762
+ }
763
+
764
+ val config = Config(
765
+ host = Maybe.present("api.example.com"),
766
+ port = Maybe.absent,
767
+ timeout = Maybe.present(10000L)
768
+ )
769
+
770
+ println(buildConnection(config))
771
+ ```
772
+
773
+ ### Example: Error Handling with Either Conversion
774
+
775
+ Convert `Maybe` to `Either` for composable error handling in result types:
776
+
777
+ ```scala
778
+ import zio.blocks.maybe._
779
+
780
+ case class Request(id: Maybe[String], method: Maybe[String])
781
+
782
+ def validateRequest(req: Request): Either[String, (String, String)] = {
783
+ for {
784
+ id <- req.id.toRight("Missing request ID")
785
+ method <- req.method.toRight("Missing HTTP method")
786
+ } yield (id, method)
787
+ }
788
+
789
+ val validReq = Request(
790
+ id = Maybe.present("req-123"),
791
+ method = Maybe.present("GET")
792
+ )
793
+
794
+ val invalidReq = Request(
795
+ id = Maybe.absent,
796
+ method = Maybe.present("POST")
797
+ )
798
+
799
+ println(validateRequest(validReq)) // Right((req-123, GET))
800
+ println(validateRequest(invalidReq)) // Left(Missing request ID)
801
+ ```
802
+
803
+ ### Example: Building a Computation Pipeline
804
+
805
+ Chain multiple transformations with fallback at each step:
806
+
807
+ ```scala
808
+ import zio.blocks.maybe._
809
+
810
+ def getUser(id: Int): Maybe[String] =
811
+ if (id > 0) Maybe.present(s"user_$id") else Maybe.absent
812
+
813
+ def getUserEmail(username: String): Maybe[String] =
814
+ if (username.nonEmpty) Maybe.present(s"$username@example.com") else Maybe.absent
815
+
816
+ def getUserProfile(id: Int): Maybe[String] = {
817
+ val username = getUser(id)
818
+ username
819
+ .flatMap(getUserEmail)
820
+ .map(email => s"Profile for $email")
821
+ .orElse(Maybe.present("Guest user"))
822
+ }
823
+
824
+ println(getUserProfile(42)) // Profile for user_42@example.com
825
+ println(getUserProfile(-1)) // Guest user
826
+ ```