functype 0.9.0 → 0.9.1
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/dist/{Either-C-PDWX2U.d.ts → Either-BHep7I0d.d.ts} +7 -2
- package/dist/{Serializable-D9GKEo30.d.ts → Serializable-BbKuhDDL.d.ts} +14 -3
- package/dist/branded/index.d.ts +4 -6
- package/dist/branded/index.mjs +1 -1
- package/dist/chunk-GHBOC52G.mjs +43 -0
- package/dist/chunk-GHBOC52G.mjs.map +1 -0
- package/dist/chunk-R2TQJN3P.mjs +2 -0
- package/dist/chunk-R2TQJN3P.mjs.map +1 -0
- package/dist/either/index.d.ts +2 -2
- package/dist/either/index.mjs +1 -1
- package/dist/fpromise/index.d.ts +2 -2
- package/dist/fpromise/index.mjs +1 -1
- package/dist/index.d.ts +28 -20
- package/dist/index.mjs +1 -1
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.mjs +1 -1
- package/dist/map/index.d.ts +2 -2
- package/dist/map/index.mjs +1 -1
- package/dist/option/index.d.ts +2 -2
- package/dist/option/index.mjs +1 -1
- package/dist/set/index.d.ts +2 -2
- package/dist/set/index.mjs +1 -1
- package/dist/try/index.d.ts +2 -2
- package/dist/try/index.mjs +1 -1
- package/dist/tuple/index.d.ts +1 -1
- package/package.json +3 -3
- package/readme/BUNDLE_OPTIMIZATION.md +74 -0
- package/readme/FPromise-Assessment.md +43 -0
- package/readme/HKT.md +110 -0
- package/readme/ROADMAP.md +113 -0
- package/readme/TASK-TODO.md +33 -0
- package/readme/TUPLE-EXAMPLES.md +76 -0
- package/readme/TaskMigration.md +129 -0
- package/readme/ai-guide.md +406 -0
- package/readme/examples.md +2093 -0
- package/readme/quick-reference.md +514 -0
- package/readme/task-error-handling.md +283 -0
- package/readme/tasks.md +203 -0
- package/readme/type-index.md +238 -0
- package/dist/chunk-4EYCKDDF.mjs +0 -43
- package/dist/chunk-4EYCKDDF.mjs.map +0 -1
- package/dist/chunk-V6LFV5LW.mjs +0 -2
- package/dist/chunk-V6LFV5LW.mjs.map +0 -1
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
# Functype Quick Reference
|
|
2
|
+
|
|
3
|
+
## Option<T>
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { Option, Some, None } from "functype/option"
|
|
7
|
+
|
|
8
|
+
// Creation
|
|
9
|
+
Option(42) // Some(42)
|
|
10
|
+
Option(null) // None
|
|
11
|
+
Option(undefined) // None
|
|
12
|
+
Some(42) // Some(42)
|
|
13
|
+
None() // None
|
|
14
|
+
Option.from(42) // Some(42)
|
|
15
|
+
Option.none() // None
|
|
16
|
+
|
|
17
|
+
// Checking
|
|
18
|
+
option.isDefined() // true if Some, false if None
|
|
19
|
+
option.isEmpty() // true if None, false if Some
|
|
20
|
+
|
|
21
|
+
// Accessing
|
|
22
|
+
option.get() // Gets value or throws if None
|
|
23
|
+
option.getOrElse(defaultValue) // Gets value or returns default
|
|
24
|
+
option.getOrElseThrow(new Error("No value")) // Gets value or throws custom error
|
|
25
|
+
|
|
26
|
+
// Transforming
|
|
27
|
+
option.map((x) => x * 2) // Transforms inner value
|
|
28
|
+
option.flatMap((x) => Option(x.toString())) // Chains with operations returning Option
|
|
29
|
+
option.filter((x) => x > 10) // None if predicate fails, unchanged if passes
|
|
30
|
+
|
|
31
|
+
// Pattern matching
|
|
32
|
+
option.fold(
|
|
33
|
+
() => "empty", // Called for None
|
|
34
|
+
(value) => `value: ${value}`, // Called for Some
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
option.match({
|
|
38
|
+
Some: (value) => `value: ${value}`,
|
|
39
|
+
None: () => "empty",
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Either<L, R>
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Either, Left, Right } from "functype/either"
|
|
47
|
+
|
|
48
|
+
// Creation
|
|
49
|
+
Right<string, number>(42) // Right<number>(42)
|
|
50
|
+
Left<string, number>("error") // Left<string>("error")
|
|
51
|
+
Either.right(42) // Right(42)
|
|
52
|
+
Either.left("error") // Left("error")
|
|
53
|
+
Either.fromNullable(value, "was null") // Left if null/undefined
|
|
54
|
+
|
|
55
|
+
// Checking
|
|
56
|
+
either.isRight() // true if Right, false if Left
|
|
57
|
+
either.isLeft() // true if Left, false if Right
|
|
58
|
+
|
|
59
|
+
// Accessing
|
|
60
|
+
either.get() // Gets Right value or throws if Left
|
|
61
|
+
either.getOrElse(defaultValue) // Gets Right value or returns default
|
|
62
|
+
either.getLeft() // Gets Left value or throws if Right
|
|
63
|
+
|
|
64
|
+
// Transforming
|
|
65
|
+
either.map((x) => x * 2) // Transforms Right value
|
|
66
|
+
either.mapLeft((e) => `Error: ${e}`) // Transforms Left value
|
|
67
|
+
either.flatMap((x) => Right(x.toString())) // Chains with operations returning Either
|
|
68
|
+
either.filter(
|
|
69
|
+
(x) => x > 10,
|
|
70
|
+
(x) => `${x} is too small`,
|
|
71
|
+
) // Left if predicate fails
|
|
72
|
+
|
|
73
|
+
// Operations
|
|
74
|
+
either.swap() // Converts Left to Right and vice versa
|
|
75
|
+
|
|
76
|
+
// Pattern matching
|
|
77
|
+
either.fold(
|
|
78
|
+
(left) => `Error: ${left}`,
|
|
79
|
+
(right) => `Success: ${right}`,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
either.match({
|
|
83
|
+
Right: (value) => `Success: ${value}`,
|
|
84
|
+
Left: (error) => `Error: ${error}`,
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Try<T>
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { Try, Success, Failure } from "functype/try"
|
|
92
|
+
|
|
93
|
+
// Creation
|
|
94
|
+
Try(() => 42) // Success(42)
|
|
95
|
+
Try(() => {
|
|
96
|
+
throw new Error("Failed")
|
|
97
|
+
}) // Failure(Error)
|
|
98
|
+
Success(42) // Success(42)
|
|
99
|
+
Failure(new Error("Failed")) // Failure(Error)
|
|
100
|
+
|
|
101
|
+
// Checking
|
|
102
|
+
tryVal.isSuccess() // true if Success, false if Failure
|
|
103
|
+
tryVal.isFailure() // true if Failure, false if Success
|
|
104
|
+
|
|
105
|
+
// Accessing
|
|
106
|
+
tryVal.get() // Gets value or throws original error
|
|
107
|
+
tryVal.getOrElse(defaultValue) // Gets value or returns default
|
|
108
|
+
tryVal.error // Gets error for Failure
|
|
109
|
+
|
|
110
|
+
// Error handling
|
|
111
|
+
tryVal.recover(defaultValue) // Success with original value or default
|
|
112
|
+
tryVal.recoverWith(() => Try(() => backup())) // Tries alternative computation on failure
|
|
113
|
+
|
|
114
|
+
// Transforming
|
|
115
|
+
tryVal.map((x) => x * 2) // Maps success value
|
|
116
|
+
tryVal.flatMap((x) => Try(() => operation(x))) // Chains with operations returning Try
|
|
117
|
+
|
|
118
|
+
// Conversions
|
|
119
|
+
tryVal.toOption() // Option.Some for Success, Option.None for Failure
|
|
120
|
+
tryVal.toEither() // Either.Right for Success, Either.Left for Failure
|
|
121
|
+
|
|
122
|
+
// Pattern matching
|
|
123
|
+
tryVal.fold(
|
|
124
|
+
(error) => `Error: ${error.message}`,
|
|
125
|
+
(value) => `Success: ${value}`,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
tryVal.match({
|
|
129
|
+
Success: (value) => `Success: ${value}`,
|
|
130
|
+
Failure: (error) => `Error: ${error.message}`,
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## List<T>
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { List } from "functype/list"
|
|
138
|
+
|
|
139
|
+
// Creation
|
|
140
|
+
List([1, 2, 3]) // List([1, 2, 3])
|
|
141
|
+
List.empty<number>() // List([])
|
|
142
|
+
|
|
143
|
+
// Properties
|
|
144
|
+
list.isEmpty() // true if empty, false otherwise
|
|
145
|
+
list.size() // Number of elements
|
|
146
|
+
|
|
147
|
+
// Accessing
|
|
148
|
+
list.head() // Option.Some with first element or None if empty
|
|
149
|
+
list.tail() // List with all elements except the first
|
|
150
|
+
list.at(2) // Option.Some with element at index or None
|
|
151
|
+
|
|
152
|
+
// Adding/removing
|
|
153
|
+
list.add(4) // New list with element added
|
|
154
|
+
list.addAll([4, 5, 6]) // New list with elements added
|
|
155
|
+
list.remove(2) // New list with element removed
|
|
156
|
+
list.removeAt(1) // New list with element at index removed
|
|
157
|
+
|
|
158
|
+
// Transforming
|
|
159
|
+
list.map((x) => x * 2) // List with transformed elements
|
|
160
|
+
list.flatMap((x) => List([x, x * 2])) // Transform + flatten
|
|
161
|
+
list.filter((x) => x % 2 === 0) // List with elements matching predicate
|
|
162
|
+
|
|
163
|
+
// Slicing
|
|
164
|
+
list.take(2) // First n elements
|
|
165
|
+
list.drop(2) // All elements after first n
|
|
166
|
+
list.slice(1, 3) // Elements from start to end (exclusive)
|
|
167
|
+
|
|
168
|
+
// Finding
|
|
169
|
+
list.find((x) => x > 10) // Option.Some with first match or None
|
|
170
|
+
list.exists((x) => x > 10) // true if any element matches predicate
|
|
171
|
+
list.forAll((x) => x > 0) // true if all elements match predicate
|
|
172
|
+
list.count((x) => x % 2 === 0) // Count of elements matching predicate
|
|
173
|
+
|
|
174
|
+
// Aggregating
|
|
175
|
+
list.foldLeft(0)((acc, x) => acc + x) // Reduce from left to right
|
|
176
|
+
list.foldRight(0)((x, acc) => x + acc) // Reduce from right to left
|
|
177
|
+
list.reduce((a, b) => a + b) // Reduce (without initial value)
|
|
178
|
+
|
|
179
|
+
// Operations
|
|
180
|
+
list.reverse() // Reversed list
|
|
181
|
+
list.sort((a, b) => a - b) // Sorted list
|
|
182
|
+
list.distinct() // List with duplicates removed
|
|
183
|
+
list.concat(List([4, 5, 6])) // Lists combined
|
|
184
|
+
list.groupBy((x) => (x % 2 === 0 ? "even" : "odd")) // Map of grouped elements
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Map<K, V>
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { Map } from "functype/map"
|
|
191
|
+
|
|
192
|
+
// Creation
|
|
193
|
+
Map({ a: 1, b: 2, c: 3 }) // Map({a: 1, b: 2, c: 3})
|
|
194
|
+
Map.empty<string, number>() // Empty map
|
|
195
|
+
|
|
196
|
+
// Properties
|
|
197
|
+
map.isEmpty() // true if empty
|
|
198
|
+
map.size() // Number of entries
|
|
199
|
+
map.has("a") // true if key exists
|
|
200
|
+
|
|
201
|
+
// Accessing
|
|
202
|
+
map.get("a") // Option.Some with value or None
|
|
203
|
+
map.getOrElse("a", 0) // Value or default
|
|
204
|
+
map.keys() // List of keys
|
|
205
|
+
map.values() // List of values
|
|
206
|
+
map.entries() // List of [key, value] tuples
|
|
207
|
+
|
|
208
|
+
// Modifying
|
|
209
|
+
map.add("d", 4) // New map with entry added
|
|
210
|
+
map.addAll({ d: 4, e: 5 }) // New map with entries added
|
|
211
|
+
map.remove("a") // New map with key removed
|
|
212
|
+
map.removeAll(["a", "b"]) // New map with keys removed
|
|
213
|
+
|
|
214
|
+
// Transforming
|
|
215
|
+
map.map((v) => v * 2) // Map with transformed values
|
|
216
|
+
map.mapEntries(([k, v]) => [`key_${k}`, v * 2]) // Transform both keys and values
|
|
217
|
+
map.filter((v) => v > 1) // Map with entries matching predicate
|
|
218
|
+
map.filterKeys((k) => k !== "a") // Map with entries having keys matching predicate
|
|
219
|
+
|
|
220
|
+
// Operations
|
|
221
|
+
map.merge(Map({ c: 30, d: 40 })) // Maps combined (right values override)
|
|
222
|
+
map.mergeWith(Map({ c: 30, d: 40 }), (v1, v2) => v1 + v2) // Maps combined with custom function
|
|
223
|
+
|
|
224
|
+
// Conversions
|
|
225
|
+
map.toObject() // Standard JS object
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Set<T>
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { Set } from "functype/set"
|
|
232
|
+
|
|
233
|
+
// Creation
|
|
234
|
+
Set([1, 2, 3]) // Set([1, 2, 3])
|
|
235
|
+
Set.empty<number>() // Empty set
|
|
236
|
+
|
|
237
|
+
// Properties
|
|
238
|
+
set.isEmpty() // true if empty
|
|
239
|
+
set.size() // Number of elements
|
|
240
|
+
set.has(2) // true if element exists
|
|
241
|
+
|
|
242
|
+
// Modifying
|
|
243
|
+
set.add(4) // New set with element added
|
|
244
|
+
set.addAll([4, 5]) // New set with elements added
|
|
245
|
+
set.remove(2) // New set with element removed
|
|
246
|
+
set.removeAll([1, 2]) // New set with elements removed
|
|
247
|
+
|
|
248
|
+
// Transforming
|
|
249
|
+
set.map((x) => x * 2) // Set with transformed elements
|
|
250
|
+
set.flatMap((x) => Set([x, x + 1])) // Transform + flatten
|
|
251
|
+
set.filter((x) => x % 2 === 0) // Set with elements matching predicate
|
|
252
|
+
|
|
253
|
+
// Set operations
|
|
254
|
+
set.union(Set([3, 4, 5])) // Union of sets
|
|
255
|
+
set.intersect(Set([2, 3, 4])) // Intersection of sets
|
|
256
|
+
set.difference(Set([3, 4])) // Elements in this set but not in other
|
|
257
|
+
set.symmetricDifference(Set([3, 4])) // Elements in either set but not both
|
|
258
|
+
set.isSubsetOf(Set([1, 2, 3, 4])) // true if all elements in other set
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## FPromise<T, E>
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { FPromise } from "functype/fpromise"
|
|
265
|
+
|
|
266
|
+
// Creation
|
|
267
|
+
FPromise.resolve(42) // Resolves with value
|
|
268
|
+
FPromise.reject(new Error()) // Rejects with error
|
|
269
|
+
FPromise.fromPromise(fetch("/api")) // From standard Promise
|
|
270
|
+
|
|
271
|
+
// From functions
|
|
272
|
+
FPromise.tryCatch(
|
|
273
|
+
() => JSON.parse(data),
|
|
274
|
+
(err) => new Error(`Parse error: ${err}`),
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
FPromise.tryCatchAsync(
|
|
278
|
+
async () => await fetch("/api").then((r) => r.json()),
|
|
279
|
+
(err) => new Error(`Fetch error: ${err}`),
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
// Transforming
|
|
283
|
+
promise.map((x) => x * 2) // Transform success value
|
|
284
|
+
promise.mapError((e) => new Error(`Enhanced: ${e}`)) // Transform error
|
|
285
|
+
promise.flatMap((x) => FPromise.resolve(x.toString())) // Chain with another FPromise
|
|
286
|
+
|
|
287
|
+
// Error handling
|
|
288
|
+
promise.recover(defaultValue) // Default if rejected
|
|
289
|
+
promise.recoverWith((err) => FPromise.resolve(`Recovered from ${err}`)) // Alternative promise on error
|
|
290
|
+
|
|
291
|
+
// Side effects
|
|
292
|
+
promise.tap((value) => console.log(value)) // Side effect on success
|
|
293
|
+
promise.tapError((err) => console.error(err)) // Side effect on error
|
|
294
|
+
|
|
295
|
+
// Combining
|
|
296
|
+
FPromise.all([p1, p2, p3]) // All promises succeed
|
|
297
|
+
FPromise.race([p1, p2]) // First to resolve
|
|
298
|
+
FPromise.any([p1, p2]) // First to resolve successfully
|
|
299
|
+
|
|
300
|
+
// Timeouts
|
|
301
|
+
FPromise.timeout(promise, 1000, () => new Error("Timed out")) // Rejects if promise takes too long
|
|
302
|
+
|
|
303
|
+
// Pattern matching
|
|
304
|
+
promise.fold(
|
|
305
|
+
(err) => `Error: ${err}`,
|
|
306
|
+
(val) => `Success: ${val}`,
|
|
307
|
+
) // Handle both outcomes
|
|
308
|
+
|
|
309
|
+
// Conversion
|
|
310
|
+
promise.toPromise() // Convert to standard Promise
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Task
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { Task } from "functype/core/task"
|
|
317
|
+
|
|
318
|
+
// Synchronous tasks
|
|
319
|
+
const syncTask = Task().Sync(
|
|
320
|
+
() => 42,
|
|
321
|
+
(err) => new Error(`Failed: ${err}`),
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
// Asynchronous tasks
|
|
325
|
+
const asyncTask = Task().Async(
|
|
326
|
+
async () => await fetch("/api").then((r) => r.json()),
|
|
327
|
+
async (err) => new Error(`Fetch failed: ${err}`),
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
// Named tasks (for debugging and error identification)
|
|
331
|
+
const namedTask = Task({ name: "UserFetch" }).Sync(
|
|
332
|
+
() => ({ id: 1, name: "John" }),
|
|
333
|
+
(err) => new Error(`User fetch failed: ${err}`),
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
// Error handling with named tasks
|
|
337
|
+
try {
|
|
338
|
+
await Task({ name: "DataProcessor" }).Async(() => {
|
|
339
|
+
throw new Error("Processing failed")
|
|
340
|
+
})
|
|
341
|
+
} catch (error) {
|
|
342
|
+
console.log(error.name) // "DataProcessor"
|
|
343
|
+
console.log(error.taskInfo.name) // "DataProcessor"
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Companion functions
|
|
347
|
+
Task.success(42, { name: "SuccessResult" }) // Creates TaskResult
|
|
348
|
+
Task.fail(new Error("Failed"), data, { name: "FailureResult" }) // Creates TaskException with name
|
|
349
|
+
|
|
350
|
+
// Adapting functions
|
|
351
|
+
const fetchAPI = (id: string): Promise<User> => fetch(`/api/users/${id}`).then((r) => r.json())
|
|
352
|
+
const getUser = Task.fromPromise(fetchAPI, { name: "UserFetch" })
|
|
353
|
+
|
|
354
|
+
// Converting
|
|
355
|
+
const promise = Task.toPromise(syncTask) // Standard Promise
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Tuple
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
import { Tuple } from "functype/tuple"
|
|
362
|
+
|
|
363
|
+
// Creation
|
|
364
|
+
const pair = Tuple(42, "hello")
|
|
365
|
+
const triple = Tuple(true, 42, "hello")
|
|
366
|
+
|
|
367
|
+
// Accessing
|
|
368
|
+
pair.first() // 42
|
|
369
|
+
pair.second() // "hello"
|
|
370
|
+
triple.third() // "hello"
|
|
371
|
+
pair.toArray() // [42, "hello"]
|
|
372
|
+
|
|
373
|
+
// Transforming
|
|
374
|
+
pair.mapFirst((x) => x * 2) // Tuple(84, "hello")
|
|
375
|
+
pair.mapSecond((s) => s.toUpperCase()) // Tuple(42, "HELLO")
|
|
376
|
+
pair.map(([a, b]) => [a * 2, b.toUpperCase()]) // Tuple(84, "HELLO")
|
|
377
|
+
|
|
378
|
+
// Operations
|
|
379
|
+
pair.swap() // Tuple("hello", 42)
|
|
380
|
+
pair.apply((a, b) => a + b.length) // 47
|
|
381
|
+
|
|
382
|
+
// Combining
|
|
383
|
+
pair.concat(Tuple(true)) // Tuple(42, "hello", true)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Branded Types
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
import { Brand } from "functype/branded"
|
|
390
|
+
|
|
391
|
+
// Type definitions
|
|
392
|
+
type UserId = Brand<string, "UserId">
|
|
393
|
+
type Email = Brand<string, "Email">
|
|
394
|
+
type PositiveInt = Brand<number, "PositiveInt">
|
|
395
|
+
|
|
396
|
+
// Factory functions with validation
|
|
397
|
+
const UserId = (id: string): UserId => {
|
|
398
|
+
if (!/^U\d{6}$/.test(id)) throw new Error("Invalid ID format")
|
|
399
|
+
return id as UserId
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const PositiveInt = (n: number): PositiveInt => {
|
|
403
|
+
if (!Number.isInteger(n) || n <= 0) throw new Error("Not a positive integer")
|
|
404
|
+
return n as PositiveInt
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Usage
|
|
408
|
+
function getUserById(id: UserId): User {
|
|
409
|
+
/* ... */
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Type-safe calls
|
|
413
|
+
getUserById(UserId("U123456")) // Works
|
|
414
|
+
// getUserById("U123456") // Type error: string is not UserId
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## Pattern Matching
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { Option, Either, Try, List, MatchableUtils } from "functype"
|
|
421
|
+
|
|
422
|
+
// Built-in pattern matching
|
|
423
|
+
option.match({
|
|
424
|
+
Some: (value) => `Found: ${value}`,
|
|
425
|
+
None: () => "Not found",
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
either.match({
|
|
429
|
+
Right: (value) => `Success: ${value}`,
|
|
430
|
+
Left: (error) => `Error: ${error}`,
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
list.match({
|
|
434
|
+
NonEmpty: (values) => `Values: ${values.join(", ")}`,
|
|
435
|
+
Empty: () => "No values",
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
// Custom pattern matching
|
|
439
|
+
const isPositive = MatchableUtils.when(
|
|
440
|
+
(n: number) => n > 0,
|
|
441
|
+
(n) => `Positive: ${n}`,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
const isZero = MatchableUtils.when(
|
|
445
|
+
(n: number) => n === 0,
|
|
446
|
+
() => "Zero",
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
const isNegative = MatchableUtils.when(
|
|
450
|
+
(n: number) => n < 0,
|
|
451
|
+
(n) => `Negative: ${n}`,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
const defaultCase = MatchableUtils.default((x: number) => `Default: ${x}`)
|
|
455
|
+
|
|
456
|
+
// Chain patterns with fallbacks
|
|
457
|
+
isPositive(42) ?? isZero(42) ?? isNegative(42) ?? defaultCase(42) // "Positive: 42"
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Common Conversions
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import { Option, Either, Try, List, FoldableUtils } from "functype"
|
|
464
|
+
|
|
465
|
+
// Convert between types
|
|
466
|
+
FoldableUtils.toList(Option(42)) // List([42])
|
|
467
|
+
FoldableUtils.toList(Either.right(42)) // List([42])
|
|
468
|
+
FoldableUtils.toList(Try(() => 42)) // List([42])
|
|
469
|
+
|
|
470
|
+
FoldableUtils.toOption(List([1, 2, 3])) // Some(1)
|
|
471
|
+
FoldableUtils.toOption(Either.right(42)) // Some(42)
|
|
472
|
+
FoldableUtils.toOption(Try(() => 42)) // Some(42)
|
|
473
|
+
|
|
474
|
+
FoldableUtils.toEither(Option(42), "Empty") // Right(42)
|
|
475
|
+
FoldableUtils.toEither(
|
|
476
|
+
Try(() => 42),
|
|
477
|
+
"Failed",
|
|
478
|
+
) // Right(42)
|
|
479
|
+
|
|
480
|
+
// Built-in conversions
|
|
481
|
+
option.toEither("No value") // Either.Right or Either.Left
|
|
482
|
+
either.toOption() // Option.Some or Option.None
|
|
483
|
+
tryVal.toEither() // Either.Right or Either.Left with error
|
|
484
|
+
tryVal.toOption() // Option.Some or Option.None
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Functional Composition
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
import { pipe } from "functype/pipe"
|
|
491
|
+
|
|
492
|
+
// Pipe operations for cleaner sequential transformations
|
|
493
|
+
const result = pipe(
|
|
494
|
+
Option("42"),
|
|
495
|
+
(opt) => opt.map((s) => s.trim()),
|
|
496
|
+
(opt) => opt.map((s) => parseInt(s, 10)),
|
|
497
|
+
(opt) => opt.filter((n) => !isNaN(n)),
|
|
498
|
+
(opt) => opt.map((n) => n * 2),
|
|
499
|
+
(opt) => opt.getOrElse(0),
|
|
500
|
+
) // 84
|
|
501
|
+
|
|
502
|
+
// Cross-type transformations
|
|
503
|
+
const mixed = pipe(
|
|
504
|
+
Option("42"),
|
|
505
|
+
(opt) => opt.map((s) => parseInt(s, 10)),
|
|
506
|
+
(opt) => opt.toEither("Invalid number"),
|
|
507
|
+
(e) => e.map((n) => n * 2),
|
|
508
|
+
(e) =>
|
|
509
|
+
e.fold(
|
|
510
|
+
(err) => `Error: ${err}`,
|
|
511
|
+
(val) => `Result: ${val}`,
|
|
512
|
+
),
|
|
513
|
+
) // "Result: 84"
|
|
514
|
+
```
|