@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
|
@@ -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
|
+
```
|