@nunofyobiz/effect-extras 2.1.0 → 3.0.0
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/README.md +2 -1
- package/dist/ArrayX.d.ts +0 -34
- package/dist/ArrayX.d.ts.map +1 -1
- package/dist/ArrayX.js +4 -58
- package/dist/ArrayX.js.map +1 -1
- package/dist/InclusiveOr.d.ts +1123 -0
- package/dist/InclusiveOr.d.ts.map +1 -0
- package/dist/InclusiveOr.js +1074 -0
- package/dist/InclusiveOr.js.map +1 -0
- package/dist/NonNullableX.d.ts.map +1 -1
- package/dist/NonNullableX.js +5 -0
- package/dist/NonNullableX.js.map +1 -1
- package/dist/OptionX.d.ts +8 -2
- package/dist/OptionX.d.ts.map +1 -1
- package/dist/OptionX.js +7 -1
- package/dist/OptionX.js.map +1 -1
- package/dist/PredicateX.d.ts +32 -0
- package/dist/PredicateX.d.ts.map +1 -1
- package/dist/PredicateX.js +38 -0
- package/dist/PredicateX.js.map +1 -1
- package/dist/RecordX.d.ts +128 -1
- package/dist/RecordX.d.ts.map +1 -1
- package/dist/RecordX.js +162 -1
- package/dist/RecordX.js.map +1 -1
- package/dist/StringX.d.ts +61 -0
- package/dist/StringX.d.ts.map +1 -1
- package/dist/StringX.js +68 -0
- package/dist/StringX.js.map +1 -1
- package/dist/WarnResult.d.ts +115 -70
- package/dist/WarnResult.d.ts.map +1 -1
- package/dist/WarnResult.js +141 -210
- package/dist/WarnResult.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/ArrayX.ts +4 -86
- package/src/InclusiveOr.ts +1255 -0
- package/src/NonNullableX.ts +5 -0
- package/src/OptionX.ts +8 -2
- package/src/PredicateX.ts +41 -0
- package/src/RecordX.ts +183 -1
- package/src/StringX.ts +113 -0
- package/src/WarnResult.ts +297 -227
- package/src/index.ts +1 -0
|
@@ -0,0 +1,1074 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `InclusiveOr` data type — an inclusive-or carrying a `left`, a `right`, or both at once.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.0.0
|
|
5
|
+
*/
|
|
6
|
+
import { Array, Data, Effect, Option, Predicate, Struct, pipe } from "effect";
|
|
7
|
+
import { constUndefined, dual, identity } from "effect/Function";
|
|
8
|
+
const taggedEnum = /*#__PURE__*/Data.taggedEnum();
|
|
9
|
+
/**
|
|
10
|
+
* Constructs a `LeftOnly` — an `InclusiveOr` that carries only a `left`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
15
|
+
*
|
|
16
|
+
* const value = InclusiveOr.LeftOnly({ left: 1 })
|
|
17
|
+
*
|
|
18
|
+
* assert.deepStrictEqual(value._tag, "LeftOnly")
|
|
19
|
+
* assert.deepStrictEqual(value.left, 1)
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @category constructors
|
|
23
|
+
* @since 0.0.0
|
|
24
|
+
*/
|
|
25
|
+
export const LeftOnly = taggedEnum.LeftOnly;
|
|
26
|
+
/**
|
|
27
|
+
* Constructs a `RightOnly` — an `InclusiveOr` that carries only a `right`.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
32
|
+
*
|
|
33
|
+
* const value = InclusiveOr.RightOnly({ right: "a" })
|
|
34
|
+
*
|
|
35
|
+
* assert.deepStrictEqual(value._tag, "RightOnly")
|
|
36
|
+
* assert.deepStrictEqual(value.right, "a")
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @category constructors
|
|
40
|
+
* @since 0.0.0
|
|
41
|
+
*/
|
|
42
|
+
export const RightOnly = taggedEnum.RightOnly;
|
|
43
|
+
/**
|
|
44
|
+
* Constructs a `LeftAndRight` — an `InclusiveOr` that carries both a `left` and a
|
|
45
|
+
* `right`.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
50
|
+
*
|
|
51
|
+
* const value = InclusiveOr.LeftAndRight({ left: 1, right: "a" })
|
|
52
|
+
*
|
|
53
|
+
* assert.deepStrictEqual(value._tag, "LeftAndRight")
|
|
54
|
+
* assert.deepStrictEqual(value.left, 1)
|
|
55
|
+
* assert.deepStrictEqual(value.right, "a")
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @category constructors
|
|
59
|
+
* @since 0.0.0
|
|
60
|
+
*/
|
|
61
|
+
export const LeftAndRight = taggedEnum.LeftAndRight;
|
|
62
|
+
/**
|
|
63
|
+
* Builds per-tag refinements for `InclusiveOr`. `is("LeftOnly")` is a type guard that
|
|
64
|
+
* narrows an `InclusiveOr` to its `LeftOnly` member, and likewise for `"RightOnly"` and
|
|
65
|
+
* `"LeftAndRight"`.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
70
|
+
*
|
|
71
|
+
* assert.deepStrictEqual(InclusiveOr.is("LeftOnly")(InclusiveOr.LeftOnly({ left: 1 })), true)
|
|
72
|
+
* assert.deepStrictEqual(
|
|
73
|
+
* InclusiveOr.is("LeftOnly")(InclusiveOr.RightOnly({ right: "a" })),
|
|
74
|
+
* false
|
|
75
|
+
* )
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @category guards
|
|
79
|
+
* @since 0.0.0
|
|
80
|
+
*/
|
|
81
|
+
export const is = taggedEnum.$is;
|
|
82
|
+
/**
|
|
83
|
+
* Folds an `InclusiveOr` over its three tags. Provide a handler for `LeftOnly`,
|
|
84
|
+
* `RightOnly`, and `LeftAndRight` and `match` returns a function from an `InclusiveOr`
|
|
85
|
+
* to the handlers' common result type.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
90
|
+
*
|
|
91
|
+
* const describe = InclusiveOr.match({
|
|
92
|
+
* LeftOnly: ({ left }) => `left ${left}`,
|
|
93
|
+
* RightOnly: ({ right }) => `right ${right}`,
|
|
94
|
+
* LeftAndRight: ({ left, right }) => `both ${left}/${right}`
|
|
95
|
+
* })
|
|
96
|
+
*
|
|
97
|
+
* assert.deepStrictEqual(
|
|
98
|
+
* describe(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
|
|
99
|
+
* "both 1/a"
|
|
100
|
+
* )
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @category pattern matching
|
|
104
|
+
* @since 0.0.0
|
|
105
|
+
*/
|
|
106
|
+
export const match = taggedEnum.$match;
|
|
107
|
+
/**
|
|
108
|
+
* Builds an `InclusiveOr` known to carry a `left`, choosing `LeftAndRight` when `right`
|
|
109
|
+
* is present and `LeftOnly` otherwise.
|
|
110
|
+
*
|
|
111
|
+
* Use it when the `left` is mandatory and the `right` is an optional companion:
|
|
112
|
+
* pass an absent (`null`/`undefined`) `right` to get a `LeftOnly`, or a present
|
|
113
|
+
* one to get a `LeftAndRight`. The return type `WithLeft<L, R>` reflects that a
|
|
114
|
+
* `left` is always present.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
119
|
+
*
|
|
120
|
+
* assert.deepStrictEqual(
|
|
121
|
+
* InclusiveOr.WithLeft({ left: 1, right: "a" }),
|
|
122
|
+
* InclusiveOr.LeftAndRight({ left: 1, right: "a" })
|
|
123
|
+
* )
|
|
124
|
+
*
|
|
125
|
+
* assert.deepStrictEqual(
|
|
126
|
+
* InclusiveOr.WithLeft({ left: 1 }),
|
|
127
|
+
* InclusiveOr.LeftOnly({ left: 1 })
|
|
128
|
+
* )
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @category constructors
|
|
132
|
+
* @since 0.0.0
|
|
133
|
+
*/
|
|
134
|
+
export const WithLeft = ({
|
|
135
|
+
left,
|
|
136
|
+
right
|
|
137
|
+
}) => Predicate.isNotNullish(right) ? LeftAndRight({
|
|
138
|
+
left,
|
|
139
|
+
right
|
|
140
|
+
}) : LeftOnly({
|
|
141
|
+
left
|
|
142
|
+
});
|
|
143
|
+
/**
|
|
144
|
+
* Builds an `InclusiveOr` known to carry a `right`, choosing `LeftAndRight` when `left`
|
|
145
|
+
* is present and `RightOnly` otherwise.
|
|
146
|
+
*
|
|
147
|
+
* The mirror of `WithLeft`: the `right` is mandatory and the `left` is an
|
|
148
|
+
* optional companion. Pass an absent (`null`/`undefined`) `left` to get a
|
|
149
|
+
* `RightOnly`, or a present one to get a `LeftAndRight`. The return type
|
|
150
|
+
* `WithRight<L, R>` reflects that a `right` is always present.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
155
|
+
*
|
|
156
|
+
* assert.deepStrictEqual(
|
|
157
|
+
* InclusiveOr.WithRight({ left: 1, right: "a" }),
|
|
158
|
+
* InclusiveOr.LeftAndRight({ left: 1, right: "a" })
|
|
159
|
+
* )
|
|
160
|
+
*
|
|
161
|
+
* assert.deepStrictEqual(
|
|
162
|
+
* InclusiveOr.WithRight({ right: "a" }),
|
|
163
|
+
* InclusiveOr.RightOnly({ right: "a" })
|
|
164
|
+
* )
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @category constructors
|
|
168
|
+
* @since 0.0.0
|
|
169
|
+
*/
|
|
170
|
+
export const WithRight = ({
|
|
171
|
+
left,
|
|
172
|
+
right
|
|
173
|
+
}) => Predicate.isNotNullish(left) ? LeftAndRight({
|
|
174
|
+
left,
|
|
175
|
+
right
|
|
176
|
+
}) : RightOnly({
|
|
177
|
+
right
|
|
178
|
+
});
|
|
179
|
+
/**
|
|
180
|
+
* Builds an `InclusiveOr` from a pair of possibly-nullish inputs, wrapping the result in
|
|
181
|
+
* an `Option` so the all-absent case is expressible.
|
|
182
|
+
*
|
|
183
|
+
* Returns `Option.some(LeftAndRight)` when both are present, `Option.some(LeftOnly)`
|
|
184
|
+
* or `Option.some(RightOnly)` when exactly one is present, and `Option.none()`
|
|
185
|
+
* when both are nullish. Use it as the total entry point for turning two optional
|
|
186
|
+
* values into an `InclusiveOr`.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
191
|
+
* import { Option } from "effect"
|
|
192
|
+
*
|
|
193
|
+
* assert.deepStrictEqual(
|
|
194
|
+
* InclusiveOr.optionFromNullables({ left: 1, right: "a" }),
|
|
195
|
+
* Option.some(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))
|
|
196
|
+
* )
|
|
197
|
+
*
|
|
198
|
+
* assert.deepStrictEqual(
|
|
199
|
+
* InclusiveOr.optionFromNullables({ left: 1, right: null }),
|
|
200
|
+
* Option.some(InclusiveOr.LeftOnly({ left: 1 }))
|
|
201
|
+
* )
|
|
202
|
+
*
|
|
203
|
+
* assert.deepStrictEqual(
|
|
204
|
+
* InclusiveOr.optionFromNullables({ left: null, right: undefined }),
|
|
205
|
+
* Option.none()
|
|
206
|
+
* )
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* @category constructors
|
|
210
|
+
* @since 0.0.0
|
|
211
|
+
*/
|
|
212
|
+
export const optionFromNullables = ({
|
|
213
|
+
left,
|
|
214
|
+
right
|
|
215
|
+
}) => {
|
|
216
|
+
if (Predicate.isNotNullish(left) && Predicate.isNotNullish(right)) {
|
|
217
|
+
return Option.some(LeftAndRight({
|
|
218
|
+
left,
|
|
219
|
+
right
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
if (Predicate.isNotNullish(left)) {
|
|
223
|
+
return Option.some(LeftOnly({
|
|
224
|
+
left
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
if (Predicate.isNotNullish(right)) {
|
|
228
|
+
return Option.some(RightOnly({
|
|
229
|
+
right
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
return Option.none();
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Builds an `InclusiveOr` from a pair of possibly-nullish inputs, falling back to the
|
|
236
|
+
* `orElse` thunk when both are absent.
|
|
237
|
+
*
|
|
238
|
+
* The non-optional companion to `optionFromNullables`: it unwraps the same logic
|
|
239
|
+
* but resolves the all-absent case with `orElse` instead of an `Option`. The
|
|
240
|
+
* default `orElse` throws, so omit it only when at least one side is guaranteed
|
|
241
|
+
* present.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```ts
|
|
245
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
246
|
+
*
|
|
247
|
+
* assert.deepStrictEqual(
|
|
248
|
+
* InclusiveOr.fromNullables({ left: 1, right: "a" }),
|
|
249
|
+
* InclusiveOr.LeftAndRight({ left: 1, right: "a" })
|
|
250
|
+
* )
|
|
251
|
+
*
|
|
252
|
+
* // Both absent — fall back via orElse instead of throwing
|
|
253
|
+
* assert.deepStrictEqual(
|
|
254
|
+
* InclusiveOr.fromNullables({
|
|
255
|
+
* left: null,
|
|
256
|
+
* right: null,
|
|
257
|
+
* orElse: () => InclusiveOr.LeftOnly({ left: 0 })
|
|
258
|
+
* }),
|
|
259
|
+
* InclusiveOr.LeftOnly({ left: 0 })
|
|
260
|
+
* )
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* @category constructors
|
|
264
|
+
* @since 0.0.0
|
|
265
|
+
*/
|
|
266
|
+
export const fromNullables = ({
|
|
267
|
+
left,
|
|
268
|
+
right,
|
|
269
|
+
orElse = () => {
|
|
270
|
+
throw new Error("Both left and right are nullable");
|
|
271
|
+
}
|
|
272
|
+
}) => pipe(optionFromNullables({
|
|
273
|
+
left,
|
|
274
|
+
right
|
|
275
|
+
}), Option.getOrElse(orElse));
|
|
276
|
+
/**
|
|
277
|
+
* Folds an `InclusiveOr` from the left's perspective, collapsing the three tags into two
|
|
278
|
+
* handlers.
|
|
279
|
+
*
|
|
280
|
+
* Both `LeftOnly` and `LeftAndRight` carry a `left`, so they route to the `Left`
|
|
281
|
+
* handler; only `RightOnly` lacks a `left` and routes to `RightOnly`. Use it when
|
|
282
|
+
* you care about the `left` value and treat the right-only case as the exception.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
287
|
+
*
|
|
288
|
+
* const onLeft = InclusiveOr.matchLeft({
|
|
289
|
+
* Left: (left: number) => `left ${left}`,
|
|
290
|
+
* RightOnly: (right: string) => `right ${right}`
|
|
291
|
+
* })
|
|
292
|
+
*
|
|
293
|
+
* assert.deepStrictEqual(onLeft(InclusiveOr.LeftOnly({ left: 1 })), "left 1")
|
|
294
|
+
* assert.deepStrictEqual(
|
|
295
|
+
* onLeft(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
|
|
296
|
+
* "left 1"
|
|
297
|
+
* )
|
|
298
|
+
* assert.deepStrictEqual(onLeft(InclusiveOr.RightOnly({ right: "a" })), "right a")
|
|
299
|
+
* ```
|
|
300
|
+
*
|
|
301
|
+
* @category pattern matching
|
|
302
|
+
* @since 0.0.0
|
|
303
|
+
*/
|
|
304
|
+
export const matchLeft = ({
|
|
305
|
+
Left,
|
|
306
|
+
RightOnly
|
|
307
|
+
}) => inclusiveOr =>
|
|
308
|
+
// `$match` widens the common return to `Unify<A>`; narrow it back to `A`.
|
|
309
|
+
match(inclusiveOr, {
|
|
310
|
+
LeftOnly: ({
|
|
311
|
+
left
|
|
312
|
+
}) => Left(left),
|
|
313
|
+
RightOnly: ({
|
|
314
|
+
right
|
|
315
|
+
}) => RightOnly(right),
|
|
316
|
+
LeftAndRight: ({
|
|
317
|
+
left
|
|
318
|
+
}) => Left(left)
|
|
319
|
+
});
|
|
320
|
+
/**
|
|
321
|
+
* Folds an `InclusiveOr` from the right's perspective, collapsing the three tags into two
|
|
322
|
+
* handlers.
|
|
323
|
+
*
|
|
324
|
+
* The mirror of `matchLeft`: both `RightOnly` and `LeftAndRight` carry a `right`,
|
|
325
|
+
* so they route to the `Right` handler; only `LeftOnly` lacks a `right` and routes
|
|
326
|
+
* to `LeftOnly`. Use it when you care about the `right` value and treat the
|
|
327
|
+
* left-only case as the exception.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```ts
|
|
331
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
332
|
+
*
|
|
333
|
+
* const onRight = InclusiveOr.matchRight({
|
|
334
|
+
* LeftOnly: (left: number) => `left ${left}`,
|
|
335
|
+
* Right: (right: string) => `right ${right}`
|
|
336
|
+
* })
|
|
337
|
+
*
|
|
338
|
+
* assert.deepStrictEqual(onRight(InclusiveOr.RightOnly({ right: "a" })), "right a")
|
|
339
|
+
* assert.deepStrictEqual(
|
|
340
|
+
* onRight(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
|
|
341
|
+
* "right a"
|
|
342
|
+
* )
|
|
343
|
+
* assert.deepStrictEqual(onRight(InclusiveOr.LeftOnly({ left: 1 })), "left 1")
|
|
344
|
+
* ```
|
|
345
|
+
*
|
|
346
|
+
* @category pattern matching
|
|
347
|
+
* @since 0.0.0
|
|
348
|
+
*/
|
|
349
|
+
export const matchRight = ({
|
|
350
|
+
LeftOnly,
|
|
351
|
+
Right
|
|
352
|
+
}) => inclusiveOr =>
|
|
353
|
+
// `$match` widens the common return to `Unify<A>`; narrow it back to `A`.
|
|
354
|
+
match(inclusiveOr, {
|
|
355
|
+
LeftOnly: ({
|
|
356
|
+
left
|
|
357
|
+
}) => LeftOnly(left),
|
|
358
|
+
RightOnly: ({
|
|
359
|
+
right
|
|
360
|
+
}) => Right(right),
|
|
361
|
+
LeftAndRight: ({
|
|
362
|
+
right
|
|
363
|
+
}) => Right(right)
|
|
364
|
+
});
|
|
365
|
+
/**
|
|
366
|
+
* Completes an `InclusiveOr` into a guaranteed `LeftAndRight` by filling whichever side
|
|
367
|
+
* is missing from the matching `orElse` thunk.
|
|
368
|
+
*
|
|
369
|
+
* A `LeftAndRight` passes through unchanged; a `LeftOnly` gains a `right` from
|
|
370
|
+
* `orElseRight`; a `RightOnly` gains a `left` from `orElseLeft`. Use it to
|
|
371
|
+
* normalise a partial `InclusiveOr` into the both-present shape before reading both
|
|
372
|
+
* sides.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```ts
|
|
376
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
377
|
+
*
|
|
378
|
+
* const fill = InclusiveOr.orElse({
|
|
379
|
+
* orElseLeft: () => 0,
|
|
380
|
+
* orElseRight: () => "default"
|
|
381
|
+
* })
|
|
382
|
+
*
|
|
383
|
+
* assert.deepStrictEqual(
|
|
384
|
+
* fill(InclusiveOr.LeftOnly({ left: 1 })),
|
|
385
|
+
* InclusiveOr.LeftAndRight({ left: 1, right: "default" })
|
|
386
|
+
* )
|
|
387
|
+
* assert.deepStrictEqual(
|
|
388
|
+
* fill(InclusiveOr.RightOnly({ right: "a" })),
|
|
389
|
+
* InclusiveOr.LeftAndRight({ left: 0, right: "a" })
|
|
390
|
+
* )
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* @category getters
|
|
394
|
+
* @since 0.0.0
|
|
395
|
+
*/
|
|
396
|
+
export const orElse = ({
|
|
397
|
+
orElseLeft,
|
|
398
|
+
orElseRight
|
|
399
|
+
}) => match({
|
|
400
|
+
LeftOnly: ({
|
|
401
|
+
left
|
|
402
|
+
}) => LeftAndRight({
|
|
403
|
+
left,
|
|
404
|
+
right: orElseRight()
|
|
405
|
+
}),
|
|
406
|
+
RightOnly: ({
|
|
407
|
+
right
|
|
408
|
+
}) => LeftAndRight({
|
|
409
|
+
left: orElseLeft(),
|
|
410
|
+
right
|
|
411
|
+
}),
|
|
412
|
+
LeftAndRight: ({
|
|
413
|
+
left,
|
|
414
|
+
right
|
|
415
|
+
}) => LeftAndRight({
|
|
416
|
+
left,
|
|
417
|
+
right
|
|
418
|
+
})
|
|
419
|
+
});
|
|
420
|
+
/**
|
|
421
|
+
* Completes an `InclusiveOr` into a `LeftAndRight` whose missing side is filled with
|
|
422
|
+
* `undefined`.
|
|
423
|
+
*
|
|
424
|
+
* A specialisation of `orElse` that supplies `undefined` for whichever side is
|
|
425
|
+
* absent, so the result always exposes both `left` and `right` keys (each
|
|
426
|
+
* possibly `undefined`). Use it when you want to destructure both sides without
|
|
427
|
+
* branching on the tag.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```ts
|
|
431
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
432
|
+
*
|
|
433
|
+
* assert.deepStrictEqual(
|
|
434
|
+
* InclusiveOr.orUndefined(InclusiveOr.LeftOnly({ left: 1 })),
|
|
435
|
+
* InclusiveOr.LeftAndRight({ left: 1, right: undefined })
|
|
436
|
+
* )
|
|
437
|
+
* assert.deepStrictEqual(
|
|
438
|
+
* InclusiveOr.orUndefined(InclusiveOr.RightOnly({ right: "a" })),
|
|
439
|
+
* InclusiveOr.LeftAndRight({ left: undefined, right: "a" })
|
|
440
|
+
* )
|
|
441
|
+
* ```
|
|
442
|
+
*
|
|
443
|
+
* @category getters
|
|
444
|
+
* @since 0.0.0
|
|
445
|
+
*/
|
|
446
|
+
export const orUndefined = /*#__PURE__*/orElse({
|
|
447
|
+
orElseLeft: () => undefined,
|
|
448
|
+
orElseRight: () => undefined
|
|
449
|
+
});
|
|
450
|
+
/**
|
|
451
|
+
* Extracts the `left` of an `InclusiveOr`, falling back to `orElseReturn` when no `left`
|
|
452
|
+
* is present.
|
|
453
|
+
*
|
|
454
|
+
* `LeftOnly` and `LeftAndRight` return their `left`; `RightOnly` returns the
|
|
455
|
+
* result of `orElseReturn`. Use it to read the left side with a default in one
|
|
456
|
+
* step.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```ts
|
|
460
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
461
|
+
*
|
|
462
|
+
* const leftOrZero = InclusiveOr.leftOrElse(() => 0)
|
|
463
|
+
*
|
|
464
|
+
* assert.deepStrictEqual(leftOrZero(InclusiveOr.LeftOnly({ left: 1 })), 1)
|
|
465
|
+
* assert.deepStrictEqual(
|
|
466
|
+
* leftOrZero(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
|
|
467
|
+
* 1
|
|
468
|
+
* )
|
|
469
|
+
* assert.deepStrictEqual(leftOrZero(InclusiveOr.RightOnly({ right: "a" })), 0)
|
|
470
|
+
* ```
|
|
471
|
+
*
|
|
472
|
+
* @category getters
|
|
473
|
+
* @since 0.0.0
|
|
474
|
+
*/
|
|
475
|
+
export const leftOrElse = orElseReturn => inclusiveOr => pipe(inclusiveOr, orElse({
|
|
476
|
+
orElseLeft: orElseReturn,
|
|
477
|
+
orElseRight: constUndefined
|
|
478
|
+
}), Struct.get("left"));
|
|
479
|
+
/**
|
|
480
|
+
* Extracts the `left` of an `InclusiveOr`, returning `undefined` when no `left` is
|
|
481
|
+
* present.
|
|
482
|
+
*
|
|
483
|
+
* A specialisation of `leftOrElse` whose fallback is `undefined`: `LeftOnly` and
|
|
484
|
+
* `LeftAndRight` yield their `left`, while `RightOnly` yields `undefined`.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```ts
|
|
488
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
489
|
+
*
|
|
490
|
+
* assert.deepStrictEqual(InclusiveOr.leftOrUndefined(InclusiveOr.LeftOnly({ left: 1 })), 1)
|
|
491
|
+
* assert.deepStrictEqual(
|
|
492
|
+
* InclusiveOr.leftOrUndefined(InclusiveOr.RightOnly({ right: "a" })),
|
|
493
|
+
* undefined
|
|
494
|
+
* )
|
|
495
|
+
* ```
|
|
496
|
+
*
|
|
497
|
+
* @category getters
|
|
498
|
+
* @since 0.0.0
|
|
499
|
+
*/
|
|
500
|
+
export const leftOrUndefined = /*#__PURE__*/leftOrElse(() => undefined);
|
|
501
|
+
/**
|
|
502
|
+
* Extracts the `right` of an `InclusiveOr`, falling back to `orElseReturn` when no
|
|
503
|
+
* `right` is present.
|
|
504
|
+
*
|
|
505
|
+
* The mirror of `leftOrElse`: `RightOnly` and `LeftAndRight` return their
|
|
506
|
+
* `right`; `LeftOnly` returns the result of `orElseReturn`.
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* ```ts
|
|
510
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
511
|
+
*
|
|
512
|
+
* const rightOrDefault = InclusiveOr.rightOrElse(() => "default")
|
|
513
|
+
*
|
|
514
|
+
* assert.deepStrictEqual(
|
|
515
|
+
* rightOrDefault(InclusiveOr.RightOnly({ right: "a" })),
|
|
516
|
+
* "a"
|
|
517
|
+
* )
|
|
518
|
+
* assert.deepStrictEqual(
|
|
519
|
+
* rightOrDefault(InclusiveOr.LeftOnly({ left: 1 })),
|
|
520
|
+
* "default"
|
|
521
|
+
* )
|
|
522
|
+
* ```
|
|
523
|
+
*
|
|
524
|
+
* @category getters
|
|
525
|
+
* @since 0.0.0
|
|
526
|
+
*/
|
|
527
|
+
export const rightOrElse = orElseReturn => inclusiveOr => pipe(inclusiveOr, orElse({
|
|
528
|
+
orElseLeft: constUndefined,
|
|
529
|
+
orElseRight: orElseReturn
|
|
530
|
+
}), Struct.get("right"));
|
|
531
|
+
/**
|
|
532
|
+
* Extracts the `right` of an `InclusiveOr`, returning `undefined` when no `right` is
|
|
533
|
+
* present.
|
|
534
|
+
*
|
|
535
|
+
* A specialisation of `rightOrElse` whose fallback is `undefined`: `RightOnly` and
|
|
536
|
+
* `LeftAndRight` yield their `right`, while `LeftOnly` yields `undefined`.
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* ```ts
|
|
540
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
541
|
+
*
|
|
542
|
+
* assert.deepStrictEqual(
|
|
543
|
+
* InclusiveOr.rightOrUndefined(InclusiveOr.RightOnly({ right: "a" })),
|
|
544
|
+
* "a"
|
|
545
|
+
* )
|
|
546
|
+
* assert.deepStrictEqual(
|
|
547
|
+
* InclusiveOr.rightOrUndefined(InclusiveOr.LeftOnly({ left: 1 })),
|
|
548
|
+
* undefined
|
|
549
|
+
* )
|
|
550
|
+
* ```
|
|
551
|
+
*
|
|
552
|
+
* @category getters
|
|
553
|
+
* @since 0.0.0
|
|
554
|
+
*/
|
|
555
|
+
export const rightOrUndefined = /*#__PURE__*/rightOrElse(() => undefined);
|
|
556
|
+
/**
|
|
557
|
+
* Extracts the `right` of an `InclusiveOr` as an `Option`.
|
|
558
|
+
*
|
|
559
|
+
* `RightOnly` and `LeftAndRight` yield `Option.some(right)`; `LeftOnly` yields
|
|
560
|
+
* `Option.none()`. Use it when you want to chain the right side through `Option`
|
|
561
|
+
* combinators rather than fall back to a default eagerly.
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* ```ts
|
|
565
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
566
|
+
* import { Option } from "effect"
|
|
567
|
+
*
|
|
568
|
+
* assert.deepStrictEqual(
|
|
569
|
+
* InclusiveOr.rightOption(InclusiveOr.RightOnly({ right: "a" })),
|
|
570
|
+
* Option.some("a")
|
|
571
|
+
* )
|
|
572
|
+
* assert.deepStrictEqual(
|
|
573
|
+
* InclusiveOr.rightOption(InclusiveOr.LeftOnly({ left: 1 })),
|
|
574
|
+
* Option.none()
|
|
575
|
+
* )
|
|
576
|
+
* ```
|
|
577
|
+
*
|
|
578
|
+
* @category getters
|
|
579
|
+
* @since 0.0.0
|
|
580
|
+
*/
|
|
581
|
+
export const rightOption = inclusiveOr => pipe(inclusiveOr, matchRight({
|
|
582
|
+
LeftOnly: () => Option.none(),
|
|
583
|
+
Right: Option.some
|
|
584
|
+
}));
|
|
585
|
+
/**
|
|
586
|
+
* Extracts the `left` of an `InclusiveOr` as an `Option`.
|
|
587
|
+
*
|
|
588
|
+
* The mirror of `rightOption`: `LeftOnly` and `LeftAndRight` yield
|
|
589
|
+
* `Option.some(left)`, while `RightOnly` yields `Option.none()`.
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* ```ts
|
|
593
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
594
|
+
* import { Option } from "effect"
|
|
595
|
+
*
|
|
596
|
+
* assert.deepStrictEqual(
|
|
597
|
+
* InclusiveOr.leftOption(InclusiveOr.LeftOnly({ left: 1 })),
|
|
598
|
+
* Option.some(1)
|
|
599
|
+
* )
|
|
600
|
+
* assert.deepStrictEqual(
|
|
601
|
+
* InclusiveOr.leftOption(InclusiveOr.RightOnly({ right: "a" })),
|
|
602
|
+
* Option.none()
|
|
603
|
+
* )
|
|
604
|
+
* ```
|
|
605
|
+
*
|
|
606
|
+
* @category getters
|
|
607
|
+
* @since 0.0.0
|
|
608
|
+
*/
|
|
609
|
+
export const leftOption = inclusiveOr => pipe(inclusiveOr, matchLeft({
|
|
610
|
+
Left: Option.some,
|
|
611
|
+
RightOnly: () => Option.none()
|
|
612
|
+
}));
|
|
613
|
+
/**
|
|
614
|
+
* Transforms both sides of an `InclusiveOr`, applying `mapLeft` to any `left` and
|
|
615
|
+
* `mapRight` to any `right`.
|
|
616
|
+
*
|
|
617
|
+
* Each constructor is rebuilt with its mapped contents, so `LeftOnly` maps only
|
|
618
|
+
* the left, `RightOnly` only the right, and `LeftAndRight` both. The tag is
|
|
619
|
+
* preserved. Use it as the bifunctor map over `InclusiveOr`.
|
|
620
|
+
*
|
|
621
|
+
* @example
|
|
622
|
+
* ```ts
|
|
623
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
624
|
+
*
|
|
625
|
+
* const both = InclusiveOr.mapBoth({
|
|
626
|
+
* mapLeft: (left: number) => left + 1,
|
|
627
|
+
* mapRight: (right: string) => right.toUpperCase()
|
|
628
|
+
* })
|
|
629
|
+
*
|
|
630
|
+
* assert.deepStrictEqual(
|
|
631
|
+
* both(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
|
|
632
|
+
* InclusiveOr.LeftAndRight({ left: 2, right: "A" })
|
|
633
|
+
* )
|
|
634
|
+
* assert.deepStrictEqual(
|
|
635
|
+
* both(InclusiveOr.LeftOnly({ left: 1 })),
|
|
636
|
+
* InclusiveOr.LeftOnly({ left: 2 })
|
|
637
|
+
* )
|
|
638
|
+
* ```
|
|
639
|
+
*
|
|
640
|
+
* @category mapping
|
|
641
|
+
* @since 0.0.0
|
|
642
|
+
*/
|
|
643
|
+
export const mapBoth = ({
|
|
644
|
+
mapLeft,
|
|
645
|
+
mapRight
|
|
646
|
+
}) => match({
|
|
647
|
+
LeftOnly: ({
|
|
648
|
+
left
|
|
649
|
+
}) => LeftOnly({
|
|
650
|
+
left: mapLeft(left)
|
|
651
|
+
}),
|
|
652
|
+
RightOnly: ({
|
|
653
|
+
right
|
|
654
|
+
}) => RightOnly({
|
|
655
|
+
right: mapRight(right)
|
|
656
|
+
}),
|
|
657
|
+
LeftAndRight: ({
|
|
658
|
+
left,
|
|
659
|
+
right
|
|
660
|
+
}) => LeftAndRight({
|
|
661
|
+
left: mapLeft(left),
|
|
662
|
+
right: mapRight(right)
|
|
663
|
+
})
|
|
664
|
+
});
|
|
665
|
+
/**
|
|
666
|
+
* Effectful `mapBoth`: transforms each present side through an `Effect`,
|
|
667
|
+
* reassembling the results into an `InclusiveOr` inside an `Effect`.
|
|
668
|
+
*
|
|
669
|
+
* For `LeftAndRight` both effects run via `Effect.all` and their results are
|
|
670
|
+
* combined; `LeftOnly`/`RightOnly` run only the relevant effect. Errors and
|
|
671
|
+
* requirements from both mappers are unioned into the result type. Use it when
|
|
672
|
+
* mapping an `InclusiveOr`'s sides requires effects (validation, IO).
|
|
673
|
+
*
|
|
674
|
+
* @example
|
|
675
|
+
* ```ts
|
|
676
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
677
|
+
* import { Effect } from "effect"
|
|
678
|
+
*
|
|
679
|
+
* const both = InclusiveOr.mapBothEffect({
|
|
680
|
+
* mapLeft: (left: number) => Effect.succeed(left + 1),
|
|
681
|
+
* mapRight: (right: string) => Effect.succeed(right.toUpperCase())
|
|
682
|
+
* })
|
|
683
|
+
*
|
|
684
|
+
* assert.deepStrictEqual(
|
|
685
|
+
* Effect.runSync(both(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))),
|
|
686
|
+
* InclusiveOr.LeftAndRight({ left: 2, right: "A" })
|
|
687
|
+
* )
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
690
|
+
* @category sequencing
|
|
691
|
+
* @since 0.0.0
|
|
692
|
+
*/
|
|
693
|
+
export const mapBothEffect = ({
|
|
694
|
+
mapLeft,
|
|
695
|
+
mapRight
|
|
696
|
+
}) => match({
|
|
697
|
+
LeftOnly: ({
|
|
698
|
+
left
|
|
699
|
+
}) => pipe(mapLeft(left), Effect.map(left2 => LeftOnly({
|
|
700
|
+
left: left2
|
|
701
|
+
}))),
|
|
702
|
+
RightOnly: ({
|
|
703
|
+
right
|
|
704
|
+
}) => pipe(mapRight(right), Effect.map(right2 => RightOnly({
|
|
705
|
+
right: right2
|
|
706
|
+
}))),
|
|
707
|
+
LeftAndRight: ({
|
|
708
|
+
left,
|
|
709
|
+
right
|
|
710
|
+
}) => pipe(Effect.all({
|
|
711
|
+
left: mapLeft(left),
|
|
712
|
+
right: mapRight(right)
|
|
713
|
+
}), Effect.map(({
|
|
714
|
+
left: left2,
|
|
715
|
+
right: right2
|
|
716
|
+
}) => LeftAndRight({
|
|
717
|
+
left: left2,
|
|
718
|
+
right: right2
|
|
719
|
+
})))
|
|
720
|
+
});
|
|
721
|
+
/**
|
|
722
|
+
* Transforms the `left` of an `InclusiveOr`, leaving any `right` untouched.
|
|
723
|
+
*
|
|
724
|
+
* A specialisation of `mapBoth` with the right mapper set to `identity`:
|
|
725
|
+
* `LeftOnly` and `LeftAndRight` have their `left` mapped, while `RightOnly` passes
|
|
726
|
+
* through unchanged.
|
|
727
|
+
*
|
|
728
|
+
* @example
|
|
729
|
+
* ```ts
|
|
730
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
731
|
+
*
|
|
732
|
+
* const inc = InclusiveOr.mapLeft((left: number) => left + 1)
|
|
733
|
+
*
|
|
734
|
+
* assert.deepStrictEqual(
|
|
735
|
+
* inc(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
|
|
736
|
+
* InclusiveOr.LeftAndRight({ left: 2, right: "a" })
|
|
737
|
+
* )
|
|
738
|
+
* assert.deepStrictEqual(
|
|
739
|
+
* inc(InclusiveOr.RightOnly({ right: "a" })),
|
|
740
|
+
* InclusiveOr.RightOnly({ right: "a" })
|
|
741
|
+
* )
|
|
742
|
+
* ```
|
|
743
|
+
*
|
|
744
|
+
* @category mapping
|
|
745
|
+
* @since 0.0.0
|
|
746
|
+
*/
|
|
747
|
+
export const mapLeft = mapLeft => mapBoth({
|
|
748
|
+
mapLeft,
|
|
749
|
+
mapRight: identity
|
|
750
|
+
});
|
|
751
|
+
/**
|
|
752
|
+
* Chains the `left` of an `InclusiveOr` into a new `InclusiveOr`, flattening the result.
|
|
753
|
+
*
|
|
754
|
+
* Whenever a `left` is present (`LeftOnly` or `LeftAndRight`) it is passed to
|
|
755
|
+
* `mapLeft`, whose returned `InclusiveOr` replaces the original; `RightOnly` passes
|
|
756
|
+
* through unchanged. Use it to sequence left-driven computations that themselves
|
|
757
|
+
* produce an `InclusiveOr`.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* ```ts
|
|
761
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
762
|
+
*
|
|
763
|
+
* const chain = InclusiveOr.flatMapLeft((left: number) =>
|
|
764
|
+
* left > 0
|
|
765
|
+
* ? InclusiveOr.LeftOnly({ left: left * 10 })
|
|
766
|
+
* : InclusiveOr.RightOnly({ right: "non-positive" })
|
|
767
|
+
* )
|
|
768
|
+
*
|
|
769
|
+
* assert.deepStrictEqual(
|
|
770
|
+
* chain(InclusiveOr.LeftOnly({ left: 2 })),
|
|
771
|
+
* InclusiveOr.LeftOnly({ left: 20 })
|
|
772
|
+
* )
|
|
773
|
+
* assert.deepStrictEqual(
|
|
774
|
+
* chain(InclusiveOr.RightOnly({ right: "a" })),
|
|
775
|
+
* InclusiveOr.RightOnly({ right: "a" })
|
|
776
|
+
* )
|
|
777
|
+
* ```
|
|
778
|
+
*
|
|
779
|
+
* @category sequencing
|
|
780
|
+
* @since 0.0.0
|
|
781
|
+
*/
|
|
782
|
+
export const flatMapLeft = mapLeft => match({
|
|
783
|
+
LeftOnly: ({
|
|
784
|
+
left
|
|
785
|
+
}) => mapLeft(left),
|
|
786
|
+
RightOnly: ({
|
|
787
|
+
right
|
|
788
|
+
}) => RightOnly({
|
|
789
|
+
right
|
|
790
|
+
}),
|
|
791
|
+
LeftAndRight: ({
|
|
792
|
+
left
|
|
793
|
+
}) => mapLeft(left)
|
|
794
|
+
});
|
|
795
|
+
/**
|
|
796
|
+
* Effectful `mapLeft`: transforms the `left` of an `InclusiveOr` through an `Effect`,
|
|
797
|
+
* leaving any `right` untouched.
|
|
798
|
+
*
|
|
799
|
+
* A specialisation of `mapBothEffect` with the right mapper set to
|
|
800
|
+
* `Effect.succeed`: the `left` (when present) is mapped effectfully and the
|
|
801
|
+
* `right` is carried through unchanged.
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```ts
|
|
805
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
806
|
+
* import { Effect } from "effect"
|
|
807
|
+
*
|
|
808
|
+
* const inc = InclusiveOr.mapLeftEffect((left: number) => Effect.succeed(left + 1))
|
|
809
|
+
*
|
|
810
|
+
* assert.deepStrictEqual(
|
|
811
|
+
* Effect.runSync(inc(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))),
|
|
812
|
+
* InclusiveOr.LeftAndRight({ left: 2, right: "a" })
|
|
813
|
+
* )
|
|
814
|
+
* ```
|
|
815
|
+
*
|
|
816
|
+
* @category sequencing
|
|
817
|
+
* @since 0.0.0
|
|
818
|
+
*/
|
|
819
|
+
export const mapLeftEffect = mapLeft => mapBothEffect({
|
|
820
|
+
mapLeft,
|
|
821
|
+
mapRight: Effect.succeed
|
|
822
|
+
});
|
|
823
|
+
/**
|
|
824
|
+
* Effectful `flatMapLeft`: chains the `left` of an `InclusiveOr` into an `Effect` that
|
|
825
|
+
* yields a new `InclusiveOr`, flattening the result.
|
|
826
|
+
*
|
|
827
|
+
* When a `left` is present it is passed to `mapLeft`, whose effectful `InclusiveOr`
|
|
828
|
+
* replaces the original; `RightOnly` is lifted unchanged via `Effect.succeed`. Use
|
|
829
|
+
* it to sequence left-driven effectful computations that produce an `InclusiveOr`.
|
|
830
|
+
*
|
|
831
|
+
* @example
|
|
832
|
+
* ```ts
|
|
833
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
834
|
+
* import { Effect } from "effect"
|
|
835
|
+
*
|
|
836
|
+
* const chain = InclusiveOr.flatMapLeftEffect((left: number) =>
|
|
837
|
+
* Effect.succeed(InclusiveOr.LeftOnly({ left: left * 10 }))
|
|
838
|
+
* )
|
|
839
|
+
*
|
|
840
|
+
* assert.deepStrictEqual(
|
|
841
|
+
* Effect.runSync(chain(InclusiveOr.LeftOnly({ left: 2 }))),
|
|
842
|
+
* InclusiveOr.LeftOnly({ left: 20 })
|
|
843
|
+
* )
|
|
844
|
+
* assert.deepStrictEqual(
|
|
845
|
+
* Effect.runSync(chain(InclusiveOr.RightOnly({ right: "a" }))),
|
|
846
|
+
* InclusiveOr.RightOnly({ right: "a" })
|
|
847
|
+
* )
|
|
848
|
+
* ```
|
|
849
|
+
*
|
|
850
|
+
* @category sequencing
|
|
851
|
+
* @since 0.0.0
|
|
852
|
+
*/
|
|
853
|
+
export const flatMapLeftEffect = mapLeft => match({
|
|
854
|
+
LeftOnly: ({
|
|
855
|
+
left
|
|
856
|
+
}) => mapLeft(left),
|
|
857
|
+
RightOnly: ({
|
|
858
|
+
right
|
|
859
|
+
}) => Effect.succeed(RightOnly({
|
|
860
|
+
right
|
|
861
|
+
})),
|
|
862
|
+
LeftAndRight: ({
|
|
863
|
+
left
|
|
864
|
+
}) => mapLeft(left)
|
|
865
|
+
});
|
|
866
|
+
/**
|
|
867
|
+
* Transforms the `right` of an `InclusiveOr`, leaving any `left` untouched.
|
|
868
|
+
*
|
|
869
|
+
* The mirror of `mapLeft`: `RightOnly` and `LeftAndRight` have their `right`
|
|
870
|
+
* mapped, while `LeftOnly` passes through unchanged.
|
|
871
|
+
*
|
|
872
|
+
* @example
|
|
873
|
+
* ```ts
|
|
874
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
875
|
+
*
|
|
876
|
+
* const upper = InclusiveOr.mapRight((right: string) => right.toUpperCase())
|
|
877
|
+
*
|
|
878
|
+
* assert.deepStrictEqual(
|
|
879
|
+
* upper(InclusiveOr.LeftAndRight({ left: 1, right: "a" })),
|
|
880
|
+
* InclusiveOr.LeftAndRight({ left: 1, right: "A" })
|
|
881
|
+
* )
|
|
882
|
+
* assert.deepStrictEqual(
|
|
883
|
+
* upper(InclusiveOr.LeftOnly({ left: 1 })),
|
|
884
|
+
* InclusiveOr.LeftOnly({ left: 1 })
|
|
885
|
+
* )
|
|
886
|
+
* ```
|
|
887
|
+
*
|
|
888
|
+
* @category mapping
|
|
889
|
+
* @since 0.0.0
|
|
890
|
+
*/
|
|
891
|
+
export const mapRight = mapRight => mapBoth({
|
|
892
|
+
mapLeft: identity,
|
|
893
|
+
mapRight
|
|
894
|
+
});
|
|
895
|
+
/**
|
|
896
|
+
* Chains the `right` of an `InclusiveOr` into a new `InclusiveOr`, flattening the result.
|
|
897
|
+
*
|
|
898
|
+
* The mirror of `flatMapLeft`: whenever a `right` is present (`RightOnly` or
|
|
899
|
+
* `LeftAndRight`) it is passed to `mapRight`, whose returned `InclusiveOr` replaces the
|
|
900
|
+
* original; `LeftOnly` passes through unchanged.
|
|
901
|
+
*
|
|
902
|
+
* @example
|
|
903
|
+
* ```ts
|
|
904
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
905
|
+
*
|
|
906
|
+
* const chain = InclusiveOr.flatMapRight((right: string) =>
|
|
907
|
+
* InclusiveOr.RightOnly({ right: right.toUpperCase() })
|
|
908
|
+
* )
|
|
909
|
+
*
|
|
910
|
+
* assert.deepStrictEqual(
|
|
911
|
+
* chain(InclusiveOr.RightOnly({ right: "a" })),
|
|
912
|
+
* InclusiveOr.RightOnly({ right: "A" })
|
|
913
|
+
* )
|
|
914
|
+
* assert.deepStrictEqual(
|
|
915
|
+
* chain(InclusiveOr.LeftOnly({ left: 1 })),
|
|
916
|
+
* InclusiveOr.LeftOnly({ left: 1 })
|
|
917
|
+
* )
|
|
918
|
+
* ```
|
|
919
|
+
*
|
|
920
|
+
* @category sequencing
|
|
921
|
+
* @since 0.0.0
|
|
922
|
+
*/
|
|
923
|
+
export const flatMapRight = mapRight => match({
|
|
924
|
+
LeftOnly: ({
|
|
925
|
+
left
|
|
926
|
+
}) => LeftOnly({
|
|
927
|
+
left
|
|
928
|
+
}),
|
|
929
|
+
RightOnly: ({
|
|
930
|
+
right
|
|
931
|
+
}) => mapRight(right),
|
|
932
|
+
LeftAndRight: ({
|
|
933
|
+
right
|
|
934
|
+
}) => mapRight(right)
|
|
935
|
+
});
|
|
936
|
+
/**
|
|
937
|
+
* Effectful `mapRight`: transforms the `right` of an `InclusiveOr` through an `Effect`,
|
|
938
|
+
* leaving any `left` untouched.
|
|
939
|
+
*
|
|
940
|
+
* The mirror of `mapLeftEffect`: the `right` (when present) is mapped effectfully
|
|
941
|
+
* and the `left` is carried through unchanged via `Effect.succeed`.
|
|
942
|
+
*
|
|
943
|
+
* @example
|
|
944
|
+
* ```ts
|
|
945
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
946
|
+
* import { Effect } from "effect"
|
|
947
|
+
*
|
|
948
|
+
* const upper = InclusiveOr.mapRightEffect((right: string) =>
|
|
949
|
+
* Effect.succeed(right.toUpperCase())
|
|
950
|
+
* )
|
|
951
|
+
*
|
|
952
|
+
* assert.deepStrictEqual(
|
|
953
|
+
* Effect.runSync(upper(InclusiveOr.LeftAndRight({ left: 1, right: "a" }))),
|
|
954
|
+
* InclusiveOr.LeftAndRight({ left: 1, right: "A" })
|
|
955
|
+
* )
|
|
956
|
+
* ```
|
|
957
|
+
*
|
|
958
|
+
* @category sequencing
|
|
959
|
+
* @since 0.0.0
|
|
960
|
+
*/
|
|
961
|
+
export const mapRightEffect = mapRight => mapBothEffect({
|
|
962
|
+
mapLeft: Effect.succeed,
|
|
963
|
+
mapRight
|
|
964
|
+
});
|
|
965
|
+
/**
|
|
966
|
+
* Effectful `flatMapRight`: chains the `right` of an `InclusiveOr` into an `Effect` that
|
|
967
|
+
* yields a new `InclusiveOr`, flattening the result.
|
|
968
|
+
*
|
|
969
|
+
* The mirror of `flatMapLeftEffect`: when a `right` is present it is passed to
|
|
970
|
+
* `mapRight`, whose effectful `InclusiveOr` replaces the original; `LeftOnly` is lifted
|
|
971
|
+
* unchanged via `Effect.succeed`.
|
|
972
|
+
*
|
|
973
|
+
* @example
|
|
974
|
+
* ```ts
|
|
975
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
976
|
+
* import { Effect } from "effect"
|
|
977
|
+
*
|
|
978
|
+
* const chain = InclusiveOr.flatMapRightEffect((right: string) =>
|
|
979
|
+
* Effect.succeed(InclusiveOr.RightOnly({ right: right.toUpperCase() }))
|
|
980
|
+
* )
|
|
981
|
+
*
|
|
982
|
+
* assert.deepStrictEqual(
|
|
983
|
+
* Effect.runSync(chain(InclusiveOr.RightOnly({ right: "a" }))),
|
|
984
|
+
* InclusiveOr.RightOnly({ right: "A" })
|
|
985
|
+
* )
|
|
986
|
+
* assert.deepStrictEqual(
|
|
987
|
+
* Effect.runSync(chain(InclusiveOr.LeftOnly({ left: 1 }))),
|
|
988
|
+
* InclusiveOr.LeftOnly({ left: 1 })
|
|
989
|
+
* )
|
|
990
|
+
* ```
|
|
991
|
+
*
|
|
992
|
+
* @category sequencing
|
|
993
|
+
* @since 0.0.0
|
|
994
|
+
*/
|
|
995
|
+
export const flatMapRightEffect = mapRight => match({
|
|
996
|
+
LeftOnly: ({
|
|
997
|
+
left
|
|
998
|
+
}) => Effect.succeed(LeftOnly({
|
|
999
|
+
left
|
|
1000
|
+
})),
|
|
1001
|
+
RightOnly: ({
|
|
1002
|
+
right
|
|
1003
|
+
}) => mapRight(right),
|
|
1004
|
+
LeftAndRight: ({
|
|
1005
|
+
right
|
|
1006
|
+
}) => mapRight(right)
|
|
1007
|
+
});
|
|
1008
|
+
/**
|
|
1009
|
+
* Zips two arrays into one, calling `f` with an `InclusiveOr` for each index so
|
|
1010
|
+
* that length mismatches are handled explicitly rather than truncated.
|
|
1011
|
+
*
|
|
1012
|
+
* Unlike `Array.zipWith` (which stops at the shorter array), this walks to the
|
|
1013
|
+
* length of the *longer* array. The first array's element fills the `left` side
|
|
1014
|
+
* and the second array's element fills the `right` side, so at each index `f`
|
|
1015
|
+
* receives an `InclusiveOr<A, B>`: `LeftAndRight` when both arrays have an
|
|
1016
|
+
* element, `LeftOnly` when only the first does, and `RightOnly` when only the
|
|
1017
|
+
* second does. Use it when the "extra" tail of either array still carries
|
|
1018
|
+
* meaning.
|
|
1019
|
+
*
|
|
1020
|
+
* @example
|
|
1021
|
+
* ```ts
|
|
1022
|
+
* import { InclusiveOr } from "@nunofyobiz/effect-extras"
|
|
1023
|
+
* import { pipe } from "effect"
|
|
1024
|
+
*
|
|
1025
|
+
* const describe = InclusiveOr.match({
|
|
1026
|
+
* LeftOnly: ({ left }) => `left ${left}`,
|
|
1027
|
+
* RightOnly: ({ right }) => `right ${right}`,
|
|
1028
|
+
* LeftAndRight: ({ left, right }) => `both ${left}/${right}`
|
|
1029
|
+
* })
|
|
1030
|
+
*
|
|
1031
|
+
* // data-first
|
|
1032
|
+
* assert.deepStrictEqual(InclusiveOr.zip([1, 2, 3], [10, 20], describe), [
|
|
1033
|
+
* "both 1/10",
|
|
1034
|
+
* "both 2/20",
|
|
1035
|
+
* "left 3"
|
|
1036
|
+
* ])
|
|
1037
|
+
*
|
|
1038
|
+
* // data-last (pipeable)
|
|
1039
|
+
* assert.deepStrictEqual(pipe([1, 2, 3], InclusiveOr.zip([10, 20], describe)), [
|
|
1040
|
+
* "both 1/10",
|
|
1041
|
+
* "both 2/20",
|
|
1042
|
+
* "left 3"
|
|
1043
|
+
* ])
|
|
1044
|
+
* ```
|
|
1045
|
+
*
|
|
1046
|
+
* @category combinators
|
|
1047
|
+
* @since 0.0.0
|
|
1048
|
+
*/
|
|
1049
|
+
export const zip = /*#__PURE__*/dual(3, (array1, array2, f) => {
|
|
1050
|
+
const newLength = Math.max(array1.length, array2.length);
|
|
1051
|
+
if (newLength === 0) {
|
|
1052
|
+
return [];
|
|
1053
|
+
}
|
|
1054
|
+
return Array.makeBy(newLength, index => {
|
|
1055
|
+
if (index < array1.length && index < array2.length) {
|
|
1056
|
+
return f(LeftAndRight({
|
|
1057
|
+
left: array1[index],
|
|
1058
|
+
right: array2[index]
|
|
1059
|
+
}));
|
|
1060
|
+
}
|
|
1061
|
+
if (index < array1.length) {
|
|
1062
|
+
return f(LeftOnly({
|
|
1063
|
+
left: array1[index]
|
|
1064
|
+
}));
|
|
1065
|
+
}
|
|
1066
|
+
if (index < array2.length) {
|
|
1067
|
+
return f(RightOnly({
|
|
1068
|
+
right: array2[index]
|
|
1069
|
+
}));
|
|
1070
|
+
}
|
|
1071
|
+
throw new Error(`Index ${index} is out of bounds for array1 and array2`);
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
1074
|
+
//# sourceMappingURL=InclusiveOr.js.map
|