functype 0.40.0 → 0.41.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/dist/Brand-BPeggBaO.d.ts +1 -2
- package/dist/Brand-Cfr5zy8F.js +1 -2
- package/dist/Tuple-C4maYbiO.d.ts +1 -2
- package/dist/Tuple-CgX4p79w.js +1 -2
- package/dist/cli/index.js +2 -3
- package/dist/do/index.d.ts +1 -1
- package/dist/do/index.js +1 -1
- package/dist/either/index.d.ts +1 -1
- package/dist/either/index.js +1 -1
- package/dist/fpromise/index.d.ts +1 -1
- package/dist/fpromise/index.js +1 -1
- package/dist/{index-Bn_yRBx8.d.ts → index-B6Civ4kr.d.ts} +19 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.js +1 -1
- package/dist/map/index.d.ts +1 -1
- package/dist/map/index.js +1 -1
- package/dist/option/index.d.ts +1 -1
- package/dist/option/index.js +1 -1
- package/dist/set/index.d.ts +1 -1
- package/dist/set/index.js +1 -1
- package/dist/{src-JcsaR9MX.js → src-DpfaJv6K.js} +2 -3
- package/dist/try/index.d.ts +1 -1
- package/dist/try/index.js +1 -1
- package/package.json +35 -38
- package/README.processed.md +0 -862
- package/dist/Brand-Cfr5zy8F.js.map +0 -1
- package/dist/Tuple-CgX4p79w.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/src-JcsaR9MX.js.map +0 -1
- package/readme/BRAND_MIGRATION_GUIDE.md +0 -230
- package/readme/BUNDLE_OPTIMIZATION.md +0 -74
- package/readme/FPromise-Assessment.md +0 -43
- package/readme/HKT.md +0 -110
- package/readme/ROADMAP.md +0 -113
- package/readme/TASK-TODO.md +0 -33
- package/readme/TUPLE-EXAMPLES.md +0 -76
- package/readme/TaskMigration.md +0 -129
- package/readme/ai-guide.md +0 -406
- package/readme/examples.md +0 -2093
- package/readme/functype-changes-required.md +0 -189
- package/readme/quick-reference.md +0 -514
- package/readme/task-error-handling.md +0 -283
- package/readme/tasks.md +0 -203
- package/readme/type-index.md +0 -238
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
# Required Changes to functype Library
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
Remove `PromiseLike<R>` from `Either<L, R>` interface and fix `FPromise.toEither()` return type to create a cleaner, more predictable functional programming API.
|
|
6
|
-
|
|
7
|
-
## Changes Required
|
|
8
|
-
|
|
9
|
-
### 1. Remove PromiseLike from Either Interface
|
|
10
|
-
|
|
11
|
-
**File**: `Either-i_F6B_IB.d.ts` (or equivalent source file)
|
|
12
|
-
**Line**: ~582
|
|
13
|
-
|
|
14
|
-
**Current**:
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
interface Either<L extends Type, R extends Type> extends FunctypeBase<R, "Left" | "Right">, PromiseLike<R> {
|
|
18
|
-
// ... rest of interface
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
**Change to**:
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
interface Either<L extends Type, R extends Type> extends FunctypeBase<R, "Left" | "Right"> {
|
|
26
|
-
// ... rest of interface (no changes to methods)
|
|
27
|
-
// Remove: , PromiseLike<R>
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### 2. Fix FPromise.toEither Return Type
|
|
32
|
-
|
|
33
|
-
**File**: `fpromise/index.d.ts` (or equivalent source file)
|
|
34
|
-
**Line**: ~205
|
|
35
|
-
|
|
36
|
-
**Current**:
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
type FPromise<T extends Type, E extends Type = unknown> = PromiseLike<T> & {
|
|
40
|
-
// ...
|
|
41
|
-
toEither: () => Promise<T> // ← This is wrong!
|
|
42
|
-
// ...
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Change to**:
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
type FPromise<T extends Type, E extends Type = unknown> = PromiseLike<T> & {
|
|
50
|
-
// ...
|
|
51
|
-
toEither: () => Promise<Either<E, T>> // ← Correct return type
|
|
52
|
-
// ...
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 3. Update Implementation (if needed)
|
|
57
|
-
|
|
58
|
-
If there's implementation code (not just type definitions), ensure:
|
|
59
|
-
|
|
60
|
-
1. **Either implementation**: Remove any Promise/thenable behavior
|
|
61
|
-
2. **FPromise.toEither implementation**: Actually return `Either<E, T>` wrapped in Promise
|
|
62
|
-
|
|
63
|
-
Example implementation for FPromise.toEither:
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
toEither: async (): Promise<Either<E, T>> => {
|
|
67
|
-
try {
|
|
68
|
-
const value = await this.toPromise()
|
|
69
|
-
return Right<E, T>(value)
|
|
70
|
-
} catch (error) {
|
|
71
|
-
return Left<E, T>(error as E)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Impact Analysis
|
|
77
|
-
|
|
78
|
-
### Breaking Changes
|
|
79
|
-
|
|
80
|
-
- ✅ **Expected**: Code using `await either` will break (this is desired)
|
|
81
|
-
- ✅ **Expected**: Code relying on Either auto-unwrapping will break
|
|
82
|
-
|
|
83
|
-
### Non-Breaking
|
|
84
|
-
|
|
85
|
-
- ✅ **Safe**: All existing Either methods (`isLeft`, `isRight`, `get`, `fold`, etc.) remain unchanged
|
|
86
|
-
- ✅ **Safe**: FPromise behavior remains the same (still implements PromiseLike)
|
|
87
|
-
- ✅ **Safe**: Option and List interfaces unchanged
|
|
88
|
-
|
|
89
|
-
## Validation
|
|
90
|
-
|
|
91
|
-
After making these changes, the following should be true:
|
|
92
|
-
|
|
93
|
-
### TypeScript Compilation
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
// This should compile (Either stays Either)
|
|
97
|
-
const either: Either<string, number> = Right(42)
|
|
98
|
-
const stillEither = either.map((x) => x * 2) // Either<string, number>
|
|
99
|
-
|
|
100
|
-
// This should NOT compile (no auto-unwrapping)
|
|
101
|
-
const result = await either // ← TypeScript error (good!)
|
|
102
|
-
|
|
103
|
-
// This should compile (explicit methods still work)
|
|
104
|
-
const value = either.get() // number
|
|
105
|
-
const folded = either.fold(
|
|
106
|
-
(err) => 0,
|
|
107
|
-
(val) => val,
|
|
108
|
-
) // number
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### FPromise Integration
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// This should work (FPromise still awaitable)
|
|
115
|
-
const fpromise: FPromise<string, Error> = FPromise.resolve("hello")
|
|
116
|
-
const result = await fpromise // string
|
|
117
|
-
|
|
118
|
-
// This should work (explicit Either extraction)
|
|
119
|
-
const either = await fpromise.toEither() // Either<Error, string>
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Version Impact
|
|
123
|
-
|
|
124
|
-
This is a **major breaking change** and should increment the major version number of functype:
|
|
125
|
-
|
|
126
|
-
- Current: `0.9.4` → Proposed: `1.0.0`
|
|
127
|
-
- Or if already 1.x: `1.x.y` → `2.0.0`
|
|
128
|
-
|
|
129
|
-
## Testing
|
|
130
|
-
|
|
131
|
-
Recommended tests to add/update:
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
describe("Either without PromiseLike", () => {
|
|
135
|
-
it("should not be awaitable", async () => {
|
|
136
|
-
const either = Right<string, number>(42)
|
|
137
|
-
|
|
138
|
-
// This should be a TypeScript error
|
|
139
|
-
// const result = await either
|
|
140
|
-
|
|
141
|
-
// This should work
|
|
142
|
-
const result = either.get()
|
|
143
|
-
expect(result).toBe(42)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it("should still support all Either methods", () => {
|
|
147
|
-
const either = Right<string, number>(42)
|
|
148
|
-
|
|
149
|
-
expect(either.isRight()).toBe(true)
|
|
150
|
-
expect(either.isLeft()).toBe(false)
|
|
151
|
-
expect(either.get()).toBe(42)
|
|
152
|
-
|
|
153
|
-
const mapped = either.map((x) => x * 2)
|
|
154
|
-
expect(mapped.get()).toBe(84)
|
|
155
|
-
|
|
156
|
-
const folded = either.fold(
|
|
157
|
-
(err) => 0,
|
|
158
|
-
(val) => val,
|
|
159
|
-
)
|
|
160
|
-
expect(folded).toBe(42)
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
describe("FPromise.toEither", () => {
|
|
165
|
-
it("should return Promise<Either<E, T>>", async () => {
|
|
166
|
-
const fpromise = FPromise.resolve(42)
|
|
167
|
-
const either = await fpromise.toEither()
|
|
168
|
-
|
|
169
|
-
expect(either.isRight()).toBe(true)
|
|
170
|
-
expect(either.get()).toBe(42)
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it("should handle errors properly", async () => {
|
|
174
|
-
const fpromise = FPromise.reject<number, string>("error")
|
|
175
|
-
const either = await fpromise.toEither()
|
|
176
|
-
|
|
177
|
-
expect(either.isLeft()).toBe(true)
|
|
178
|
-
expect(either.get()).toBe("error")
|
|
179
|
-
})
|
|
180
|
-
})
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## Implementation Priority
|
|
184
|
-
|
|
185
|
-
1. **High Priority**: Remove PromiseLike from Either (core change)
|
|
186
|
-
2. **Medium Priority**: Fix FPromise.toEither return type (nice to have)
|
|
187
|
-
3. **Low Priority**: Add additional convenience methods if desired
|
|
188
|
-
|
|
189
|
-
The first change is the most critical for resolving the API confusion issues.
|
|
@@ -1,514 +0,0 @@
|
|
|
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.orElse(defaultValue) // Gets value or returns default
|
|
24
|
-
option.orThrow(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.orElse(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.orElse(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.orElse("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.orElse(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
|
-
```
|