functype 0.8.83 → 0.8.85
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-CncND8cm.d.ts → Either-BlY4VB1r.d.ts} +3 -3
- package/dist/{chunk-JMCOLAJY.mjs → chunk-JAPOUVPL.mjs} +2 -2
- package/dist/chunk-JAPOUVPL.mjs.map +1 -0
- package/dist/either/index.d.ts +1 -1
- package/dist/either/index.mjs +1 -1
- package/dist/fpromise/index.d.ts +1 -1
- package/dist/fpromise/index.mjs +1 -1
- package/dist/index.d.ts +3 -28
- package/dist/index.mjs +1 -1
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.mjs +1 -1
- package/dist/map/index.d.ts +1 -1
- package/dist/map/index.mjs +1 -1
- package/dist/option/index.d.ts +1 -1
- package/dist/option/index.mjs +1 -1
- package/dist/set/index.d.ts +1 -1
- package/dist/set/index.mjs +1 -1
- package/dist/try/index.d.ts +1 -1
- package/dist/try/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-JMCOLAJY.mjs.map +0 -1
- 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-IMPLEMENTATION.md +0 -290
- package/readme/TASK-TODO.md +0 -40
- package/readme/TASK-UPDATES.md +0 -64
- 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/quick-reference.md +0 -514
- package/readme/task-cancellation-progress.md +0 -258
- package/readme/task-error-handling.md +0 -127
- package/readme/task-quick-reference.md +0 -157
- package/readme/tasks.md +0 -205
- package/readme/type-index.md +0 -238
package/readme/TASK-UPDATES.md
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Task Module Updates Summary
|
|
2
|
-
|
|
3
|
-
## Overview of Enhancements
|
|
4
|
-
|
|
5
|
-
We've made the following significant enhancements to the Task module:
|
|
6
|
-
|
|
7
|
-
1. **Implemented Task.race** - For racing multiple tasks with timeout support
|
|
8
|
-
2. **Added Task.fromNodeCallback** - For Node.js-style callback integration
|
|
9
|
-
3. **Implemented Cancellation Support** - For stopping long-running operations
|
|
10
|
-
4. **Added Progress Tracking** - For monitoring operation completion status
|
|
11
|
-
5. **Comprehensive Tests** - Including property-based tests and edge case handling
|
|
12
|
-
|
|
13
|
-
## Documentation Updates
|
|
14
|
-
|
|
15
|
-
The following new documentation has been created:
|
|
16
|
-
|
|
17
|
-
1. **[TASK-IMPLEMENTATION.md](../docs/TASK-IMPLEMENTATION.md)** - Deep analysis of Task design considerations and trade-offs
|
|
18
|
-
2. **[task-cancellation-progress.md](task-cancellation-progress.md)** - Guide to using cancellation and progress features
|
|
19
|
-
3. **[task-quick-reference.md](task-quick-reference.md)** - Compact reference for all Task functionality
|
|
20
|
-
|
|
21
|
-
## Current Status
|
|
22
|
-
|
|
23
|
-
The Task implementation now:
|
|
24
|
-
|
|
25
|
-
- Provides a robust bridge between functional patterns and async operations
|
|
26
|
-
- Seamlessly integrates with existing Promise-based and Node.js callback APIs
|
|
27
|
-
- Supports cancellation with proper resource cleanup
|
|
28
|
-
- Tracks progress for long-running operations
|
|
29
|
-
- Maintains rich error context throughout the operation lifecycle
|
|
30
|
-
- Follows functional programming principles
|
|
31
|
-
|
|
32
|
-
## Recommendations for Integration
|
|
33
|
-
|
|
34
|
-
### Documentation Recommendations
|
|
35
|
-
|
|
36
|
-
1. **Update quick-reference.md**: Add Task section based on task-quick-reference.md
|
|
37
|
-
2. **Update TASK-TODO.md**: Mark newly implemented items as completed
|
|
38
|
-
3. **Update tasks.md**: Mark cancellation and Node.js callback items as completed
|
|
39
|
-
|
|
40
|
-
### Implementation Recommendations
|
|
41
|
-
|
|
42
|
-
1. **Error Context Preservation**: Consider an option to preserve innermost task context
|
|
43
|
-
2. **Timeout Support**: Add global timeout support for any task operation
|
|
44
|
-
3. **Structured Resource Management**: Implement a `Task.using` pattern for guaranteed cleanup
|
|
45
|
-
4. **Lightweight Mode**: Consider a performance-optimized Task variant for hot paths
|
|
46
|
-
|
|
47
|
-
## Next Steps
|
|
48
|
-
|
|
49
|
-
Based on the existing roadmap and our implementation, these items could be prioritized:
|
|
50
|
-
|
|
51
|
-
1. **Error Aggregation for Parallel Operations**: For collecting multiple errors when running tasks in parallel
|
|
52
|
-
2. **Task Scope API**: For structured concurrency patterns with linked task lifecycles
|
|
53
|
-
3. **Framework Integration**: Create React/Vue hooks for Task management
|
|
54
|
-
4. **Performance Monitoring**: Add performance tracking capabilities to tasks
|
|
55
|
-
|
|
56
|
-
## Testing Observations
|
|
57
|
-
|
|
58
|
-
During the implementation and testing process, we discovered:
|
|
59
|
-
|
|
60
|
-
1. **Task Nesting Behavior**: Outer task context overwrites inner task context in errors
|
|
61
|
-
2. **Cancellation Edge Cases**: Some race conditions exist in rapidly cancelled tasks
|
|
62
|
-
3. **Testing Challenges**: Timing-dependent tests require careful structuring
|
|
63
|
-
|
|
64
|
-
Overall, the Task implementation is now significantly more robust and feature-complete, providing a strong foundation for asynchronous functional programming in TypeScript.
|
package/readme/TUPLE-EXAMPLES.md
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# Tuple Enhanced with Modern TypeScript Features
|
|
2
|
-
|
|
3
|
-
## What's Improved
|
|
4
|
-
|
|
5
|
-
Our enhanced `Tuple` implementation leverages modern TypeScript features:
|
|
6
|
-
|
|
7
|
-
1. **Const type parameters** (TypeScript 5.0+)
|
|
8
|
-
2. **Variadic tuple types** (TypeScript 4.0+)
|
|
9
|
-
3. **Stronger type inference** for tuple elements
|
|
10
|
-
|
|
11
|
-
## Examples
|
|
12
|
-
|
|
13
|
-
### Basic Usage
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { Tuple } from "@/tuple"
|
|
17
|
-
|
|
18
|
-
// Create a tuple with mixed types
|
|
19
|
-
const personTuple = Tuple(["John Doe", 42, true])
|
|
20
|
-
|
|
21
|
-
// Access values with type safety
|
|
22
|
-
const name = personTuple.get(0) // Type is string
|
|
23
|
-
const age = personTuple.get(1) // Type is number
|
|
24
|
-
const active = personTuple.get(2) // Type is boolean
|
|
25
|
-
|
|
26
|
-
// Transform the tuple
|
|
27
|
-
const mapped = personTuple.map((values) => values.map((x) => (typeof x === "number" ? x * 2 : x)))
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Preserving Literal Types
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
// Using 'as const' with the enhanced implementation
|
|
34
|
-
const literalTuple = Tuple([1, "hello", true] as const)
|
|
35
|
-
|
|
36
|
-
// TypeScript now knows the exact types:
|
|
37
|
-
const first = literalTuple.get(0) // Type is exactly 1 (not just number)
|
|
38
|
-
const second = literalTuple.get(1) // Type is exactly 'hello' (not just string)
|
|
39
|
-
const third = literalTuple.get(2) // Type is exactly true (not just boolean)
|
|
40
|
-
|
|
41
|
-
// Benefits:
|
|
42
|
-
// - Better type checking
|
|
43
|
-
// - Autocomplete shows exact values
|
|
44
|
-
// - Prevents invalid index access
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Type-Level Utilities (for future improvements)
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
// Example of potential future type utilities
|
|
51
|
-
type FirstElement<T extends Tuple<unknown[]>> = T extends Tuple<[infer F, ...unknown[]]> ? F : never
|
|
52
|
-
|
|
53
|
-
// Extract first element's type
|
|
54
|
-
type First = FirstElement<typeof literalTuple> // Would be 1
|
|
55
|
-
|
|
56
|
-
// Could expand to other utilities like:
|
|
57
|
-
// - LastElement
|
|
58
|
-
// - RemoveFirst
|
|
59
|
-
// - Prepend<T, Item>
|
|
60
|
-
// - etc.
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## When Is This Useful?
|
|
64
|
-
|
|
65
|
-
1. **Strong typing for heterogeneous collections**:
|
|
66
|
-
- When you need to store different types in a fixed structure
|
|
67
|
-
- When you want type safety beyond what arrays provide
|
|
68
|
-
|
|
69
|
-
2. **APIs returning fixed-length, mixed-type results**:
|
|
70
|
-
- When a function returns multiple values of different types
|
|
71
|
-
|
|
72
|
-
3. **Data transformation pipelines**:
|
|
73
|
-
- When you need to transform data while preserving type information
|
|
74
|
-
|
|
75
|
-
4. **Configuration objects with fixed format**:
|
|
76
|
-
- When config must follow a specific format with specific types at each position
|
package/readme/TaskMigration.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# Task Migration Guide
|
|
2
|
-
|
|
3
|
-
This guide demonstrates how to migrate traditional Promise-based code to the functional `Task` pattern in the functype library.
|
|
4
|
-
|
|
5
|
-
## The Problem with Traditional Promises
|
|
6
|
-
|
|
7
|
-
Traditional JavaScript/TypeScript Promise-based code often suffers from:
|
|
8
|
-
|
|
9
|
-
1. Implicit error handling (errors silently propagate)
|
|
10
|
-
2. Hard-to-trace error stacks
|
|
11
|
-
3. Mixed error types without type safety
|
|
12
|
-
4. Verbose try/catch blocks
|
|
13
|
-
5. Side effects spread throughout the codebase
|
|
14
|
-
|
|
15
|
-
## Migration Path: Traditional Promises → Task
|
|
16
|
-
|
|
17
|
-
### Step 1: Identify Promise-Based Code
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
// Traditional promise-based API call
|
|
21
|
-
function fetchUserData(userId: string): Promise<UserData> {
|
|
22
|
-
return fetch(`/api/users/${userId}`).then((response) => {
|
|
23
|
-
if (!response.ok) {
|
|
24
|
-
throw new Error(`Failed to fetch user: ${response.statusText}`)
|
|
25
|
-
}
|
|
26
|
-
return response.json()
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Usage with promise chains
|
|
31
|
-
function displayUserProfile(userId: string): Promise<void> {
|
|
32
|
-
return fetchUserData(userId)
|
|
33
|
-
.then((userData) => {
|
|
34
|
-
renderProfile(userData)
|
|
35
|
-
})
|
|
36
|
-
.catch((error) => {
|
|
37
|
-
showErrorMessage(error)
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Step 2: Convert to Task with fromPromise adapter
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
import { Task } from "@/core/task/Task"
|
|
46
|
-
|
|
47
|
-
// Step 1: Create a Task wrapper
|
|
48
|
-
const userTask = Task<UserData>({ name: "UserOperations" })
|
|
49
|
-
|
|
50
|
-
// Step 2: Convert promise function to Task function
|
|
51
|
-
const fetchUserData = (userId: string): FPromise<UserData> => {
|
|
52
|
-
return userTask.fromPromise((id: string) =>
|
|
53
|
-
fetch(`/api/users/${id}`).then((response) => {
|
|
54
|
-
if (!response.ok) {
|
|
55
|
-
throw new Error(`Failed to fetch user: ${response.statusText}`)
|
|
56
|
-
}
|
|
57
|
-
return response.json()
|
|
58
|
-
}),
|
|
59
|
-
)(userId)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Step 3: Use in Task-based workflow
|
|
63
|
-
const displayUserProfile = (userId: string): FPromise<void> => {
|
|
64
|
-
return fetchUserData(userId)
|
|
65
|
-
.then((userData) => {
|
|
66
|
-
renderProfile(userData)
|
|
67
|
-
return undefined
|
|
68
|
-
})
|
|
69
|
-
.catch((error) => {
|
|
70
|
-
showErrorMessage(error)
|
|
71
|
-
return undefined
|
|
72
|
-
})
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Step 3: Fully Embrace the Functional Pattern
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
import { Task, TaskResult, TaskException } from "@/core/task/Task"
|
|
80
|
-
import { Either } from "@/either/Either"
|
|
81
|
-
import { Throwable } from "@/core/throwable/Throwable"
|
|
82
|
-
|
|
83
|
-
// Create a domain-specific Task
|
|
84
|
-
const userTask = Task<UserData>({ name: "UserOperations" })
|
|
85
|
-
|
|
86
|
-
// Fully functional API
|
|
87
|
-
const fetchUserData = (userId: string): FPromise<Either<Throwable, UserData>> => {
|
|
88
|
-
return FPromise<Either<Throwable, UserData>>(async (resolve) => {
|
|
89
|
-
try {
|
|
90
|
-
const response = await fetch(`/api/users/${userId}`)
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
resolve(userTask.fail(new Error(`Failed to fetch user: ${response.statusText}`)))
|
|
93
|
-
return
|
|
94
|
-
}
|
|
95
|
-
const data = await response.json()
|
|
96
|
-
resolve(userTask.success(data))
|
|
97
|
-
} catch (error) {
|
|
98
|
-
resolve(userTask.fail(error))
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Functional composition with proper error handling
|
|
104
|
-
const displayUserProfile = (userId: string): FPromise<void> => {
|
|
105
|
-
return fetchUserData(userId).then((result) => {
|
|
106
|
-
if (result.isRight()) {
|
|
107
|
-
renderProfile(result.get())
|
|
108
|
-
} else {
|
|
109
|
-
showErrorMessage(result.get())
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Benefits of Task-Based Approach
|
|
116
|
-
|
|
117
|
-
1. **Explicit Error Handling**: Errors are represented as values in the Either type
|
|
118
|
-
2. **Type Safety**: Error and success paths are fully typed
|
|
119
|
-
3. **Composition**: Tasks can be composed with other functional patterns
|
|
120
|
-
4. **Interoperability**: Can work with existing Promise-based code
|
|
121
|
-
5. **Testability**: Pure functions are easier to test
|
|
122
|
-
|
|
123
|
-
## Best Practices
|
|
124
|
-
|
|
125
|
-
1. Use descriptive names for Tasks that reflect their domain purpose
|
|
126
|
-
2. Leverage the `fromPromise` adapter for gradual migration
|
|
127
|
-
3. Combine with Either for full type safety in error handling
|
|
128
|
-
4. Use `toPromise` when integrating back with traditional Promise-based APIs
|
|
129
|
-
5. Keep Task operations pure when possible
|
package/readme/ai-guide.md
DELETED
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
# AI Guide to Functype
|
|
2
|
-
|
|
3
|
-
This document provides a concise reference for AI models to understand the patterns and usage of the Functype library.
|
|
4
|
-
|
|
5
|
-
## Core Types
|
|
6
|
-
|
|
7
|
-
### Option<T>
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
// Create: Option(value) returns Some(value) or None
|
|
11
|
-
const some = Option(42) // Some(42)
|
|
12
|
-
const none = Option(null) // None
|
|
13
|
-
|
|
14
|
-
// Access: .get() or .getOrElse(default)
|
|
15
|
-
some.get() // 42
|
|
16
|
-
none.getOrElse("default") // "default"
|
|
17
|
-
|
|
18
|
-
// Transform: .map(), .flatMap(), .filter()
|
|
19
|
-
some.map((x) => x * 2) // Some(84)
|
|
20
|
-
some.flatMap((x) => Option(x.toString())) // Some("42")
|
|
21
|
-
some.filter((x) => x > 50) // None
|
|
22
|
-
|
|
23
|
-
// Pattern match: .fold() or .match()
|
|
24
|
-
some.fold(
|
|
25
|
-
() => "empty",
|
|
26
|
-
(val) => `value: ${val}`,
|
|
27
|
-
) // "value: 42"
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Either<L, R>
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
// Create: Right(value) or Left(error)
|
|
34
|
-
const right = Right<string, number>(42)
|
|
35
|
-
const left = Left<string, number>("error")
|
|
36
|
-
|
|
37
|
-
// From functions: Either.tryCatch()
|
|
38
|
-
const result = Either.tryCatch(
|
|
39
|
-
() => JSON.parse('{"key":"value"}'),
|
|
40
|
-
(err) => `Parse error: ${err}`,
|
|
41
|
-
) // Right({key: "value"})
|
|
42
|
-
|
|
43
|
-
// Transform: .map(), .mapLeft(), .flatMap()
|
|
44
|
-
right.map((x) => x * 2) // Right(84)
|
|
45
|
-
left.mapLeft((e) => e.toUpperCase()) // Left("ERROR")
|
|
46
|
-
right.flatMap((x) => Right(x.toString())) // Right("42")
|
|
47
|
-
|
|
48
|
-
// Pattern match: .fold() or .match()
|
|
49
|
-
right.fold(
|
|
50
|
-
(err) => `Error: ${err}`,
|
|
51
|
-
(val) => `Success: ${val}`,
|
|
52
|
-
) // "Success: 42"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Try<T>
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
// Create: Try(() => potentially_throwing_function())
|
|
59
|
-
const success = Try(() => 42)
|
|
60
|
-
const failure = Try(() => {
|
|
61
|
-
throw new Error("Failed")
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
// Transform: .map(), .flatMap(), .recover()
|
|
65
|
-
success.map((x) => x * 2) // Success(84)
|
|
66
|
-
failure.recover("default") // Success("default")
|
|
67
|
-
success.flatMap((x) => Try(() => x.toString())) // Success("42")
|
|
68
|
-
|
|
69
|
-
// Pattern match: .fold() or .match()
|
|
70
|
-
success.fold(
|
|
71
|
-
(err) => `Error: ${err.message}`,
|
|
72
|
-
(val) => `Success: ${val}`,
|
|
73
|
-
) // "Success: 42"
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### List<T>
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
// Create: List([...elements])
|
|
80
|
-
const list = List([1, 2, 3, 4, 5])
|
|
81
|
-
|
|
82
|
-
// Access: .head(), .tail(), .at(index)
|
|
83
|
-
list.head() // Some(1)
|
|
84
|
-
list.tail() // List([2, 3, 4, 5])
|
|
85
|
-
|
|
86
|
-
// Transform: .map(), .flatMap(), .filter()
|
|
87
|
-
list.map((x) => x * 2) // List([2, 4, 6, 8, 10])
|
|
88
|
-
list.filter((x) => x % 2 === 0) // List([2, 4])
|
|
89
|
-
list.flatMap((x) => List([x, x])) // List([1, 1, 2, 2, 3, 3, 4, 4, 5, 5])
|
|
90
|
-
|
|
91
|
-
// Reduce: .foldLeft(), .foldRight()
|
|
92
|
-
list.foldLeft(0)((acc, x) => acc + x) // 15
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Map<K, V>
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
// Create: Map({key: value})
|
|
99
|
-
const map = Map({ a: 1, b: 2, c: 3 })
|
|
100
|
-
|
|
101
|
-
// Access: .get(key), .getOrElse(key, default)
|
|
102
|
-
map.get("a") // Some(1)
|
|
103
|
-
map.getOrElse("d", 0) // 0
|
|
104
|
-
|
|
105
|
-
// Transform: .map(), .filter()
|
|
106
|
-
map.map((v) => v * 2) // Map({a: 2, b: 4, c: 6})
|
|
107
|
-
map.filter((v) => v > 1) // Map({b: 2, c: 3})
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### Set<T>
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
// Create: Set([...elements])
|
|
114
|
-
const set = Set([1, 2, 3, 4, 5])
|
|
115
|
-
|
|
116
|
-
// Operations: .add(), .remove(), .has()
|
|
117
|
-
set.add(6) // Set([1, 2, 3, 4, 5, 6])
|
|
118
|
-
set.remove(3) // Set([1, 2, 4, 5])
|
|
119
|
-
set.has(2) // true
|
|
120
|
-
|
|
121
|
-
// Set operations: .union(), .intersect(), .difference()
|
|
122
|
-
const set2 = Set([4, 5, 6, 7])
|
|
123
|
-
set.union(set2) // Set([1, 2, 3, 4, 5, 6, 7])
|
|
124
|
-
set.intersect(set2) // Set([4, 5])
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### FPromise<T, E>
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
// Create: FPromise.resolve(), FPromise.reject(), FPromise.tryCatch()
|
|
131
|
-
const success = FPromise.resolve(42)
|
|
132
|
-
const failure = FPromise.reject(new Error("Failed"))
|
|
133
|
-
|
|
134
|
-
// From async functions
|
|
135
|
-
const result = FPromise.tryCatchAsync(
|
|
136
|
-
async () => await fetchData(),
|
|
137
|
-
(err) => new Error(`Fetch failed: ${err}`),
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
// Transform: .map(), .mapError(), .flatMap()
|
|
141
|
-
success.map((x) => x * 2) // FPromise<84, never>
|
|
142
|
-
failure.mapError((e) => new Error(`Enhanced: ${e.message}`))
|
|
143
|
-
success.flatMap((x) => FPromise.resolve(x.toString()))
|
|
144
|
-
|
|
145
|
-
// Error handling
|
|
146
|
-
failure.recover("default") // FPromise<"default", never>
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Task
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// Create: Task().Sync() or Task().Async()
|
|
153
|
-
const syncTask = Task().Sync(
|
|
154
|
-
() => 42,
|
|
155
|
-
(err) => new Error(`Failed: ${err}`),
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
const asyncTask = Task().Async(
|
|
159
|
-
async () => await fetchData(),
|
|
160
|
-
async (err) => new Error(`Fetch failed: ${err}`),
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
// From promise
|
|
164
|
-
const fetchUser = Task({ name: "UserFetch" }).fromPromise(fetchUserAPI)
|
|
165
|
-
|
|
166
|
-
// Usage
|
|
167
|
-
syncTask.then((value) => console.log(value)).catch((error) => console.error(error))
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Tuple
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
// Create: Tuple(...values)
|
|
174
|
-
const pair = Tuple(42, "hello")
|
|
175
|
-
|
|
176
|
-
// Access: .first(), .second(), etc.
|
|
177
|
-
pair.first() // 42
|
|
178
|
-
pair.second() // "hello"
|
|
179
|
-
|
|
180
|
-
// Transform: .map(), .mapFirst(), .mapSecond()
|
|
181
|
-
pair.mapFirst((x) => x * 2) // Tuple(84, "hello")
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## Common Patterns
|
|
185
|
-
|
|
186
|
-
### Type Safety
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
// Branded types
|
|
190
|
-
type UserId = Brand<string, "UserId">
|
|
191
|
-
const UserId = (id: string): UserId => {
|
|
192
|
-
if (!/^U\d{6}$/.test(id)) throw new Error("Invalid ID format")
|
|
193
|
-
return id as UserId
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Type-safe functions
|
|
197
|
-
function getUserById(id: UserId): User {
|
|
198
|
-
/* ... */
|
|
199
|
-
}
|
|
200
|
-
getUserById(UserId("U123456")) // Works
|
|
201
|
-
getUserById("U123456") // Type error
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Error Handling
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
// Option for nullable values
|
|
208
|
-
const maybeUser = Option(findUser(id))
|
|
209
|
-
maybeUser.fold(
|
|
210
|
-
() => console.log("User not found"),
|
|
211
|
-
(user) => console.log("User:", user.name),
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
// Either for errors with context
|
|
215
|
-
const validationResult = validateForm(formData)
|
|
216
|
-
validationResult.fold(
|
|
217
|
-
(errors) => handleErrors(errors),
|
|
218
|
-
(data) => processForm(data),
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
// Try for exception safety
|
|
222
|
-
const parseResult = Try(() => JSON.parse(input))
|
|
223
|
-
parseResult.fold(
|
|
224
|
-
(err) => console.error("Parse error:", err.message),
|
|
225
|
-
(data) => console.log("Data:", data),
|
|
226
|
-
)
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
### Chaining Operations
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
// Option chain
|
|
233
|
-
const userCity = Option(user)
|
|
234
|
-
.flatMap((u) => Option(u.address))
|
|
235
|
-
.flatMap((a) => Option(a.city))
|
|
236
|
-
.getOrElse("Unknown")
|
|
237
|
-
|
|
238
|
-
// Either chain
|
|
239
|
-
parseInput(input)
|
|
240
|
-
.flatMap(validateData)
|
|
241
|
-
.flatMap(transformData)
|
|
242
|
-
.fold(
|
|
243
|
-
(err) => handleError(err),
|
|
244
|
-
(result) => displayResult(result),
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
// List processing
|
|
248
|
-
List([1, 2, 3, 4, 5])
|
|
249
|
-
.filter((n) => n % 2 === 0)
|
|
250
|
-
.map((n) => n * n)
|
|
251
|
-
.foldLeft(0)((acc, n) => acc + n) // 20 (4 + 16)
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Pattern Matching
|
|
255
|
-
|
|
256
|
-
```typescript
|
|
257
|
-
// Using match method
|
|
258
|
-
result.match({
|
|
259
|
-
Some: (value) => `Found: ${value}`,
|
|
260
|
-
None: () => "Not found",
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
// Using fold method
|
|
264
|
-
either.fold(
|
|
265
|
-
(left) => `Error: ${left}`,
|
|
266
|
-
(right) => `Success: ${right}`,
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
// Using MatchableUtils
|
|
270
|
-
const isPositive = MatchableUtils.when(
|
|
271
|
-
(n: number) => n > 0,
|
|
272
|
-
(n) => `Positive: ${n}`,
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
const isNegative = MatchableUtils.when(
|
|
276
|
-
(n: number) => n < 0,
|
|
277
|
-
(n) => `Negative: ${n}`,
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
const defaultCase = MatchableUtils.default((n: number) => `Zero: ${n}`)
|
|
281
|
-
|
|
282
|
-
// Usage
|
|
283
|
-
isPositive(42) ?? isNegative(42) ?? defaultCase(42) // "Positive: 42"
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Functional Composition
|
|
287
|
-
|
|
288
|
-
```typescript
|
|
289
|
-
// Using pipe for sequential operations
|
|
290
|
-
import { pipe } from "functype"
|
|
291
|
-
|
|
292
|
-
const result = pipe(
|
|
293
|
-
Option(input),
|
|
294
|
-
(opt) => opt.map((s) => s.trim()),
|
|
295
|
-
(opt) => opt.filter((s) => s.length > 0),
|
|
296
|
-
(opt) => opt.map((s) => parseInt(s, 10)),
|
|
297
|
-
(opt) => opt.filter((n) => !isNaN(n)),
|
|
298
|
-
(opt) => opt.getOrElse(0),
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
// Converting between types
|
|
302
|
-
import { FoldableUtils } from "functype"
|
|
303
|
-
|
|
304
|
-
const optionAsList = FoldableUtils.toList(option)
|
|
305
|
-
const listAsOption = FoldableUtils.toOption(list)
|
|
306
|
-
const tryAsEither = FoldableUtils.toEither(tryVal, "Default error")
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
## Key Principles
|
|
310
|
-
|
|
311
|
-
1. **Immutability**: All data structures return new instances when modified
|
|
312
|
-
2. **Type Safety**: Strong TypeScript typing throughout the library
|
|
313
|
-
3. **Null Safety**: No null/undefined values within containers (Option, Either, etc.)
|
|
314
|
-
4. **Error Handling**: Explicit error handling using functional patterns
|
|
315
|
-
5. **Pattern Matching**: Consistent APIs for inspecting and handling variants
|
|
316
|
-
6. **Composability**: Methods designed for chaining and composition
|
|
317
|
-
7. **Consistency**: Similar patterns across different data structures
|
|
318
|
-
|
|
319
|
-
## Common Imports
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
// Full package import (not recommended for production)
|
|
323
|
-
import { Option, Either, Try, List } from "functype"
|
|
324
|
-
|
|
325
|
-
// Optimized imports for tree-shaking
|
|
326
|
-
import { Option } from "functype/option"
|
|
327
|
-
import { Either } from "functype/either"
|
|
328
|
-
import { List } from "functype/list"
|
|
329
|
-
|
|
330
|
-
// Individual constructor imports
|
|
331
|
-
import { some, none } from "functype/option"
|
|
332
|
-
import { right, left } from "functype/either"
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
## Type Class Hierarchy
|
|
336
|
-
|
|
337
|
-
- **Functor**: `map` - Transform values while preserving structure
|
|
338
|
-
- **Applicative**: Apply functions inside containers
|
|
339
|
-
- **Monad**: `flatMap` - Chain operations that return containerized values
|
|
340
|
-
- **Foldable**: `fold`, `foldLeft`, `foldRight` - Collapse structure
|
|
341
|
-
- **Traversable**: Convert/sequence containers
|
|
342
|
-
|
|
343
|
-
## Anti-Patterns to Avoid
|
|
344
|
-
|
|
345
|
-
1. ❌ **Unnecessary Unwrapping**:
|
|
346
|
-
|
|
347
|
-
```typescript
|
|
348
|
-
// Bad
|
|
349
|
-
if (option.isDefined()) {
|
|
350
|
-
doSomething(option.get())
|
|
351
|
-
} else {
|
|
352
|
-
doSomethingElse()
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Good
|
|
356
|
-
option.fold(
|
|
357
|
-
() => doSomethingElse(),
|
|
358
|
-
(value) => doSomething(value),
|
|
359
|
-
)
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
2. ❌ **Throwing from Inside Containers**:
|
|
363
|
-
|
|
364
|
-
```typescript
|
|
365
|
-
// Bad
|
|
366
|
-
option.map((value) => {
|
|
367
|
-
if (!isValid(value)) throw new Error("Invalid")
|
|
368
|
-
return transform(value)
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
// Good
|
|
372
|
-
option.flatMap((value) => (isValid(value) ? Option(transform(value)) : Option(null)))
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
3. ❌ **Not Using Composition**:
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
// Bad
|
|
379
|
-
const a = option.map((x) => x + 1)
|
|
380
|
-
const b = a.filter((x) => x > 10)
|
|
381
|
-
const c = b.getOrElse(0)
|
|
382
|
-
|
|
383
|
-
// Good
|
|
384
|
-
const result = option
|
|
385
|
-
.map((x) => x + 1)
|
|
386
|
-
.filter((x) => x > 10)
|
|
387
|
-
.getOrElse(0)
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
4. ❌ **Mixing Imperative and Functional Styles**:
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
// Bad
|
|
394
|
-
let result = 0
|
|
395
|
-
option.fold(
|
|
396
|
-
() => {
|
|
397
|
-
result = 42
|
|
398
|
-
},
|
|
399
|
-
(value) => {
|
|
400
|
-
result = value
|
|
401
|
-
},
|
|
402
|
-
)
|
|
403
|
-
|
|
404
|
-
// Good
|
|
405
|
-
const result = option.getOrElse(42)
|
|
406
|
-
```
|