functype 0.8.70 → 0.8.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +177 -3
  2. package/package.json +27 -8
  3. package/readme/TUPLE-EXAMPLES.md +0 -3
  4. package/readme/task-error-handling.md +0 -1
  5. package/dist/Either-DlOLCe3b.d.ts +0 -533
  6. package/dist/Map-R4vpSnzG.d.ts +0 -65
  7. package/dist/Valuable-D4BtNlTV.d.ts +0 -59
  8. package/dist/branded/index.d.ts +0 -48
  9. package/dist/branded/index.mjs +0 -2
  10. package/dist/branded/index.mjs.map +0 -1
  11. package/dist/chunk-5ABR3A24.mjs +0 -49
  12. package/dist/chunk-5ABR3A24.mjs.map +0 -1
  13. package/dist/chunk-7PQA3W7W.mjs +0 -2
  14. package/dist/chunk-7PQA3W7W.mjs.map +0 -1
  15. package/dist/chunk-TQJDL6YW.mjs +0 -2
  16. package/dist/chunk-TQJDL6YW.mjs.map +0 -1
  17. package/dist/either/index.d.ts +0 -2
  18. package/dist/either/index.mjs +0 -2
  19. package/dist/either/index.mjs.map +0 -1
  20. package/dist/fpromise/index.d.ts +0 -373
  21. package/dist/fpromise/index.mjs +0 -2
  22. package/dist/fpromise/index.mjs.map +0 -1
  23. package/dist/index.d.ts +0 -1247
  24. package/dist/index.mjs +0 -2
  25. package/dist/index.mjs.map +0 -1
  26. package/dist/list/index.d.ts +0 -2
  27. package/dist/list/index.mjs +0 -2
  28. package/dist/list/index.mjs.map +0 -1
  29. package/dist/map/index.d.ts +0 -4
  30. package/dist/map/index.mjs +0 -2
  31. package/dist/map/index.mjs.map +0 -1
  32. package/dist/option/index.d.ts +0 -2
  33. package/dist/option/index.mjs +0 -2
  34. package/dist/option/index.mjs.map +0 -1
  35. package/dist/set/index.d.ts +0 -2
  36. package/dist/set/index.mjs +0 -2
  37. package/dist/set/index.mjs.map +0 -1
  38. package/dist/try/index.d.ts +0 -59
  39. package/dist/try/index.mjs +0 -2
  40. package/dist/try/index.mjs.map +0 -1
  41. package/dist/tuple/index.d.ts +0 -12
  42. package/dist/tuple/index.mjs +0 -2
  43. package/dist/tuple/index.mjs.map +0 -1
package/README.md CHANGED
@@ -15,6 +15,7 @@ Functype is a lightweight functional programming library for TypeScript, drawing
15
15
  - **Type Safety**: Leverages TypeScript's type system to ensure compile-time safety
16
16
  - **Composability**: Provides abstractions for building complex programs from simple components
17
17
  - **Functional Paradigms**: Embraces concepts like monads, functors, and type classes
18
+ - **Unified Interface**: All data structures implement a common hierarchy of interfaces for consistency
18
19
 
19
20
  ## Key Features
20
21
 
@@ -23,11 +24,13 @@ Functype is a lightweight functional programming library for TypeScript, drawing
23
24
  - **List, Set, Map**: Immutable collection types with functional operators
24
25
  - **Try Type**: Safely execute operations that might throw exceptions
25
26
  - **Task**: Handle synchronous and asynchronous operations with error handling
27
+ - **Lazy**: Deferred computation with memoization
26
28
  - **Tuple**: Type-safe fixed-length arrays
27
29
  - **Typeable**: Runtime type identification with compile-time safety
28
30
  - **Branded Types**: Nominal typing in TypeScript's structural type system
29
31
  - **FPromise**: Enhanced Promise functionality with built-in error handling
30
32
  - **Error Formatting**: Utilities for improved error visualization and logging
33
+ - **Unified Type Classes**: Consistent interfaces across all data structures
31
34
 
32
35
  ## Installation
33
36
 
@@ -128,6 +131,11 @@ const sum = numbers.foldLeft(0)((acc, x) => acc + x) // 10
128
131
  // Add/remove elements (immutably)
129
132
  const withFive = numbers.add(5) // List([1, 2, 3, 4, 5])
130
133
  const without3 = numbers.remove(3) // List([1, 2, 4])
134
+
135
+ // Universal container operations
136
+ const hasEven = numbers.exists((x) => x % 2 === 0) // true
137
+ const firstEven = numbers.find((x) => x % 2 === 0) // Some(2)
138
+ const evenCount = numbers.count((x) => x % 2 === 0) // 2
131
139
  ```
132
140
 
133
141
  ### Try
@@ -155,6 +163,32 @@ const name = result.map((obj) => obj.name)
155
163
  const either = result.toEither()
156
164
  ```
157
165
 
166
+ ### Lazy
167
+
168
+ ```typescript
169
+ import { Lazy } from "functype"
170
+
171
+ // Create lazy computations
172
+ const expensive = Lazy(() => {
173
+ console.log("Computing...")
174
+ return Math.random() * 1000
175
+ })
176
+
177
+ // Value is computed on first access and memoized
178
+ const value1 = expensive.get() // Logs "Computing...", returns number
179
+ const value2 = expensive.get() // Returns same number, no log
180
+
181
+ // Transform lazy values
182
+ const doubled = expensive.map((x) => x * 2)
183
+ const formatted = doubled.map((x) => `Value: ${x}`)
184
+
185
+ // Chain computations
186
+ const result = Lazy(() => 10)
187
+ .flatMap((x) => Lazy(() => x + 5))
188
+ .map((x) => x * 2)
189
+ .get() // 30
190
+ ```
191
+
158
192
  ### Task
159
193
 
160
194
  ```typescript
@@ -230,9 +264,76 @@ getUserByEmail("invalid") // Type error: Argument of type 'string' is not assign
230
264
  getUserByEmail(userId) // Type error: Argument of type 'UserId' is not assignable to parameter of type 'Email'
231
265
  ```
232
266
 
267
+ ## Conditional Programming
268
+
269
+ Functype provides `Cond` and `Match` for functional conditional logic without early returns:
270
+
271
+ ### Cond
272
+
273
+ ```typescript
274
+ import { Cond } from "functype"
275
+
276
+ // Replace if-else chains with Cond
277
+ const grade = Cond<number, string>()
278
+ .case((score) => score >= 90, "A")
279
+ .case((score) => score >= 80, "B")
280
+ .case((score) => score >= 70, "C")
281
+ .case((score) => score >= 60, "D")
282
+ .default("F")
283
+
284
+ console.log(grade(85)) // "B"
285
+ console.log(grade(55)) // "F"
286
+
287
+ // With transformation
288
+ const discount = Cond<number, number>()
289
+ .case(
290
+ (qty) => qty >= 100,
291
+ (qty) => qty * 0.2, // 20% off for 100+
292
+ )
293
+ .case(
294
+ (qty) => qty >= 50,
295
+ (qty) => qty * 0.1, // 10% off for 50+
296
+ )
297
+ .case(
298
+ (qty) => qty >= 10,
299
+ (qty) => qty * 0.05, // 5% off for 10+
300
+ )
301
+ .default(0)
302
+
303
+ console.log(discount(150)) // 30 (20% of 150)
304
+ ```
305
+
306
+ ### Match
307
+
308
+ ```typescript
309
+ import { Match } from "functype"
310
+
311
+ // Pattern matching with Match
312
+ type Status = "pending" | "approved" | "rejected" | "cancelled"
313
+
314
+ const statusMessage = Match<Status, string>()
315
+ .case("pending", "Your request is being processed")
316
+ .case("approved", "Your request has been approved!")
317
+ .case("rejected", "Sorry, your request was rejected")
318
+ .case("cancelled", "Your request was cancelled")
319
+ .exhaustive()
320
+
321
+ console.log(statusMessage("approved")) // "Your request has been approved!"
322
+
323
+ // Match with predicates
324
+ const numberType = Match<number, string>()
325
+ .case(0, "zero")
326
+ .case((n) => n > 0, "positive")
327
+ .case((n) => n < 0, "negative")
328
+ .exhaustive()
329
+
330
+ console.log(numberType(42)) // "positive"
331
+ console.log(numberType(-5)) // "negative"
332
+ ```
333
+
233
334
  ## Fold
234
335
 
235
- New in v0.8.66, Functype now includes a powerful `fold` operation for its monadic structures:
336
+ Functype includes a powerful `fold` operation for pattern matching and extracting values:
236
337
 
237
338
  ```typescript
238
339
  import { Option, Either, Try, List } from "functype"
@@ -265,7 +366,7 @@ const listResult = list.foldLeft(0)((acc, num) => acc + num) // 6
265
366
 
266
367
  ## Foldable
267
368
 
268
- New in v0.8.67, Functype includes a proper `Foldable` type class that data structures can implement:
369
+ Functype includes a `Foldable` type class that all data structures implement:
269
370
 
270
371
  ```typescript
271
372
  import { FoldableUtils, Option, List, Try } from "functype"
@@ -296,7 +397,7 @@ const convertedToEither = FoldableUtils.toEither(tryVal, "Error") // Right(10)
296
397
 
297
398
  ## Matchable
298
399
 
299
- New in v0.8.68, Functype now includes a `Matchable` type class for enhanced pattern matching:
400
+ Functype includes a `Matchable` type class for enhanced pattern matching:
300
401
 
301
402
  ```typescript
302
403
  import { Option, Either, Try, List, MatchableUtils } from "functype"
@@ -342,6 +443,79 @@ const num = 42
342
443
  const result = isPositive(num) ?? defaultCase(num) // "Positive: 42"
343
444
  ```
344
445
 
446
+ ## Interface Hierarchy
447
+
448
+ All data structures in Functype implement a unified hierarchy of interfaces, providing consistent behavior across the library:
449
+
450
+ ### Type Classes
451
+
452
+ Functype leverages type classes to provide common operations:
453
+
454
+ - **Functor**: Supports `map` operation for transforming wrapped values
455
+ - **Applicative**: Extends Functor with `ap` for applying wrapped functions
456
+ - **Monad**: Extends Applicative with `flatMap` for chaining operations
457
+ - **AsyncMonad**: Extends Monad with `flatMapAsync` for async operations
458
+ - **ContainerOps**: Universal operations for all containers (single-value and collections)
459
+ - **CollectionOps**: Operations specific to collections like List and Set
460
+
461
+ ### Unified Interfaces
462
+
463
+ All data structures implement the `Functype` hierarchy:
464
+
465
+ ```typescript
466
+ // Base interface for all data structures
467
+ interface FunctypeBase<A, Tag>
468
+ extends AsyncMonad<A>,
469
+ Traversable<A>,
470
+ Serializable<A>,
471
+ Foldable<A>,
472
+ Typeable<Tag>,
473
+ ContainerOps<A> {
474
+ readonly _tag: Tag
475
+ }
476
+
477
+ // For single-value containers (Option, Either, Try)
478
+ interface Functype<A, Tag> extends FunctypeBase<A, Tag>, Extractable<A>, Pipe<A>, Matchable<A, Tag> {
479
+ toValue(): { _tag: Tag; value: A }
480
+ }
481
+
482
+ // For collections (List, Set, Map)
483
+ interface FunctypeCollection<A, Tag>
484
+ extends FunctypeBase<A, Tag>,
485
+ Iterable<A>,
486
+ Pipe<A[]>,
487
+ Collection<A>,
488
+ CollectionOps<A, FunctypeCollection<A, Tag>> {
489
+ toValue(): { _tag: Tag; value: A[] }
490
+ // Collections work with Iterable instead of Monad
491
+ flatMap<B>(f: (value: A) => Iterable<B>): FunctypeCollection<B, Tag>
492
+ }
493
+ ```
494
+
495
+ ### Container Operations
496
+
497
+ All containers (Option, Either, Try, List, Set) support these universal operations:
498
+
499
+ ```typescript
500
+ import { Option, List } from "functype"
501
+
502
+ const opt = Option(42)
503
+ const list = List([1, 2, 3, 4, 5])
504
+
505
+ // Universal operations work on both single-value and collections
506
+ opt.count((x) => x > 40) // 1
507
+ list.count((x) => x > 3) // 2
508
+
509
+ opt.find((x) => x > 40) // Some(42)
510
+ list.find((x) => x > 3) // Some(4)
511
+
512
+ opt.exists((x) => x === 42) // true
513
+ list.exists((x) => x === 3) // true
514
+
515
+ opt.forEach(console.log) // Logs: 42
516
+ list.forEach(console.log) // Logs: 1, 2, 3, 4, 5
517
+ ```
518
+
345
519
  ## Type Safety
346
520
 
347
521
  Functype leverages TypeScript's advanced type system to provide compile-time safety for functional patterns, ensuring that your code is both robust and maintainable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functype",
3
- "version": "0.8.70",
3
+ "version": "0.8.81",
4
4
  "type": "module",
5
5
  "description": "A smallish functional library for TypeScript",
6
6
  "author": "jordan.burke@gmail.com",
@@ -12,12 +12,12 @@
12
12
  "homepage": "https://github.com/jordanburke/functype#readme",
13
13
  "url": "https://github.com/jordanburke/functype",
14
14
  "devDependencies": {
15
- "@eslint/compat": "^1.3.0",
15
+ "@eslint/compat": "^1.3.1",
16
16
  "@eslint/eslintrc": "^3.3.1",
17
17
  "@eslint/js": "^9.29.0",
18
- "@types/node": "^22.15.32",
19
- "@typescript-eslint/eslint-plugin": "^8.34.1",
20
- "@typescript-eslint/parser": "^8.34.1",
18
+ "@types/node": "^22.15.33",
19
+ "@typescript-eslint/eslint-plugin": "^8.35.0",
20
+ "@typescript-eslint/parser": "^8.35.0",
21
21
  "@vitest/coverage-v8": "^3.2.4",
22
22
  "@vitest/ui": "^3.2.4",
23
23
  "cross-env": "^7.0.3",
@@ -25,11 +25,11 @@
25
25
  "eslint-config-prettier": "^10.1.5",
26
26
  "eslint-plugin-functional": "^9.0.2",
27
27
  "eslint-plugin-import": "^2.32.0",
28
- "eslint-plugin-prettier": "^5.5.0",
28
+ "eslint-plugin-prettier": "^5.5.1",
29
29
  "eslint-plugin-simple-import-sort": "^12.1.1",
30
30
  "fast-check": "^4.1.1",
31
31
  "globals": "^16.2.0",
32
- "prettier": "^3.5.3",
32
+ "prettier": "^3.6.1",
33
33
  "rimraf": "^6.0.1",
34
34
  "ts-node": "^10.9.2",
35
35
  "tsup": "^8.5.0",
@@ -76,6 +76,12 @@
76
76
  "default": "./dist/conditional/index.mjs"
77
77
  }
78
78
  },
79
+ "./lazy": {
80
+ "import": {
81
+ "types": "./dist/lazy/index.d.ts",
82
+ "default": "./dist/lazy/index.mjs"
83
+ }
84
+ },
79
85
  "./task": {
80
86
  "import": {
81
87
  "types": "./dist/core/task/index.d.ts",
@@ -88,6 +94,18 @@
88
94
  "default": "./dist/fpromise/index.mjs"
89
95
  }
90
96
  },
97
+ "./functype": {
98
+ "import": {
99
+ "types": "./dist/functype/index.d.ts",
100
+ "default": "./dist/functype/index.mjs"
101
+ }
102
+ },
103
+ "./typeclass": {
104
+ "import": {
105
+ "types": "./dist/typeclass/index.d.ts",
106
+ "default": "./dist/typeclass/index.mjs"
107
+ }
108
+ },
91
109
  "./map": {
92
110
  "import": {
93
111
  "types": "./dist/map/index.d.ts",
@@ -121,10 +139,11 @@
121
139
  },
122
140
  "sideEffects": false,
123
141
  "scripts": {
142
+ "clean": "rimraf dist",
124
143
  "compile": "tsc --noEmit",
125
144
  "build": "pnpm compile && pnpm lint && pnpm test",
126
145
  "build:dev": "tsup --watch",
127
- "build:prod": "pnpm compile && pnpm test && cross-env NODE_ENV=production tsup",
146
+ "build:prod": "pnpm clean && pnpm compile && pnpm test && cross-env NODE_ENV=production tsup",
128
147
  "build:watch": "tsup --watch",
129
148
  "build:publish": "pnpm build:prod && pnpm publish --access public",
130
149
  "lint:fix": "eslint ./src --quiet --fix",
@@ -63,16 +63,13 @@ type First = FirstElement<typeof literalTuple> // Would be 1
63
63
  ## When Is This Useful?
64
64
 
65
65
  1. **Strong typing for heterogeneous collections**:
66
-
67
66
  - When you need to store different types in a fixed structure
68
67
  - When you want type safety beyond what arrays provide
69
68
 
70
69
  2. **APIs returning fixed-length, mixed-type results**:
71
-
72
70
  - When a function returns multiple values of different types
73
71
 
74
72
  3. **Data transformation pipelines**:
75
-
76
73
  - When you need to transform data while preserving type information
77
74
 
78
75
  4. **Configuration objects with fixed format**:
@@ -16,7 +16,6 @@ const namedTask = Task({
16
16
  When an error occurs within this task, the task name will be referenced in the error:
17
17
 
18
18
  1. The `Throwable` object created from the error will include:
19
-
20
19
  - The task name as the error's `name` property
21
20
  - A `taskInfo` property containing both the name and description
22
21