functype 0.8.82 → 0.8.84
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/package.json +1 -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/package.json
CHANGED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# Bundle Size Optimization Guide
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Functype is designed with tree-shaking in mind, allowing you to optimize your application's bundle size by only including the specific modules you need.
|
|
6
|
-
|
|
7
|
-
## Import Strategies
|
|
8
|
-
|
|
9
|
-
### Strategy 1: Selective Module Imports (Recommended)
|
|
10
|
-
|
|
11
|
-
Import only the specific modules you need to reduce bundle size significantly.
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import { Option } from "functype/option"
|
|
15
|
-
import { Either } from "functype/either"
|
|
16
|
-
|
|
17
|
-
// Usage
|
|
18
|
-
const option = Option.some(42)
|
|
19
|
-
const either = Either.right("value")
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### Strategy 2: Direct Constructor Imports (Smallest Bundle)
|
|
23
|
-
|
|
24
|
-
For the most aggressive tree-shaking, import only the specific constructors and functions you need.
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import { some, none } from "functype/option"
|
|
28
|
-
import { right } from "functype/either"
|
|
29
|
-
|
|
30
|
-
// Usage
|
|
31
|
-
const option = some(42)
|
|
32
|
-
const none_value = none()
|
|
33
|
-
const either = right("value")
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Bundle Size Comparison
|
|
37
|
-
|
|
38
|
-
| Import Strategy | Approximate Bundle Size | Best For |
|
|
39
|
-
| --------------- | ------------------------ | -------------------------- |
|
|
40
|
-
| Selective | 200-500 bytes per module | Most applications |
|
|
41
|
-
| Direct | <200 bytes per feature | Size-critical applications |
|
|
42
|
-
|
|
43
|
-
## Common Module Sizes
|
|
44
|
-
|
|
45
|
-
| Module | Approximate Size (minified) | Gzipped Size |
|
|
46
|
-
| -------- | --------------------------- | ------------ |
|
|
47
|
-
| Option | ~200 bytes | ~140 bytes |
|
|
48
|
-
| Either | ~290 bytes | ~190 bytes |
|
|
49
|
-
| List | ~170 bytes | ~125 bytes |
|
|
50
|
-
| Try | ~170 bytes | ~125 bytes |
|
|
51
|
-
| Tuple | ~120 bytes | ~100 bytes |
|
|
52
|
-
| FPromise | ~200 bytes | ~140 bytes |
|
|
53
|
-
|
|
54
|
-
## Additional Tips
|
|
55
|
-
|
|
56
|
-
1. **Import Analysis**: Use tools like [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) or [rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-visualizer) to analyze your bundle and identify opportunities for optimization.
|
|
57
|
-
|
|
58
|
-
2. **Dynamic Imports**: Consider using dynamic imports for rarely used functionality:
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
// Only load when needed
|
|
62
|
-
const useRareFeature = async () => {
|
|
63
|
-
const { someLargeUtility } = await import("functype/some-large-module")
|
|
64
|
-
return someLargeUtility()
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
3. **Development vs Production**: During development, you might prefer the convenience of importing everything. In production builds, switch to selective imports.
|
|
69
|
-
|
|
70
|
-
4. **Peer Dependencies**: Functype has minimal dependencies, and the only external dependency (`safe-stable-stringify`) is quite small.
|
|
71
|
-
|
|
72
|
-
## Need Help?
|
|
73
|
-
|
|
74
|
-
If you need assistance with optimizing your bundle size further, please open an issue on our GitHub repository.
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# FPromise Implementation Assessment
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This document provides an assessment of whether the FPromise implementation is ready to replace the old main branch version, with particular focus on potential impacts to implementing libraries outside this one.
|
|
6
|
-
|
|
7
|
-
## Strengths of the FPromise Implementation
|
|
8
|
-
|
|
9
|
-
1. **Comprehensive Error Handling**: FPromise provides rich error handling capabilities including `mapError`, `tapError`, `recover`, `recoverWith`, `recoverWithF`, `filterError`, and `logError`.
|
|
10
|
-
|
|
11
|
-
2. **Either Integration**: FPromise has built-in conversion to/from Either with `toEither()` and `fromEither()`, which aligns well with the functional programming approach used in the Task implementation.
|
|
12
|
-
|
|
13
|
-
3. **Promise Compatibility**: FPromise implements the PromiseLike interface, ensuring compatibility with async/await and standard Promise chains.
|
|
14
|
-
|
|
15
|
-
4. **Extensive Test Coverage**: The test suite is comprehensive, covering basic functionality, error handling, recovery mechanisms, and real-world scenarios.
|
|
16
|
-
|
|
17
|
-
5. **Retry Mechanisms**: The implementation includes multiple retry strategies (basic retry, retry with backoff, and retry with options).
|
|
18
|
-
|
|
19
|
-
## Potential Compatibility Issues
|
|
20
|
-
|
|
21
|
-
1. **Import Path Differences**: The Task.ts file imports from `"'core/throwable/Throwable"'` and `"'either/Either"'` with unusual quotes, while the actual imports should use `@/` prefix. This might cause issues if external libraries rely on these specific import paths.
|
|
22
|
-
|
|
23
|
-
2. **Type Compatibility**: The Task implementation uses `Either<Throwable, T>` for error handling, while FPromise uses a more generic approach. Libraries expecting specific Either structures might need adjustments.
|
|
24
|
-
|
|
25
|
-
3. **API Surface Changes**: External libraries that directly interact with the Promise implementation might need to adapt to the new methods and patterns provided by FPromise.
|
|
26
|
-
|
|
27
|
-
## Recommendation
|
|
28
|
-
|
|
29
|
-
The FPromise implementation is technically sound and provides significant improvements, but to avoid breaking implementing libraries outside this one:
|
|
30
|
-
|
|
31
|
-
1. **Gradual Migration**: Consider a phased approach where both implementations coexist temporarily.
|
|
32
|
-
|
|
33
|
-
2. **Version Bumping**: This change warrants a major version bump to signal potential breaking changes.
|
|
34
|
-
|
|
35
|
-
3. **Documentation**: Provide clear migration guides for dependent libraries.
|
|
36
|
-
|
|
37
|
-
4. **Import Path Fixes**: Ensure import paths are consistent and follow the project's conventions.
|
|
38
|
-
|
|
39
|
-
5. **Compatibility Layer**: Consider providing a thin compatibility layer for libraries that can't immediately migrate to the new API.
|
|
40
|
-
|
|
41
|
-
## Conclusion
|
|
42
|
-
|
|
43
|
-
The FPromise implementation appears ready to replace the old main branch version from a technical perspective, but careful migration planning is necessary to minimize disruption to implementing libraries.
|
package/readme/HKT.md
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# Higher-Kinded Types (HKT)
|
|
2
|
-
|
|
3
|
-
Higher-kinded types allow for writing generic code that works across different container types like `Option`, `List`, `Either`, and `Try`. This is a powerful abstraction that lets you create algorithms that work with any type that supports certain operations, without having to rewrite them for each specific type.
|
|
4
|
-
|
|
5
|
-
## Introduction
|
|
6
|
-
|
|
7
|
-
In functional programming, many operations like `map`, `flatMap`, or `sequence` follow similar patterns across different data structures. The HKT module provides a unified way to work with these operations for any supporting container type.
|
|
8
|
-
|
|
9
|
-
## Key Concepts
|
|
10
|
-
|
|
11
|
-
1. **Kind**: A type-level function representing a higher-kinded type relationship
|
|
12
|
-
2. **Container Types**: Data structures that contain values (Option, List, Either, etc.)
|
|
13
|
-
3. **Type Constructor**: A function that takes a type and returns a new type
|
|
14
|
-
|
|
15
|
-
## Basic Operations
|
|
16
|
-
|
|
17
|
-
### Map
|
|
18
|
-
|
|
19
|
-
Apply a function to a value inside a container:
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
import { Option, HKT } from "functype"
|
|
23
|
-
|
|
24
|
-
const option = Option(42)
|
|
25
|
-
const doubled = HKT.map(option, (x) => x * 2) // Option(84)
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### FlatMap
|
|
29
|
-
|
|
30
|
-
Apply a function that returns a container to a value inside a container, then flatten the result:
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
const option = Option(42)
|
|
34
|
-
const result = HKT.flatMap(option, (x) => Option(x * 2)) // Option(84)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Flatten
|
|
38
|
-
|
|
39
|
-
Flatten a nested container:
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
const nestedOption = Option(Option(42))
|
|
43
|
-
const flattened = HKT.flatten(nestedOption) // Option(42)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Advanced Operations
|
|
47
|
-
|
|
48
|
-
### Sequence
|
|
49
|
-
|
|
50
|
-
Transform a container of containers into a container of container (e.g., `Option<List<A>>` to `List<Option<A>>`):
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
const optionOfList = Option(List([1, 2, 3]))
|
|
54
|
-
const listOfOptions = HKT.sequence(optionOfList)
|
|
55
|
-
// List([Option(1), Option(2), Option(3)])
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Traverse
|
|
59
|
-
|
|
60
|
-
Transform each element in a container using a function that returns another container type, then sequence the results:
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
const list = List([1, 2, 3])
|
|
64
|
-
const result = HKT.traverse(list, (x) => Option(x * 2))
|
|
65
|
-
// Option(List([2, 4, 6]))
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Applicative (ap)
|
|
69
|
-
|
|
70
|
-
Apply a function inside a container to a value inside another container:
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
const optionFn = Option((x: number) => x * 2)
|
|
74
|
-
const optionValue = Option(21)
|
|
75
|
-
const result = HKT.ap(optionFn, optionValue) // Option(42)
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Supported Container Types
|
|
79
|
-
|
|
80
|
-
The HKT module currently supports the following container types:
|
|
81
|
-
|
|
82
|
-
- `Option<A>`
|
|
83
|
-
- `List<A>`
|
|
84
|
-
- `Either<E, A>`
|
|
85
|
-
- `Try<A>`
|
|
86
|
-
|
|
87
|
-
## Creating Generic Algorithms
|
|
88
|
-
|
|
89
|
-
The power of HKT is in creating algorithms that work with any container type:
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
// A function that works with any container implementing map
|
|
93
|
-
function increment<F extends (a: number) => any>(container: Kind<F, number>): Kind<F, number> {
|
|
94
|
-
return HKT.map(container, (x) => x + 1)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Works with any container
|
|
98
|
-
increment(Option(41)) // Option(42)
|
|
99
|
-
increment(List([1, 2, 3])) // List([2, 3, 4])
|
|
100
|
-
increment(Right<string, number>(41)) // Right(42)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Implementing Your Own Container Types
|
|
104
|
-
|
|
105
|
-
To make your custom container types work with HKT, you need to:
|
|
106
|
-
|
|
107
|
-
1. Implement the appropriate methods (`map`, `flatMap`, etc.)
|
|
108
|
-
2. Add type checking in the HKT functions
|
|
109
|
-
|
|
110
|
-
This allows seamless integration with the rest of the functional programming ecosystem.
|
package/readme/ROADMAP.md
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# Functype Roadmap (2025-2026)
|
|
2
|
-
|
|
3
|
-
This roadmap outlines the planned development path for the Functype library, focusing on expanding functionality, improving performance, ensuring API consistency, and enhancing TypeScript integration.
|
|
4
|
-
|
|
5
|
-
## Q2 2025: Core Functional Data Types
|
|
6
|
-
|
|
7
|
-
### Lazy Evaluation
|
|
8
|
-
|
|
9
|
-
- [ ] Implement `LazyList` / `Stream` for efficient processing of potentially infinite sequences
|
|
10
|
-
- [ ] Add common operations: `map`, `filter`, `take`, `drop`, etc.
|
|
11
|
-
- [ ] Implement memoization for evaluated values
|
|
12
|
-
|
|
13
|
-
### Validation Type
|
|
14
|
-
|
|
15
|
-
- [ ] Create `Validation` data type for applicative validation
|
|
16
|
-
- [ ] Support collecting multiple errors (unlike Either which short-circuits)
|
|
17
|
-
- [ ] Add utilities for combining validation results
|
|
18
|
-
|
|
19
|
-
### Performance Foundation
|
|
20
|
-
|
|
21
|
-
- [ ] Add memoization utilities for expensive function calls
|
|
22
|
-
- [ ] Implement a basic benchmarking suite for measuring performance
|
|
23
|
-
- [ ] Standardize performance metrics across data structures
|
|
24
|
-
|
|
25
|
-
## Q3 2025: Advanced Functional Patterns & Optimizations
|
|
26
|
-
|
|
27
|
-
### Type Classes
|
|
28
|
-
|
|
29
|
-
- [x] Implement `Foldable` typeclass with fold, foldLeft, and foldRight methods
|
|
30
|
-
- [x] Implement `Matchable` typeclass for pattern matching
|
|
31
|
-
- [ ] Implement proper `Functor` and `Monad` typeclasses
|
|
32
|
-
- [ ] Add `Applicative` typeclass
|
|
33
|
-
- [ ] Support `Traversable` with comprehensive HKT
|
|
34
|
-
|
|
35
|
-
### Monad Implementations
|
|
36
|
-
|
|
37
|
-
- [ ] Implement `Reader` monad for dependency injection
|
|
38
|
-
- [ ] Add `State` monad for managing state transformations
|
|
39
|
-
- [ ] Create `IO` monad for pure handling of side effects
|
|
40
|
-
- [ ] Add comprehensive documentation and examples
|
|
41
|
-
|
|
42
|
-
### Performance Improvements
|
|
43
|
-
|
|
44
|
-
- [ ] Implement structural sharing for immutable collections
|
|
45
|
-
- [ ] Optimize recursive operations for large data structures
|
|
46
|
-
- [ ] Add performance comparison against other FP libraries
|
|
47
|
-
|
|
48
|
-
### Lenses & Optics
|
|
49
|
-
|
|
50
|
-
- [ ] Implement lens abstraction for immutable updates
|
|
51
|
-
- [ ] Add prism implementation for optional data
|
|
52
|
-
- [ ] Create utilities for composing lenses and prisms
|
|
53
|
-
|
|
54
|
-
## Q4 2025: TypeScript Enhancements & API Consistency
|
|
55
|
-
|
|
56
|
-
### TypeScript Integration
|
|
57
|
-
|
|
58
|
-
- [x] Add support for higher-kinded types
|
|
59
|
-
- [ ] Remove `any` from HKT
|
|
60
|
-
- [x] Implement branded/nominal types for stronger type safety
|
|
61
|
-
- [ ] Add type-level utilities using newer TypeScript features
|
|
62
|
-
- [ ] Leverage const type parameters and tuple manipulation
|
|
63
|
-
|
|
64
|
-
### API Normalization
|
|
65
|
-
|
|
66
|
-
- [ ] Review and standardize API across all modules
|
|
67
|
-
- [ ] Ensure consistent implementation of the Scala-inspired pattern
|
|
68
|
-
- [ ] Standardize import patterns (@imports throughout)
|
|
69
|
-
- [ ] Create migration guides for API changes
|
|
70
|
-
|
|
71
|
-
## Q1 2026: Testing, Documentation & Community Support
|
|
72
|
-
|
|
73
|
-
### Testing Expansion
|
|
74
|
-
|
|
75
|
-
- [ ] Add test coverage metrics and set coverage goals
|
|
76
|
-
- [ ] Expand property-based testing across all modules
|
|
77
|
-
- [ ] Create interoperability tests with popular libraries
|
|
78
|
-
- [ ] Add more specialized test cases for error handling
|
|
79
|
-
|
|
80
|
-
### Documentation & Examples
|
|
81
|
-
|
|
82
|
-
- [ ] Add comprehensive documentation for all modules
|
|
83
|
-
- [ ] Create step-by-step migration guides from imperative to functional
|
|
84
|
-
- [ ] Add real-world examples showcasing practical applications
|
|
85
|
-
- [ ] Create tutorial sections for beginners
|
|
86
|
-
|
|
87
|
-
### Community Engagement
|
|
88
|
-
|
|
89
|
-
- [ ] Create contribution guidelines and templates
|
|
90
|
-
- [ ] Set up automated issue/PR handling
|
|
91
|
-
- [ ] Establish regular release schedule
|
|
92
|
-
- [ ] Add community forum/discussion platform
|
|
93
|
-
|
|
94
|
-
## Ongoing Priorities
|
|
95
|
-
|
|
96
|
-
### Compatibility
|
|
97
|
-
|
|
98
|
-
- [ ] Ensure compatibility with Node.js LTS versions
|
|
99
|
-
- [ ] Maintain browser compatibility
|
|
100
|
-
- [ ] Support for Deno and other runtimes
|
|
101
|
-
- [ ] Test with various bundlers (webpack, esbuild, etc.)
|
|
102
|
-
|
|
103
|
-
### Bundle Size
|
|
104
|
-
|
|
105
|
-
- [x] Optimize tree-shaking
|
|
106
|
-
- [x] Provide guidance on importing only needed modules
|
|
107
|
-
- [x] Add bundle size monitoring to CI/CD
|
|
108
|
-
|
|
109
|
-
### Community Feedback
|
|
110
|
-
|
|
111
|
-
- [ ] Regular review of GitHub issues and feature requests
|
|
112
|
-
- [ ] Prioritization based on community needs
|
|
113
|
-
- [ ] Transparent development process
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
# Task Implementation Analysis and Best Practices
|
|
2
|
-
|
|
3
|
-
This document provides a deep analysis of the Task implementation in functype, discussing its design considerations, trade-offs, and recommendations for effective usage.
|
|
4
|
-
|
|
5
|
-
## Overview of Task Implementation
|
|
6
|
-
|
|
7
|
-
The Task module provides a robust bridge between functional programming patterns and asynchronous operations. It offers a structured way to handle errors, cancellation, and progress tracking while maintaining type safety.
|
|
8
|
-
|
|
9
|
-
Key features include:
|
|
10
|
-
|
|
11
|
-
- Named task contexts for richer error information
|
|
12
|
-
- Explicit try/catch/finally semantics
|
|
13
|
-
- Interoperability with Promise-based code
|
|
14
|
-
- Cancellation support
|
|
15
|
-
- Progress tracking for long-running operations
|
|
16
|
-
|
|
17
|
-
## Advanced Design Considerations
|
|
18
|
-
|
|
19
|
-
### Error Context Propagation
|
|
20
|
-
|
|
21
|
-
**Current Behavior:**
|
|
22
|
-
When tasks are nested, the outer task's context (name, description) overwrites the inner task's context in error propagation:
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
// If an error occurs in the inner task:
|
|
26
|
-
await Task({ name: "OuterTask" }).Async(async () => {
|
|
27
|
-
return Task({ name: "InnerTask" }).Async(async () => {
|
|
28
|
-
throw new Error("Inner error")
|
|
29
|
-
})
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
// The error will have:
|
|
33
|
-
// - message: "Inner error"
|
|
34
|
-
// - name: "OuterTask" (not "InnerTask")
|
|
35
|
-
// - taskInfo: { name: "OuterTask", ... }
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
This happens because when the inner task's promise rejects, it's caught by the outer task, which wraps it in its own error context. This is technically correct from a runtime perspective but might be counterintuitive when debugging.
|
|
39
|
-
|
|
40
|
-
**Recommendation:**
|
|
41
|
-
Consider implementing an option to preserve the innermost error context:
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
// Possible enhancement
|
|
45
|
-
const preserveErrorContext = true
|
|
46
|
-
await Task({ name: "OuterTask", preserveErrorContext }).Async(...)
|
|
47
|
-
|
|
48
|
-
// Which would yield an error with:
|
|
49
|
-
// - Error context: "InnerTask" (preserved from inner task)
|
|
50
|
-
// - Original context chain: ["OuterTask", "InnerTask"] (for tracing)
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Asynchronous Edge Cases
|
|
54
|
-
|
|
55
|
-
Some asynchronous patterns can create complexity in error handling, particularly around cancellation and finally blocks:
|
|
56
|
-
|
|
57
|
-
**Potential Issues:**
|
|
58
|
-
|
|
59
|
-
1. Race conditions between cancellation signals and operation completion
|
|
60
|
-
2. Callback execution order with finally blocks and cancellation
|
|
61
|
-
3. Resource cleanup timing in complex async flows
|
|
62
|
-
|
|
63
|
-
**Recommendations:**
|
|
64
|
-
|
|
65
|
-
- Use explicit state machines for complex task flows
|
|
66
|
-
- Implement deterministic callback ordering guarantees
|
|
67
|
-
- Add explicit resource tracking for critical operations
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
// Structured resource pattern example
|
|
71
|
-
Task.withResource(
|
|
72
|
-
() => openResource(),
|
|
73
|
-
(resource) => useResource(resource),
|
|
74
|
-
(resource) => closeResource(resource),
|
|
75
|
-
)
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Performance Considerations
|
|
79
|
-
|
|
80
|
-
Task operations add some overhead compared to native Promises:
|
|
81
|
-
|
|
82
|
-
1. **Extra Promise wrapping** - Each Task operation creates additional Promise layers
|
|
83
|
-
2. **Error conversion** - Converting to Throwable objects has a small cost
|
|
84
|
-
3. **Context maintenance** - Task metadata is carried throughout the operation
|
|
85
|
-
4. **Cancellation checks** - Additional checks during execution
|
|
86
|
-
|
|
87
|
-
**Recommendations:**
|
|
88
|
-
|
|
89
|
-
- Use the Task API selectively for operations where the benefits outweigh costs
|
|
90
|
-
- Consider a lightweight mode for performance-critical paths:
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
// Hypothetical lightweight mode
|
|
94
|
-
const lightTask = Task.light()
|
|
95
|
-
lightTask.Async(...) // Minimal overhead, fewer features
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
- Group multiple small tasks into larger task units to amortize overhead
|
|
99
|
-
|
|
100
|
-
## Cancellation Best Practices
|
|
101
|
-
|
|
102
|
-
The cancellation system is cooperative rather than preemptive, meaning tasks must actively check for cancellation:
|
|
103
|
-
|
|
104
|
-
### Effective Cancellation Patterns
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
// 1. Regular checking pattern
|
|
108
|
-
Task.cancellable(async (token) => {
|
|
109
|
-
for (const item of largeDataset) {
|
|
110
|
-
// Check cancellation periodically
|
|
111
|
-
if (token.isCancelled) {
|
|
112
|
-
return // Exit early
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Process item
|
|
116
|
-
await processItem(item)
|
|
117
|
-
}
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
// 2. Integration with fetch and DOM APIs
|
|
121
|
-
Task.cancellable(async (token) => {
|
|
122
|
-
const response = await fetch(url, {
|
|
123
|
-
signal: token.signal, // AbortSignal integration
|
|
124
|
-
})
|
|
125
|
-
return await response.json()
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
// 3. Custom cancellation behavior
|
|
129
|
-
Task.cancellable(async (token) => {
|
|
130
|
-
let resources = []
|
|
131
|
-
|
|
132
|
-
token.onCancel(() => {
|
|
133
|
-
// Clean up resources when cancelled
|
|
134
|
-
resources.forEach((r) => r.dispose())
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
// Main task work...
|
|
138
|
-
})
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Cancellation Caveats
|
|
142
|
-
|
|
143
|
-
1. **Cancellation is not guaranteed to be immediate** - Tasks may continue briefly after cancellation
|
|
144
|
-
2. **External resources may need explicit cleanup** - Database connections, file handles, etc.
|
|
145
|
-
3. **Third-party APIs may not support cancellation** - Wrapping them requires careful handling
|
|
146
|
-
|
|
147
|
-
## Progress Tracking
|
|
148
|
-
|
|
149
|
-
For long-running operations, progress tracking provides visibility:
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// Basic progress usage
|
|
153
|
-
const { task, currentProgress } = Task.withProgress(
|
|
154
|
-
async (updateProgress) => {
|
|
155
|
-
updateProgress(0) // Start
|
|
156
|
-
|
|
157
|
-
// Do work in chunks
|
|
158
|
-
for (let i = 0; i < 10; i++) {
|
|
159
|
-
await doChunk(i)
|
|
160
|
-
updateProgress((i + 1) * 10) // 10%, 20%, etc.
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return result
|
|
164
|
-
},
|
|
165
|
-
(percent) => {
|
|
166
|
-
// Update UI with progress
|
|
167
|
-
updateProgressBar(percent)
|
|
168
|
-
},
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
// Combined with cancellation
|
|
172
|
-
const { task, cancel, currentProgress } = Task.withProgress(async (updateProgress, token) => {
|
|
173
|
-
// Both progress tracking and cancellation
|
|
174
|
-
})
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Progress Implementation Considerations
|
|
178
|
-
|
|
179
|
-
1. **Progress granularity** - Too frequent updates can impact performance
|
|
180
|
-
2. **Cancellation interaction** - Progress should stop updating when cancelled
|
|
181
|
-
3. **Indeterminate phases** - Some operations can't report precise progress
|
|
182
|
-
|
|
183
|
-
## Advanced Task Composition
|
|
184
|
-
|
|
185
|
-
### Task Racing
|
|
186
|
-
|
|
187
|
-
The `Task.race` functionality allows racing multiple tasks against each other with optional timeout:
|
|
188
|
-
|
|
189
|
-
```typescript
|
|
190
|
-
// Race with timeout
|
|
191
|
-
const raceResult = await Task.race([slowOperation(), fastOperation()], 1000) // 1 second timeout
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Potential Future Composition Enhancements
|
|
195
|
-
|
|
196
|
-
1. **Structured Concurrency**
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// Hypothetical API
|
|
200
|
-
const { results, errors } = await Task.scope(async (scope) => {
|
|
201
|
-
// All tasks in scope have linked lifecycle
|
|
202
|
-
const task1 = scope.spawn(operation1())
|
|
203
|
-
const task2 = scope.spawn(operation2())
|
|
204
|
-
|
|
205
|
-
return await scope.join() // Wait for all to complete or fail
|
|
206
|
-
})
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
2. **Resource Management**
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
// Hypothetical API for guaranteed resource cleanup
|
|
213
|
-
await Task.using(
|
|
214
|
-
() => openDatabase(), // Acquire
|
|
215
|
-
async (db) => {
|
|
216
|
-
// Use resource
|
|
217
|
-
return await db.query()
|
|
218
|
-
},
|
|
219
|
-
(db) => db.close(), // Always called
|
|
220
|
-
)
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## Testing Considerations
|
|
224
|
-
|
|
225
|
-
Testing Task-based code requires special attention:
|
|
226
|
-
|
|
227
|
-
1. **Time and async flow control** - Use timeouts and explicit resolutions
|
|
228
|
-
2. **Cancellation testing** - Test both normal and cancelled paths
|
|
229
|
-
3. **Progress testing** - Verify progress reporting accuracy
|
|
230
|
-
4. **Error context verification** - Check error details are preserved
|
|
231
|
-
|
|
232
|
-
Example property tests:
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
// Task Async should satisfy monad laws
|
|
236
|
-
test("Right identity: t.Async(f) >>= id = t.Async(f)", () => {
|
|
237
|
-
fc.assert(
|
|
238
|
-
fc.property(fc.string(), async (value) => {
|
|
239
|
-
const f = async () => value
|
|
240
|
-
const taskResult = await Task().Async(f)
|
|
241
|
-
const directResult = await f()
|
|
242
|
-
return taskResult === directResult
|
|
243
|
-
}),
|
|
244
|
-
)
|
|
245
|
-
})
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
## Integration with Other Patterns
|
|
249
|
-
|
|
250
|
-
Tasks work especially well when combined with other functional patterns:
|
|
251
|
-
|
|
252
|
-
### With Option
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
// Convert nullable API response to Option
|
|
256
|
-
const getUserOption = async (id: string) => {
|
|
257
|
-
const response = await Task().Async(async () => {
|
|
258
|
-
return fetch(`/api/users/${id}`)
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
return Option.fromNullable(response)
|
|
262
|
-
}
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### With Either
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
// Type-specific error handling
|
|
269
|
-
const result = await Task().Async(async () => {
|
|
270
|
-
// Operation returning Either<ApiError, UserData>
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
// Handle specific error types
|
|
274
|
-
result.match({
|
|
275
|
-
left: (error) => handleApiError(error),
|
|
276
|
-
right: (data) => useData(data),
|
|
277
|
-
})
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Conclusion
|
|
281
|
-
|
|
282
|
-
The Task implementation offers a powerful approach to functional asynchronous programming. By understanding its design trade-offs and following best practices, developers can create more robust, maintainable code with better error handling.
|
|
283
|
-
|
|
284
|
-
Remember that Task is not a replacement for all Promise usage - evaluate when the additional features justify the slightly increased complexity and overhead.
|
|
285
|
-
|
|
286
|
-
## See Also
|
|
287
|
-
|
|
288
|
-
- [Task Error Handling](../docs/task-error-handling.md)
|
|
289
|
-
- [Task Migration Guide](../docs/TaskMigration.md)
|
|
290
|
-
- [Task TODO](../docs/TASK-TODO.md)
|
package/readme/TASK-TODO.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Task TODO
|
|
2
|
-
|
|
3
|
-
## Goals
|
|
4
|
-
|
|
5
|
-
- Enhance the Task module as an adapter between promise-based code and functional patterns
|
|
6
|
-
- Improve interoperability with existing JavaScript/TypeScript codebases
|
|
7
|
-
- Allow gradual migration to functional patterns without complete rewrites
|
|
8
|
-
|
|
9
|
-
## Implementation Tasks
|
|
10
|
-
|
|
11
|
-
- [x] Review current Task implementation for completeness of promise integration
|
|
12
|
-
- [x] Ensure robust error handling in sync/async conversions
|
|
13
|
-
- [x] Document explicit try/catch/finally semantics
|
|
14
|
-
- [x] Add examples showing migration from promise-based to functional patterns
|
|
15
|
-
- [x] Add utilities to simplify Task composition with promise-returning functions
|
|
16
|
-
- [x] Create migration guide for converting promise chains to Task operations
|
|
17
|
-
- [x] Create Task.race for racing multiple tasks with timeout support
|
|
18
|
-
- [x] Implement Task.fromNodeCallback for Node.js style callbacks
|
|
19
|
-
- [x] Implement cancellation support for Task operations
|
|
20
|
-
- [x] Add progress tracking for long-running Task operations
|
|
21
|
-
|
|
22
|
-
## Design Considerations
|
|
23
|
-
|
|
24
|
-
- Maintain clear separation between synchronous and promise-based operations
|
|
25
|
-
- Preserve functional error handling patterns while supporting promise interop
|
|
26
|
-
- Keep API consistent with the rest of the library's functional approach
|
|
27
|
-
|
|
28
|
-
## Completed Enhancements
|
|
29
|
-
|
|
30
|
-
- Added `fromPromise` adapter to convert promise-returning functions to Task-compatible functions
|
|
31
|
-
- Added `toPromise` converter to transform Task results back to promises
|
|
32
|
-
- Enhanced documentation with clearer descriptions of functionality
|
|
33
|
-
- Created TaskMigration.md guide showing how to migrate from promises to functional Task patterns
|
|
34
|
-
- Added comprehensive tests for the new adapter methods
|
|
35
|
-
- Implemented `Task.race` for racing multiple tasks with timeout support
|
|
36
|
-
- Added `Task.fromNodeCallback` for Node.js-style callback integration
|
|
37
|
-
- Implemented cancellation support with CancellationToken pattern
|
|
38
|
-
- Added progress tracking for long-running operations
|
|
39
|
-
- Created property-based tests verifying monadic laws and edge cases
|
|
40
|
-
- Added comprehensive documentation for all new functionality
|