mirror of
https://github.com/IBM/fp-go.git
synced 2025-12-09 23:11:40 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62fcd186a3 | ||
|
|
2db7e83651 | ||
|
|
8d92df83ad |
@@ -1,277 +0,0 @@
|
||||
# Either Benchmarks
|
||||
|
||||
This document describes the benchmark suite for the `either` package and how to interpret the results to identify performance bottlenecks.
|
||||
|
||||
## Running Benchmarks
|
||||
|
||||
To run all benchmarks:
|
||||
```bash
|
||||
cd either
|
||||
go test -bench=. -benchmem
|
||||
```
|
||||
|
||||
To run specific benchmarks:
|
||||
```bash
|
||||
go test -bench=BenchmarkMap -benchmem
|
||||
go test -bench=BenchmarkChain -benchmem
|
||||
```
|
||||
|
||||
To run with more iterations for stable results:
|
||||
```bash
|
||||
go test -bench=. -benchmem -benchtime=1000000x
|
||||
```
|
||||
|
||||
## Benchmark Categories
|
||||
|
||||
### 1. Core Constructors
|
||||
- `BenchmarkLeft` - Creating Left values
|
||||
- `BenchmarkRight` - Creating Right values
|
||||
- `BenchmarkOf` - Creating Right values via Of
|
||||
|
||||
**Key Insights:**
|
||||
- Right construction is ~2x faster than Left (~0.45ns vs ~0.87ns)
|
||||
- All constructors have zero allocations
|
||||
- These are the fastest operations in the package
|
||||
|
||||
### 2. Predicates
|
||||
- `BenchmarkIsLeft` - Testing if Either is Left
|
||||
- `BenchmarkIsRight` - Testing if Either is Right
|
||||
|
||||
**Key Insights:**
|
||||
- Both predicates are extremely fast (~0.3ns)
|
||||
- Zero allocations
|
||||
- No performance difference between Left and Right checks
|
||||
|
||||
### 3. Fold Operations
|
||||
- `BenchmarkMonadFold_Right/Left` - Direct fold with handlers
|
||||
- `BenchmarkFold_Right/Left` - Curried fold
|
||||
|
||||
**Key Insights:**
|
||||
- Right path is ~10x faster than Left path (0.5ns vs 5-7ns)
|
||||
- Curried version adds ~2ns overhead on Left path
|
||||
- Zero allocations for both paths
|
||||
- **Bottleneck:** Left path has higher overhead due to type assertions
|
||||
|
||||
### 4. Unwrap Operations
|
||||
- `BenchmarkUnwrap_Right/Left` - Converting to (value, error) tuple
|
||||
- `BenchmarkUnwrapError_Right/Left` - Specialized for error types
|
||||
|
||||
**Key Insights:**
|
||||
- Right path is ~10x faster than Left path
|
||||
- Zero allocations
|
||||
- Similar performance characteristics to Fold
|
||||
|
||||
### 5. Functor Operations (Map)
|
||||
- `BenchmarkMonadMap_Right/Left` - Direct map
|
||||
- `BenchmarkMap_Right/Left` - Curried map
|
||||
- `BenchmarkMapLeft_Right/Left` - Mapping over Left channel
|
||||
- `BenchmarkBiMap_Right/Left` - Mapping both channels
|
||||
|
||||
**Key Insights:**
|
||||
- Map operations: ~11-14ns, zero allocations
|
||||
- MapLeft on Left: ~34ns with 1 allocation (16B)
|
||||
- BiMap: ~29-39ns with 1 allocation (16B)
|
||||
- **Bottleneck:** BiMap and MapLeft allocate closures
|
||||
|
||||
### 6. Monad Operations (Chain)
|
||||
- `BenchmarkMonadChain_Right/Left` - Direct chain
|
||||
- `BenchmarkChain_Right/Left` - Curried chain
|
||||
- `BenchmarkChainFirst_Right/Left` - Chain preserving original value
|
||||
- `BenchmarkFlatten_Right/Left` - Removing nesting
|
||||
|
||||
**Key Insights:**
|
||||
- Chain is very fast: 2-7ns, zero allocations
|
||||
- **Bottleneck:** ChainFirst on Right: ~168ns with 5 allocations (120B)
|
||||
- ChainFirst on Left short-circuits efficiently (~9ns)
|
||||
- Curried Chain is faster than MonadChain
|
||||
|
||||
### 7. Applicative Operations (Ap)
|
||||
- `BenchmarkMonadAp_RightRight/RightLeft/LeftRight` - Direct apply
|
||||
- `BenchmarkAp_RightRight` - Curried apply
|
||||
|
||||
**Key Insights:**
|
||||
- Ap operations: 5-12ns, zero allocations
|
||||
- Short-circuits efficiently when either operand is Left
|
||||
- MonadAp slightly slower than curried Ap
|
||||
|
||||
### 8. Alternative Operations
|
||||
- `BenchmarkAlt_RightRight/LeftRight` - Providing alternatives
|
||||
- `BenchmarkOrElse_Right/Left` - Recovery from Left
|
||||
|
||||
**Key Insights:**
|
||||
- Very fast: 1.6-4.5ns, zero allocations
|
||||
- Right path short-circuits without evaluating alternative
|
||||
- Efficient error recovery mechanism
|
||||
|
||||
### 9. Conversion Operations
|
||||
- `BenchmarkTryCatch_Success/Error` - Converting (value, error) tuples
|
||||
- `BenchmarkTryCatchError_Success/Error` - Specialized for errors
|
||||
- `BenchmarkSwap_Right/Left` - Swapping Left/Right
|
||||
- `BenchmarkGetOrElse_Right/Left` - Extracting with default
|
||||
|
||||
**Key Insights:**
|
||||
- All operations: 0.3-6ns, zero allocations
|
||||
- Very efficient conversions
|
||||
- GetOrElse on Right is extremely fast (~0.3ns)
|
||||
|
||||
### 10. Pipeline Operations
|
||||
- `BenchmarkPipeline_Map_Right/Left` - Single Map in pipeline
|
||||
- `BenchmarkPipeline_Chain_Right/Left` - Single Chain in pipeline
|
||||
- `BenchmarkPipeline_Complex_Right/Left` - Multiple operations
|
||||
|
||||
**Key Insights:**
|
||||
- **Major Bottleneck:** Pipeline operations allocate heavily
|
||||
- Single Map: ~100ns with 4 allocations (96B)
|
||||
- Complex pipeline: ~200-250ns with 8 allocations (192B)
|
||||
- Chain in pipeline is much faster: 2-4.5ns, zero allocations
|
||||
- **Recommendation:** Use direct function calls instead of F.Pipe for hot paths
|
||||
|
||||
### 11. Sequence Operations
|
||||
- `BenchmarkMonadSequence2_RightRight/LeftRight` - Combining 2 Eithers
|
||||
- `BenchmarkMonadSequence3_RightRightRight` - Combining 3 Eithers
|
||||
|
||||
**Key Insights:**
|
||||
- Sequence2: ~6ns, zero allocations
|
||||
- Sequence3: ~9ns, zero allocations
|
||||
- Efficient short-circuiting on Left
|
||||
- Linear scaling with number of Eithers
|
||||
|
||||
### 12. Do-Notation Operations
|
||||
- `BenchmarkDo` - Creating empty context
|
||||
- `BenchmarkBind_Right` - Binding values to context
|
||||
- `BenchmarkLet_Right` - Pure computations in context
|
||||
|
||||
**Key Insights:**
|
||||
- Do is extremely fast: ~0.4ns, zero allocations
|
||||
- **Bottleneck:** Bind: ~130ns with 6 allocations (144B)
|
||||
- Let is more efficient: ~23ns with 1 allocation (16B)
|
||||
- **Recommendation:** Prefer Let over Bind when possible
|
||||
|
||||
### 13. String Formatting
|
||||
- `BenchmarkString_Right/Left` - Converting to string
|
||||
|
||||
**Key Insights:**
|
||||
- Right: ~69ns with 1 allocation (16B)
|
||||
- Left: ~111ns with 1 allocation (48B)
|
||||
- Only use for debugging, not in hot paths
|
||||
|
||||
## Performance Bottlenecks Summary
|
||||
|
||||
### Critical Bottlenecks (>100ns or multiple allocations)
|
||||
|
||||
1. **Pipeline operations with F.Pipe** (~100-250ns, 4-8 allocations)
|
||||
- **Impact:** High - commonly used pattern
|
||||
- **Mitigation:** Use direct function calls in hot paths
|
||||
- **Example:**
|
||||
```go
|
||||
// Slow (100ns, 4 allocs)
|
||||
result := F.Pipe1(either, Map[error](transform))
|
||||
|
||||
// Fast (12ns, 0 allocs)
|
||||
result := Map[error](transform)(either)
|
||||
```
|
||||
|
||||
2. **ChainFirst on Right path** (~168ns, 5 allocations)
|
||||
- **Impact:** Medium - used for side effects
|
||||
- **Mitigation:** Avoid in hot paths, use direct Chain if side effect result not needed
|
||||
|
||||
3. **Bind in do-notation** (~130ns, 6 allocations)
|
||||
- **Impact:** Medium - used in complex workflows
|
||||
- **Mitigation:** Use Let for pure computations, minimize Bind calls
|
||||
|
||||
### Minor Bottlenecks (20-50ns or 1 allocation)
|
||||
|
||||
4. **BiMap operations** (~29-39ns, 1 allocation)
|
||||
- **Impact:** Low - less commonly used
|
||||
- **Mitigation:** Use Map or MapLeft separately if only one channel needs transformation
|
||||
|
||||
5. **MapLeft on Left path** (~34ns, 1 allocation)
|
||||
- **Impact:** Low - error path typically not hot
|
||||
- **Mitigation:** None needed unless in critical error handling
|
||||
|
||||
## Optimization Recommendations
|
||||
|
||||
### For Hot Paths
|
||||
|
||||
1. **Prefer direct function calls over pipelines:**
|
||||
```go
|
||||
// Instead of:
|
||||
result := F.Pipe3(either, Map(f1), Chain(f2), Map(f3))
|
||||
|
||||
// Use:
|
||||
result := Map(f3)(Chain(f2)(Map(f1)(either)))
|
||||
```
|
||||
|
||||
2. **Use Chain instead of ChainFirst when possible:**
|
||||
```go
|
||||
// If you don't need the original value:
|
||||
result := Chain(f)(either) // Fast
|
||||
|
||||
// Instead of:
|
||||
result := ChainFirst(func(a A) Either[E, B] {
|
||||
f(a)
|
||||
return Right[error](a)
|
||||
})(either) // Slow
|
||||
```
|
||||
|
||||
3. **Prefer Let over Bind in do-notation:**
|
||||
```go
|
||||
// For pure computations:
|
||||
result := Let(setter, pureFunc)(either) // 23ns
|
||||
|
||||
// Instead of:
|
||||
result := Bind(setter, func(s S) Either[E, T] {
|
||||
return Right[error](pureFunc(s))
|
||||
})(either) // 130ns
|
||||
```
|
||||
|
||||
### For Error Paths
|
||||
|
||||
- Left path operations are generally slower but acceptable since errors are exceptional
|
||||
- Focus optimization on Right (success) path
|
||||
- Use MapLeft and BiMap freely in error handling code
|
||||
|
||||
### Memory Considerations
|
||||
|
||||
- Most operations have zero allocations
|
||||
- Avoid string formatting (String()) in production code
|
||||
- Pipeline operations allocate - use sparingly in hot paths
|
||||
|
||||
## Comparative Analysis
|
||||
|
||||
### Fastest Operations (<5ns)
|
||||
- Constructors (Left, Right, Of)
|
||||
- Predicates (IsLeft, IsRight)
|
||||
- GetOrElse on Right
|
||||
- Chain (curried version)
|
||||
- Alt/OrElse on Right
|
||||
- Do
|
||||
|
||||
### Medium Speed (5-20ns)
|
||||
- Fold operations
|
||||
- Unwrap operations
|
||||
- Map operations
|
||||
- MonadChain
|
||||
- Ap operations
|
||||
- Sequence operations
|
||||
- Let
|
||||
|
||||
### Slower Operations (>20ns)
|
||||
- BiMap
|
||||
- MapLeft on Left
|
||||
- String formatting
|
||||
- Pipeline operations
|
||||
- Bind
|
||||
- ChainFirst on Right
|
||||
|
||||
## Conclusion
|
||||
|
||||
The `either` package is highly optimized with most operations completing in nanoseconds with zero allocations. The main performance considerations are:
|
||||
|
||||
1. Avoid F.Pipe in hot paths - use direct function calls
|
||||
2. Prefer Let over Bind in do-notation
|
||||
3. Use Chain instead of ChainFirst when the original value isn't needed
|
||||
4. The Right (success) path is consistently faster than Left (error) path
|
||||
5. Most operations have zero allocations, making them GC-friendly
|
||||
|
||||
For typical use cases, the performance is excellent. Only in extremely hot paths (millions of operations per second) should you consider the micro-optimizations suggested above.
|
||||
@@ -1,66 +0,0 @@
|
||||
2025/11/08 09:58:27 out: test
|
||||
goos: windows
|
||||
goarch: amd64
|
||||
pkg: github.com/IBM/fp-go/v2/either
|
||||
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
|
||||
BenchmarkLeft-16 1000000000 0.8874 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkRight-16 1000000000 0.5417 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOf-16 1000000000 0.5751 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsLeft-16 1000000000 0.2564 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsRight-16 1000000000 0.2269 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadFold_Right-16 1000000000 0.2320 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadFold_Left-16 1000000000 0.2322 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFold_Right-16 1000000000 0.2258 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFold_Left-16 1000000000 0.2313 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrap_Right-16 1000000000 0.2263 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrap_Left-16 1000000000 0.2294 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrapError_Right-16 1000000000 0.2228 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrapError_Left-16 1000000000 0.2253 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadMap_Right-16 174464096 6.904 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadMap_Left-16 268986886 4.544 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMap_Right-16 183861918 7.383 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMap_Left-16 275134764 4.550 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMapLeft_Right-16 296297173 4.534 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMapLeft_Left-16 157772442 9.138 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBiMap_Right-16 29180962 42.42 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBiMap_Left-16 28203771 42.58 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadChain_Right-16 461887719 2.479 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadChain_Left-16 572442338 1.964 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkChain_Right-16 1000000000 0.8653 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkChain_Left-16 1000000000 0.7443 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkChainFirst_Right-16 10762824 126.3 ns/op 120 B/op 5 allocs/op
|
||||
BenchmarkChainFirst_Left-16 253755582 4.394 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFlatten_Right-16 100000000 10.23 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFlatten_Left-16 100000000 10.09 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadAp_RightRight-16 176284840 6.734 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadAp_RightLeft-16 434471394 2.874 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadAp_LeftRight-16 451601320 2.795 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAp_RightRight-16 177040815 6.786 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAlt_RightRight-16 1000000000 0.8134 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAlt_LeftRight-16 1000000000 0.8202 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOrElse_Right-16 1000000000 0.8501 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOrElse_Left-16 1000000000 0.6825 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatch_Success-16 565892258 2.091 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatch_Error-16 408437362 2.905 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatchError_Success-16 562500350 2.150 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatchError_Error-16 424754853 2.769 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSwap_Right-16 1000000000 1.173 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSwap_Left-16 995449963 1.162 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGetOrElse_Right-16 1000000000 0.2427 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGetOrElse_Left-16 1000000000 0.2457 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPipeline_Map_Right-16 12972158 86.78 ns/op 96 B/op 4 allocs/op
|
||||
BenchmarkPipeline_Map_Left-16 13715413 86.44 ns/op 96 B/op 4 allocs/op
|
||||
BenchmarkPipeline_Chain_Right-16 1000000000 0.8321 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPipeline_Chain_Left-16 1000000000 0.6849 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPipeline_Complex_Right-16 6963496 173.1 ns/op 192 B/op 8 allocs/op
|
||||
BenchmarkPipeline_Complex_Left-16 6503500 174.6 ns/op 192 B/op 8 allocs/op
|
||||
BenchmarkMonadSequence2_RightRight-16 236820728 5.060 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadSequence2_LeftRight-16 545203750 2.215 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadSequence3_RightRightRight-16 120316371 9.657 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkDo-16 1000000000 0.2182 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBind_Right-16 8784483 137.3 ns/op 144 B/op 6 allocs/op
|
||||
BenchmarkLet_Right-16 51356451 23.61 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkString_Right-16 15664588 79.59 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkString_Left-16 12226196 103.0 ns/op 48 B/op 1 allocs/op
|
||||
PASS
|
||||
ok github.com/IBM/fp-go/v2/either 66.778s
|
||||
@@ -1,66 +0,0 @@
|
||||
2025/11/08 09:02:49 out: test
|
||||
goos: windows
|
||||
goarch: amd64
|
||||
pkg: github.com/IBM/fp-go/v2/either
|
||||
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
|
||||
BenchmarkLeft-16 71784931 16.52 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkRight-16 132961911 8.799 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkOf-16 132703154 8.894 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkIsLeft-16 1000000000 0.2188 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsRight-16 1000000000 0.2202 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadFold_Right-16 1000000000 0.2215 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadFold_Left-16 1000000000 0.2198 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFold_Right-16 1000000000 0.4467 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFold_Left-16 1000000000 0.2616 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrap_Right-16 1000000000 0.2937 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrap_Left-16 1000000000 0.3019 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrapError_Right-16 1000000000 0.5962 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrapError_Left-16 1000000000 0.2343 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadMap_Right-16 78786684 15.06 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkMonadMap_Left-16 60553860 20.47 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkMap_Right-16 81438198 14.52 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkMap_Left-16 61249489 22.27 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkMapLeft_Right-16 100000000 11.54 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkMapLeft_Left-16 53107918 22.85 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkBiMap_Right-16 47468728 22.02 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkBiMap_Left-16 53815036 22.93 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkMonadChain_Right-16 100000000 10.73 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkMonadChain_Left-16 71138511 18.14 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkChain_Right-16 129499905 9.254 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkChain_Left-16 62561910 16.93 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkChainFirst_Right-16 9357235 134.4 ns/op 144 B/op 7 allocs/op
|
||||
BenchmarkChainFirst_Left-16 46529842 21.52 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkFlatten_Right-16 353777130 3.336 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFlatten_Left-16 63614917 20.03 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkMonadAp_RightRight-16 81210578 14.48 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkMonadAp_RightLeft-16 63263110 19.65 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkMonadAp_LeftRight-16 60824167 17.87 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkAp_RightRight-16 81860413 14.58 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkAlt_RightRight-16 130601511 8.955 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkAlt_LeftRight-16 136723171 9.062 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkOrElse_Right-16 130252060 9.187 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkOrElse_Left-16 137806992 9.325 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkTryCatch_Success-16 100000000 10.24 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkTryCatch_Error-16 61422231 19.29 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkTryCatchError_Success-16 100000000 10.06 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkTryCatchError_Error-16 68159355 18.74 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkSwap_Right-16 125292858 9.739 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkSwap_Left-16 64153282 18.13 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkGetOrElse_Right-16 1000000000 0.2167 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGetOrElse_Left-16 1000000000 0.2154 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPipeline_Map_Right-16 13649640 88.69 ns/op 104 B/op 5 allocs/op
|
||||
BenchmarkPipeline_Map_Left-16 11999940 101.6 ns/op 112 B/op 5 allocs/op
|
||||
BenchmarkPipeline_Chain_Right-16 135232720 8.888 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkPipeline_Chain_Left-16 72481712 17.98 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkPipeline_Complex_Right-16 6314605 185.0 ns/op 216 B/op 11 allocs/op
|
||||
BenchmarkPipeline_Complex_Left-16 5850564 217.1 ns/op 240 B/op 11 allocs/op
|
||||
BenchmarkMonadSequence2_RightRight-16 85073667 14.56 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkMonadSequence2_LeftRight-16 69183006 19.21 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkMonadSequence3_RightRightRight-16 73083387 16.82 ns/op 8 B/op 1 allocs/op
|
||||
BenchmarkDo-16 1000000000 0.2121 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBind_Right-16 8551692 142.5 ns/op 160 B/op 8 allocs/op
|
||||
BenchmarkLet_Right-16 40338846 30.32 ns/op 24 B/op 2 allocs/op
|
||||
BenchmarkString_Right-16 15758826 73.75 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkString_Left-16 12798620 94.71 ns/op 48 B/op 1 allocs/op
|
||||
PASS
|
||||
ok github.com/IBM/fp-go/v2/either 72.607s
|
||||
@@ -1,66 +0,0 @@
|
||||
2025/11/08 09:04:15 out: test
|
||||
goos: windows
|
||||
goarch: amd64
|
||||
pkg: github.com/IBM/fp-go/v2/either
|
||||
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
|
||||
BenchmarkLeft-16 1000000000 0.8335 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkRight-16 1000000000 0.4256 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOf-16 1000000000 0.4370 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsLeft-16 1000000000 0.2199 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsRight-16 1000000000 0.2181 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadFold_Right-16 1000000000 0.3209 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadFold_Left-16 289017271 4.176 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFold_Right-16 1000000000 0.4687 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFold_Left-16 274668618 4.669 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrap_Right-16 1000000000 0.3520 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrap_Left-16 273187717 4.226 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrapError_Right-16 1000000000 0.4503 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnwrapError_Left-16 279699219 4.285 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadMap_Right-16 152084527 7.855 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadMap_Left-16 181258614 6.711 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMap_Right-16 146735268 8.328 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMap_Left-16 136155859 8.829 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMapLeft_Right-16 254870900 4.687 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMapLeft_Left-16 44555998 27.87 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkBiMap_Right-16 53596072 22.82 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkBiMap_Left-16 44864508 26.88 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkMonadChain_Right-16 350417858 3.456 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadChain_Left-16 228175792 5.328 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkChain_Right-16 681885982 1.558 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkChain_Left-16 273064258 4.305 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkChainFirst_Right-16 11384614 107.1 ns/op 120 B/op 5 allocs/op
|
||||
BenchmarkChainFirst_Left-16 145602547 8.177 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFlatten_Right-16 333052550 3.563 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFlatten_Left-16 178190731 6.822 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadAp_RightRight-16 153308394 7.690 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadAp_RightLeft-16 187848016 6.593 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadAp_LeftRight-16 226528400 5.175 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAp_RightRight-16 163718392 7.422 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAlt_RightRight-16 773928914 1.603 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAlt_LeftRight-16 296220595 4.034 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOrElse_Right-16 772122764 1.587 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkOrElse_Left-16 295411228 4.018 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatch_Success-16 446405984 2.691 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatch_Error-16 445387840 2.750 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatchError_Success-16 442356684 2.682 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkTryCatchError_Error-16 441426070 2.801 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSwap_Right-16 448856370 2.819 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSwap_Left-16 213215637 5.602 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGetOrElse_Right-16 1000000000 0.2997 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGetOrElse_Left-16 270844960 4.397 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPipeline_Map_Right-16 13732585 83.72 ns/op 96 B/op 4 allocs/op
|
||||
BenchmarkPipeline_Map_Left-16 15044474 84.31 ns/op 96 B/op 4 allocs/op
|
||||
BenchmarkPipeline_Chain_Right-16 763123821 1.638 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPipeline_Chain_Left-16 265623768 4.513 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPipeline_Complex_Right-16 6963698 173.9 ns/op 192 B/op 8 allocs/op
|
||||
BenchmarkPipeline_Complex_Left-16 6829795 178.2 ns/op 192 B/op 8 allocs/op
|
||||
BenchmarkMonadSequence2_RightRight-16 188600779 6.451 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadSequence2_LeftRight-16 203297380 5.912 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMonadSequence3_RightRightRight-16 127004353 9.377 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkDo-16 1000000000 0.2096 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBind_Right-16 9656920 124.5 ns/op 144 B/op 6 allocs/op
|
||||
BenchmarkLet_Right-16 49086178 22.46 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkString_Right-16 16014156 71.13 ns/op 16 B/op 1 allocs/op
|
||||
BenchmarkString_Left-16 11845627 94.86 ns/op 48 B/op 1 allocs/op
|
||||
PASS
|
||||
ok github.com/IBM/fp-go/v2/either 82.686s
|
||||
@@ -58,8 +58,8 @@ func Do[E, S any](
|
||||
//go:inline
|
||||
func Bind[E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) Either[E, T],
|
||||
) func(Either[E, S1]) Either[E, S2] {
|
||||
f Kleisli[E, S1, T],
|
||||
) Operator[E, S1, S2] {
|
||||
return C.Bind(
|
||||
Chain[E, S1, S2],
|
||||
Map[E, T, S2],
|
||||
@@ -88,7 +88,7 @@ func Bind[E, S1, S2, T any](
|
||||
func Let[E, S1, S2, T any](
|
||||
key func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) func(Either[E, S1]) Either[E, S2] {
|
||||
) Operator[E, S1, S2] {
|
||||
return F.Let(
|
||||
Map[E, S1, S2],
|
||||
key,
|
||||
@@ -115,7 +115,7 @@ func Let[E, S1, S2, T any](
|
||||
func LetTo[E, S1, S2, T any](
|
||||
key func(T) func(S1) S2,
|
||||
b T,
|
||||
) func(Either[E, S1]) Either[E, S2] {
|
||||
) Operator[E, S1, S2] {
|
||||
return F.LetTo(
|
||||
Map[E, S1, S2],
|
||||
key,
|
||||
@@ -137,7 +137,7 @@ func LetTo[E, S1, S2, T any](
|
||||
//go:inline
|
||||
func BindTo[E, S1, T any](
|
||||
setter func(T) S1,
|
||||
) func(Either[E, T]) Either[E, S1] {
|
||||
) Operator[E, T, S1] {
|
||||
return C.BindTo(
|
||||
Map[E, T, S1],
|
||||
setter,
|
||||
@@ -164,7 +164,7 @@ func BindTo[E, S1, T any](
|
||||
func ApS[E, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa Either[E, T],
|
||||
) func(Either[E, S1]) Either[E, S2] {
|
||||
) Operator[E, S1, S2] {
|
||||
return A.ApS(
|
||||
Ap[S2, E, T],
|
||||
Map[E, S1, func(T) S2],
|
||||
@@ -271,7 +271,7 @@ func ApSL[E, S, T any](
|
||||
//go:inline
|
||||
func BindL[E, S, T any](
|
||||
lens Lens[S, T],
|
||||
f func(T) Either[E, T],
|
||||
f Kleisli[E, T, T],
|
||||
) Endomorphism[Either[E, S]] {
|
||||
return Bind[E, S, S, T](lens.Set, function.Flow2(lens.Get, f))
|
||||
}
|
||||
|
||||
@@ -449,7 +449,7 @@ func Alt[E, A any](that L.Lazy[Either[E, A]]) Operator[E, A, A] {
|
||||
// return either.Right[error](0) // default value
|
||||
// })
|
||||
// result := recover(either.Left[int](errors.New("fail"))) // Right(0)
|
||||
func OrElse[E, A any](onLeft func(e E) Either[E, A]) Operator[E, A, A] {
|
||||
func OrElse[E, A any](onLeft Kleisli[E, E, A]) Operator[E, A, A] {
|
||||
return Fold(onLeft, Of[E, A])
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
benchErr = errors.New("benchmark error")
|
||||
errBench = errors.New("benchmark error")
|
||||
benchResult Either[error, int]
|
||||
benchBool bool
|
||||
benchInt int
|
||||
@@ -34,7 +34,7 @@ var (
|
||||
func BenchmarkLeft(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchResult = Left[int](benchErr)
|
||||
benchResult = Left[int](errBench)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func BenchmarkOf(b *testing.B) {
|
||||
|
||||
// Benchmark predicates
|
||||
func BenchmarkIsLeft(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -84,7 +84,7 @@ func BenchmarkMonadFold_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMonadFold_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
onLeft := func(e error) int { return 0 }
|
||||
onRight := func(a int) int { return a * 2 }
|
||||
b.ResetTimer()
|
||||
@@ -108,7 +108,7 @@ func BenchmarkFold_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkFold_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
folder := Fold(
|
||||
func(e error) int { return 0 },
|
||||
func(a int) int { return a * 2 },
|
||||
@@ -131,7 +131,7 @@ func BenchmarkUnwrap_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkUnwrap_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -149,7 +149,7 @@ func BenchmarkUnwrapError_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkUnwrapError_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -169,7 +169,7 @@ func BenchmarkMonadMap_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMonadMap_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
mapper := func(a int) int { return a * 2 }
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -189,7 +189,7 @@ func BenchmarkMap_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMap_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
mapper := Map[error](func(a int) int { return a * 2 })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -209,7 +209,7 @@ func BenchmarkMapLeft_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMapLeft_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
mapper := MapLeft[int](func(e error) string { return e.Error() })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -232,7 +232,7 @@ func BenchmarkBiMap_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkBiMap_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
mapper := BiMap(
|
||||
func(e error) string { return e.Error() },
|
||||
func(a int) string { return "value" },
|
||||
@@ -256,7 +256,7 @@ func BenchmarkMonadChain_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMonadChain_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
chainer := func(a int) Either[error, int] { return Right[error](a * 2) }
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -276,7 +276,7 @@ func BenchmarkChain_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkChain_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
chainer := Chain[error](func(a int) Either[error, int] { return Right[error](a * 2) })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -287,7 +287,7 @@ func BenchmarkChain_Left(b *testing.B) {
|
||||
|
||||
func BenchmarkChainFirst_Right(b *testing.B) {
|
||||
right := Right[error](42)
|
||||
chainer := ChainFirst[error](func(a int) Either[error, string] { return Right[error]("logged") })
|
||||
chainer := ChainFirst(func(a int) Either[error, string] { return Right[error]("logged") })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -296,7 +296,7 @@ func BenchmarkChainFirst_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkChainFirst_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
chainer := ChainFirst[error](func(a int) Either[error, string] { return Right[error]("logged") })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -315,7 +315,7 @@ func BenchmarkFlatten_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkFlatten_Left(b *testing.B) {
|
||||
nested := Left[Either[error, int]](benchErr)
|
||||
nested := Left[Either[error, int]](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -336,7 +336,7 @@ func BenchmarkMonadAp_RightRight(b *testing.B) {
|
||||
|
||||
func BenchmarkMonadAp_RightLeft(b *testing.B) {
|
||||
fab := Right[error](func(a int) int { return a * 2 })
|
||||
fa := Left[int](benchErr)
|
||||
fa := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -345,7 +345,7 @@ func BenchmarkMonadAp_RightLeft(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMonadAp_LeftRight(b *testing.B) {
|
||||
fab := Left[func(int) int](benchErr)
|
||||
fab := Left[func(int) int](errBench)
|
||||
fa := Right[error](42)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -368,7 +368,7 @@ func BenchmarkAp_RightRight(b *testing.B) {
|
||||
// Benchmark alternative operations
|
||||
func BenchmarkAlt_RightRight(b *testing.B) {
|
||||
right := Right[error](42)
|
||||
alternative := Alt[error](func() Either[error, int] { return Right[error](99) })
|
||||
alternative := Alt(func() Either[error, int] { return Right[error](99) })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -377,7 +377,7 @@ func BenchmarkAlt_RightRight(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkAlt_LeftRight(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
alternative := Alt[error](func() Either[error, int] { return Right[error](99) })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -397,8 +397,8 @@ func BenchmarkOrElse_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkOrElse_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
recover := OrElse[error](func(e error) Either[error, int] { return Right[error](0) })
|
||||
left := Left[int](errBench)
|
||||
recover := OrElse(func(e error) Either[error, int] { return Right[error](0) })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -419,7 +419,7 @@ func BenchmarkTryCatch_Error(b *testing.B) {
|
||||
onThrow := func(err error) error { return err }
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchResult = TryCatch(0, benchErr, onThrow)
|
||||
benchResult = TryCatch(0, errBench, onThrow)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ func BenchmarkTryCatchError_Success(b *testing.B) {
|
||||
func BenchmarkTryCatchError_Error(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchResult = TryCatchError(0, benchErr)
|
||||
benchResult = TryCatchError(0, errBench)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +447,7 @@ func BenchmarkSwap_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkSwap_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -466,7 +466,7 @@ func BenchmarkGetOrElse_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkGetOrElse_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
getter := GetOrElse[error](func(e error) int { return 0 })
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
@@ -489,7 +489,7 @@ func BenchmarkPipeline_Map_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkPipeline_Map_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -513,7 +513,7 @@ func BenchmarkPipeline_Chain_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkPipeline_Chain_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -539,7 +539,7 @@ func BenchmarkPipeline_Complex_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkPipeline_Complex_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -565,7 +565,7 @@ func BenchmarkMonadSequence2_RightRight(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMonadSequence2_LeftRight(b *testing.B) {
|
||||
e1 := Left[int](benchErr)
|
||||
e1 := Left[int](errBench)
|
||||
e2 := Right[error](20)
|
||||
f := func(a, b int) Either[error, int] { return Right[error](a + b) }
|
||||
b.ResetTimer()
|
||||
@@ -641,7 +641,7 @@ func BenchmarkString_Right(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkString_Left(b *testing.B) {
|
||||
left := Left[int](benchErr)
|
||||
left := Left[int](errBench)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestUneitherize1(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "positive", result)
|
||||
|
||||
result, err = uneitherized(-1)
|
||||
_, err = uneitherized(-1)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
// )(f)(eitherOfOption)
|
||||
func Traverse[A, E, B, HKTB, HKTRB any](
|
||||
mof func(Either[E, B]) HKTRB,
|
||||
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
|
||||
mmap func(Kleisli[E, B, B]) func(HKTB) HKTRB,
|
||||
) func(func(A) HKTB) func(Either[E, A]) HKTRB {
|
||||
|
||||
left := F.Flow2(Left[B, E], mof)
|
||||
@@ -62,7 +62,7 @@ func Traverse[A, E, B, HKTB, HKTRB any](
|
||||
// )(eitherOfOption)
|
||||
func Sequence[E, A, HKTA, HKTRA any](
|
||||
mof func(Either[E, A]) HKTRA,
|
||||
mmap func(func(A) Either[E, A]) func(HKTA) HKTRA,
|
||||
mmap func(Kleisli[E, A, A]) func(HKTA) HKTRA,
|
||||
) func(Either[E, HKTA]) HKTRA {
|
||||
return Fold(F.Flow2(Left[A, E], mof), mmap(Right[E, A]))
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ import (
|
||||
)
|
||||
|
||||
// MkdirAll create a sequence of directories, see [os.MkdirAll]
|
||||
func MkdirAll(path string, perm os.FileMode) ioeither.IOEither[error, string] {
|
||||
func MkdirAll(path string, perm os.FileMode) IOEither[error, string] {
|
||||
return ioeither.TryCatchError(func() (string, error) {
|
||||
return path, os.MkdirAll(path, perm)
|
||||
})
|
||||
}
|
||||
|
||||
// Mkdir create a directory, see [os.Mkdir]
|
||||
func Mkdir(path string, perm os.FileMode) ioeither.IOEither[error, string] {
|
||||
func Mkdir(path string, perm os.FileMode) IOEither[error, string] {
|
||||
return ioeither.TryCatchError(func() (string, error) {
|
||||
return path, os.Mkdir(path, perm)
|
||||
})
|
||||
|
||||
@@ -32,8 +32,8 @@ var (
|
||||
)
|
||||
|
||||
// WriteFile writes a data blob to a file
|
||||
func WriteFile(dstName string, perm os.FileMode) func([]byte) ioeither.IOEither[error, []byte] {
|
||||
return func(data []byte) ioeither.IOEither[error, []byte] {
|
||||
func WriteFile(dstName string, perm os.FileMode) Kleisli[error, []byte, []byte] {
|
||||
return func(data []byte) IOEither[error, []byte] {
|
||||
return ioeither.TryCatchError(func() ([]byte, error) {
|
||||
return data, os.WriteFile(dstName, data, perm)
|
||||
})
|
||||
@@ -41,14 +41,14 @@ func WriteFile(dstName string, perm os.FileMode) func([]byte) ioeither.IOEither[
|
||||
}
|
||||
|
||||
// Remove removes a file by name
|
||||
func Remove(name string) ioeither.IOEither[error, string] {
|
||||
func Remove(name string) IOEither[error, string] {
|
||||
return ioeither.TryCatchError(func() (string, error) {
|
||||
return name, os.Remove(name)
|
||||
})
|
||||
}
|
||||
|
||||
// Close closes an object
|
||||
func Close[C io.Closer](c C) ioeither.IOEither[error, any] {
|
||||
func Close[C io.Closer](c C) IOEither[error, any] {
|
||||
return ioeither.TryCatchError(func() (any, error) {
|
||||
return c, c.Close()
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
)
|
||||
|
||||
// ReadAll uses a generator function to create a stream, reads it and closes it
|
||||
func ReadAll[R io.ReadCloser](acquire ioeither.IOEither[error, R]) ioeither.IOEither[error, []byte] {
|
||||
func ReadAll[R io.ReadCloser](acquire IOEither[error, R]) IOEither[error, []byte] {
|
||||
return F.Pipe1(
|
||||
F.Flow2(
|
||||
FL.ToReader[R],
|
||||
|
||||
@@ -39,6 +39,6 @@ var (
|
||||
)
|
||||
|
||||
// WithTempFile creates a temporary file, then invokes a callback to create a resource based on the file, then close and remove the temp file
|
||||
func WithTempFile[A any](f func(*os.File) ioeither.IOEither[error, A]) ioeither.IOEither[error, A] {
|
||||
func WithTempFile[A any](f Kleisli[error, *os.File, A]) IOEither[error, A] {
|
||||
return ioeither.WithResource[A](onCreateTempFile, onReleaseTempFile)(f)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestWithTempFile(t *testing.T) {
|
||||
|
||||
func TestWithTempFileOnClosedFile(t *testing.T) {
|
||||
|
||||
res := WithTempFile(func(f *os.File) ioeither.IOEither[error, []byte] {
|
||||
res := WithTempFile(func(f *os.File) IOEither[error, []byte] {
|
||||
return F.Pipe2(
|
||||
f,
|
||||
onWriteAll[*os.File]([]byte("Carsten")),
|
||||
|
||||
11
v2/ioeither/file/types.go
Normal file
11
v2/ioeither/file/types.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
type (
|
||||
IOEither[E, T any] = ioeither.IOEither[E, T]
|
||||
Kleisli[E, A, B any] = ioeither.Kleisli[E, A, B]
|
||||
Operator[E, A, B any] = ioeither.Operator[E, A, B]
|
||||
)
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
func onWriteAll[W io.Writer](data []byte) func(w W) ioeither.IOEither[error, []byte] {
|
||||
return func(w W) ioeither.IOEither[error, []byte] {
|
||||
func onWriteAll[W io.Writer](data []byte) Kleisli[error, W, []byte] {
|
||||
return func(w W) IOEither[error, []byte] {
|
||||
return ioeither.TryCatchError(func() ([]byte, error) {
|
||||
_, err := w.Write(data)
|
||||
return data, err
|
||||
@@ -31,9 +31,9 @@ func onWriteAll[W io.Writer](data []byte) func(w W) ioeither.IOEither[error, []b
|
||||
}
|
||||
|
||||
// WriteAll uses a generator function to create a stream, writes data to it and closes it
|
||||
func WriteAll[W io.WriteCloser](data []byte) func(acquire ioeither.IOEither[error, W]) ioeither.IOEither[error, []byte] {
|
||||
func WriteAll[W io.WriteCloser](data []byte) Operator[error, W, []byte] {
|
||||
onWrite := onWriteAll[W](data)
|
||||
return func(onCreate ioeither.IOEither[error, W]) ioeither.IOEither[error, []byte] {
|
||||
return func(onCreate IOEither[error, W]) IOEither[error, []byte] {
|
||||
return ioeither.WithResource[[]byte](
|
||||
onCreate,
|
||||
Close[W])(
|
||||
@@ -43,7 +43,7 @@ func WriteAll[W io.WriteCloser](data []byte) func(acquire ioeither.IOEither[erro
|
||||
}
|
||||
|
||||
// Write uses a generator function to create a stream, writes data to it and closes it
|
||||
func Write[R any, W io.WriteCloser](acquire ioeither.IOEither[error, W]) func(use func(W) ioeither.IOEither[error, R]) ioeither.IOEither[error, R] {
|
||||
func Write[R any, W io.WriteCloser](acquire IOEither[error, W]) Kleisli[error, Kleisli[error, W, R], R] {
|
||||
return ioeither.WithResource[R](
|
||||
acquire,
|
||||
Close[W])
|
||||
|
||||
2687
v2/ioeither/gen.go
2687
v2/ioeither/gen.go
File diff suppressed because it is too large
Load Diff
@@ -230,7 +230,7 @@ func Memoize[E, A any](ma IOEither[E, A]) IOEither[E, A] {
|
||||
return io.Memoize(ma)
|
||||
}
|
||||
|
||||
func MonadMapLeft[E1, E2, A any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2, A] {
|
||||
func MonadMapLeft[A, E1, E2 any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2, A] {
|
||||
return eithert.MonadMapLeft(
|
||||
io.MonadMap[Either[E1, A], Either[E2, A]],
|
||||
fa,
|
||||
|
||||
@@ -24,7 +24,7 @@ type (
|
||||
)
|
||||
|
||||
// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative
|
||||
func AltSemigroup[E, A any]() semigroup.Semigroup[IOEither[E, A]] {
|
||||
func AltSemigroup[E, A any]() Semigroup[E, A] {
|
||||
return semigroup.AltSemigroup(
|
||||
MonadAlt[E, A],
|
||||
)
|
||||
|
||||
48
v2/ioresult/ap.go
Normal file
48
v2/ioresult/ap.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
||||
//
|
||||
//go:inline
|
||||
func MonadApFirst[A, B any](first IOResult[A], second IOResult[B]) IOResult[A] {
|
||||
return ioeither.MonadApFirst(first, second)
|
||||
}
|
||||
|
||||
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||
//
|
||||
//go:inline
|
||||
func ApFirst[A, B any](second IOResult[B]) Operator[A, A] {
|
||||
return ioeither.ApFirst[A](second)
|
||||
}
|
||||
|
||||
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
||||
//
|
||||
//go:inline
|
||||
func MonadApSecond[A, B any](first IOResult[A], second IOResult[B]) IOResult[B] {
|
||||
return ioeither.MonadApSecond(first, second)
|
||||
}
|
||||
|
||||
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||
//
|
||||
//go:inline
|
||||
func ApSecond[A, B any](second IOResult[B]) Operator[A, B] {
|
||||
return ioeither.ApSecond[A](second)
|
||||
}
|
||||
296
v2/ioresult/bind.go
Normal file
296
v2/ioresult/bind.go
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||
)
|
||||
|
||||
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||
// This is the starting point for do-notation style composition.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
// result := ioeither.Do[error](State{})
|
||||
//
|
||||
//go:inline
|
||||
func Do[S any](
|
||||
empty S,
|
||||
) IOResult[S] {
|
||||
return ioeither.Do[error](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
|
||||
// This enables sequential composition where each step can depend on the results of previous steps.
|
||||
//
|
||||
// The setter function takes the result of the computation and returns a function that
|
||||
// updates the context from S1 to S2.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// ioeither.Do[error](State{}),
|
||||
// ioeither.Bind(
|
||||
// func(user User) func(State) State {
|
||||
// return func(s State) State { s.User = user; return s }
|
||||
// },
|
||||
// func(s State) ioeither.IOResult[error, User] {
|
||||
// return ioeither.TryCatch(func() (User, error) {
|
||||
// return fetchUser()
|
||||
// })
|
||||
// },
|
||||
// ),
|
||||
// ioeither.Bind(
|
||||
// func(posts []Post) func(State) State {
|
||||
// return func(s State) State { s.Posts = posts; return s }
|
||||
// },
|
||||
// func(s State) ioeither.IOResult[error, []Post] {
|
||||
// // This can access s.User from the previous step
|
||||
// return ioeither.TryCatch(func() ([]Post, error) {
|
||||
// return fetchPostsForUser(s.User.ID)
|
||||
// })
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func Bind[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f Kleisli[S1, T],
|
||||
) Operator[S1, S2] {
|
||||
return ioeither.Bind(setter, f)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
//
|
||||
//go:inline
|
||||
func Let[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) Operator[S1, S2] {
|
||||
return ioeither.Let[error](setter, f)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
//
|
||||
//go:inline
|
||||
func LetTo[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
b T,
|
||||
) Operator[S1, S2] {
|
||||
return ioeither.LetTo[error](setter, b)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
//
|
||||
//go:inline
|
||||
func BindTo[S1, T any](
|
||||
setter func(T) S1,
|
||||
) Operator[T, S1] {
|
||||
return ioeither.BindTo[error](setter)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering
|
||||
// the context and the value concurrently (using Applicative rather than Monad).
|
||||
// This allows independent computations to be combined without one depending on the result of the other.
|
||||
//
|
||||
// Unlike Bind, which sequences operations, ApS can be used when operations are independent
|
||||
// and can conceptually run in parallel.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
//
|
||||
// // These operations are independent and can be combined with ApS
|
||||
// getUser := ioeither.Right[error](User{ID: 1, Name: "Alice"})
|
||||
// getPosts := ioeither.Right[error]([]Post{{ID: 1, Title: "Hello"}})
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// ioeither.Do[error](State{}),
|
||||
// ioeither.ApS(
|
||||
// func(user User) func(State) State {
|
||||
// return func(s State) State { s.User = user; return s }
|
||||
// },
|
||||
// getUser,
|
||||
// ),
|
||||
// ioeither.ApS(
|
||||
// func(posts []Post) func(State) State {
|
||||
// return func(s State) State { s.Posts = posts; return s }
|
||||
// },
|
||||
// getPosts,
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func ApS[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa IOResult[T],
|
||||
) Operator[S1, S2] {
|
||||
return ioeither.ApS(setter, fa)
|
||||
}
|
||||
|
||||
// ApSL attaches a value to a context using a lens-based setter.
|
||||
// This is a convenience function that combines ApS with a lens, allowing you to use
|
||||
// optics to update nested structures in a more composable way.
|
||||
//
|
||||
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||
// This eliminates the need to manually write setter functions.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Config struct {
|
||||
// Host string
|
||||
// Port int
|
||||
// }
|
||||
//
|
||||
// portLens := lens.MakeLens(
|
||||
// func(c Config) int { return c.Port },
|
||||
// func(c Config, p int) Config { c.Port = p; return c },
|
||||
// )
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// ioeither.Of[error](Config{Host: "localhost"}),
|
||||
// ioeither.ApSL(portLens, ioeither.Of[error](8080)),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func ApSL[S, T any](
|
||||
lens L.Lens[S, T],
|
||||
fa IOResult[T],
|
||||
) Operator[S, S] {
|
||||
return ioeither.ApSL(lens, fa)
|
||||
}
|
||||
|
||||
// BindL attaches the result of a computation to a context using a lens-based setter.
|
||||
// This is a convenience function that combines Bind with a lens, allowing you to use
|
||||
// optics to update nested structures based on their current values.
|
||||
//
|
||||
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||
// The computation function f receives the current value of the focused field and returns
|
||||
// an IOResult that produces the new value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Counter struct {
|
||||
// Value int
|
||||
// }
|
||||
//
|
||||
// valueLens := lens.MakeLens(
|
||||
// func(c Counter) int { return c.Value },
|
||||
// func(c Counter, v int) Counter { c.Value = v; return c },
|
||||
// )
|
||||
//
|
||||
// increment := func(v int) ioeither.IOResult[error, int] {
|
||||
// return ioeither.TryCatch(func() (int, error) {
|
||||
// if v >= 100 {
|
||||
// return 0, errors.New("overflow")
|
||||
// }
|
||||
// return v + 1, nil
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// result := F.Pipe1(
|
||||
// ioeither.Of[error](Counter{Value: 42}),
|
||||
// ioeither.BindL(valueLens, increment),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func BindL[S, T any](
|
||||
lens L.Lens[S, T],
|
||||
f Kleisli[T, T],
|
||||
) Operator[S, S] {
|
||||
return ioeither.BindL(lens, f)
|
||||
}
|
||||
|
||||
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||
// This is a convenience function that combines Let with a lens, allowing you to use
|
||||
// optics to update nested structures with pure transformations.
|
||||
//
|
||||
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||
// The transformation function f receives the current value of the focused field and returns
|
||||
// the new value directly (not wrapped in IOResult).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Counter struct {
|
||||
// Value int
|
||||
// }
|
||||
//
|
||||
// valueLens := lens.MakeLens(
|
||||
// func(c Counter) int { return c.Value },
|
||||
// func(c Counter, v int) Counter { c.Value = v; return c },
|
||||
// )
|
||||
//
|
||||
// double := func(v int) int { return v * 2 }
|
||||
//
|
||||
// result := F.Pipe1(
|
||||
// ioeither.Of[error](Counter{Value: 21}),
|
||||
// ioeither.LetL(valueLens, double),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func LetL[S, T any](
|
||||
lens L.Lens[S, T],
|
||||
f Endomorphism[T],
|
||||
) Operator[S, S] {
|
||||
return ioeither.LetL[error](lens, f)
|
||||
}
|
||||
|
||||
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||
// This is a convenience function that combines LetTo with a lens, allowing you to use
|
||||
// optics to set nested fields to specific values.
|
||||
//
|
||||
// The lens parameter provides the setter for a field within the structure S.
|
||||
// Unlike LetL which transforms the current valuLetToL simply replaces it with
|
||||
// the provided constant value b.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Config struct {
|
||||
// Debug bool
|
||||
// Timeout int
|
||||
// }
|
||||
//
|
||||
// debugLens := lens.MakeLens(
|
||||
// func(c Config) bool { return c.Debug },
|
||||
// func(c Config, d bool) Config { c.Debug = d; return c },
|
||||
// )
|
||||
//
|
||||
// result := F.Pipe1(
|
||||
// ioeither.Of[error](Config{Debug: truTimeout: 30}),
|
||||
// ioeither.LetToL(debugLens, false),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func LetToL[S, T any](
|
||||
lens L.Lens[S, T],
|
||||
b T,
|
||||
) Operator[S, S] {
|
||||
return ioeither.LetToL[error](lens, b)
|
||||
}
|
||||
57
v2/ioresult/bind_test.go
Normal file
57
v2/ioresult/bind_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/internal/utils"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getLastName(s utils.Initial) IOResult[string] {
|
||||
return Of("Doe")
|
||||
}
|
||||
|
||||
func getGivenName(s utils.WithLastName) IOResult[string] {
|
||||
return Of("John")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
Bind(utils.SetLastName, getLastName),
|
||||
Bind(utils.SetGivenName, getGivenName),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), result.Of("John Doe"))
|
||||
}
|
||||
|
||||
func TestApS(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
ApS(utils.SetLastName, Of("Doe")),
|
||||
ApS(utils.SetGivenName, Of("John")),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(), result.Of("John Doe"))
|
||||
}
|
||||
32
v2/ioresult/bracket.go
Normal file
32
v2/ioresult/bracket.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||
// whether the body action returns and error or not.
|
||||
//
|
||||
//go:inline
|
||||
func Bracket[A, B, ANY any](
|
||||
acquire IOResult[A],
|
||||
use Kleisli[A, B],
|
||||
release func(A, Result[B]) IOResult[ANY],
|
||||
) IOResult[B] {
|
||||
return ioeither.Bracket(acquire, use, release)
|
||||
}
|
||||
18
v2/ioresult/doc.go
Normal file
18
v2/ioresult/doc.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
//go:generate go run .. ioeither --count 10 --filename gen.go
|
||||
36
v2/ioresult/eq.go
Normal file
36
v2/ioresult/eq.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
EQ "github.com/IBM/fp-go/v2/eq"
|
||||
"github.com/IBM/fp-go/v2/io"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IOResult monad
|
||||
//
|
||||
//go:inline
|
||||
func Eq[A any](eq EQ.Eq[Result[A]]) EQ.Eq[IOResult[A]] {
|
||||
return io.Eq(eq)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
//
|
||||
//go:inline
|
||||
func FromStrictEquals[A comparable]() EQ.Eq[IOResult[A]] {
|
||||
return Eq(result.FromStrictEquals[A]())
|
||||
}
|
||||
49
v2/ioresult/eq_test.go
Normal file
49
v2/ioresult/eq_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEq(t *testing.T) {
|
||||
|
||||
a := errors.New("a")
|
||||
b := errors.New("b")
|
||||
|
||||
r1 := Of(1)
|
||||
r2 := Of(1)
|
||||
r3 := Of(2)
|
||||
|
||||
e1 := Left[int](a)
|
||||
e2 := Left[int](a)
|
||||
e3 := Left[int](b)
|
||||
|
||||
eq := FromStrictEquals[int]()
|
||||
|
||||
assert.True(t, eq.Equals(r1, r1))
|
||||
assert.True(t, eq.Equals(r1, r2))
|
||||
assert.False(t, eq.Equals(r1, r3))
|
||||
assert.False(t, eq.Equals(r1, e1))
|
||||
|
||||
assert.True(t, eq.Equals(e1, e1))
|
||||
assert.True(t, eq.Equals(e1, e2))
|
||||
assert.False(t, eq.Equals(e1, e3))
|
||||
assert.False(t, eq.Equals(e2, r2))
|
||||
}
|
||||
57
v2/ioresult/examples_create_test.go
Normal file
57
v2/ioresult/examples_create_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
)
|
||||
|
||||
func ExampleIOEither_creation() {
|
||||
// Build an IOEither
|
||||
leftValue := Left[string](fmt.Errorf("some error"))
|
||||
rightValue := Right("value")
|
||||
|
||||
// Convert from Either
|
||||
eitherValue := result.Of(42)
|
||||
ioFromEither := FromEither(eitherValue)
|
||||
|
||||
// some predicate
|
||||
isEven := func(num int) (int, error) {
|
||||
if num%2 == 0 {
|
||||
return num, nil
|
||||
}
|
||||
return 0, fmt.Errorf("%d is an odd number", num)
|
||||
}
|
||||
fromEven := Eitherize1(isEven)
|
||||
leftFromPred := fromEven(3)
|
||||
rightFromPred := fromEven(4)
|
||||
|
||||
fmt.Println(leftValue())
|
||||
fmt.Println(rightValue())
|
||||
fmt.Println(ioFromEither())
|
||||
fmt.Println(leftFromPred())
|
||||
fmt.Println(rightFromPred())
|
||||
|
||||
// Output:
|
||||
// Left[*errors.errorString](some error)
|
||||
// Right[string](value)
|
||||
// Right[int](42)
|
||||
// Left[*errors.errorString](3 is an odd number)
|
||||
// Right[int](4)
|
||||
|
||||
}
|
||||
57
v2/ioresult/examples_do_test.go
Normal file
57
v2/ioresult/examples_do_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/io"
|
||||
T "github.com/IBM/fp-go/v2/tuple"
|
||||
)
|
||||
|
||||
func ExampleIOEither_do() {
|
||||
foo := Of("foo")
|
||||
bar := Of(1)
|
||||
|
||||
// quux consumes the state of three bindings and returns an [IO] instead of an [IOEither]
|
||||
quux := func(t T.Tuple3[string, int, string]) IO[any] {
|
||||
return io.FromImpure(func() {
|
||||
log.Printf("t1: %s, t2: %d, t3: %s", t.F1, t.F2, t.F3)
|
||||
})
|
||||
}
|
||||
|
||||
transform := func(t T.Tuple3[string, int, string]) int {
|
||||
return len(t.F1) + t.F2 + len(t.F3)
|
||||
}
|
||||
|
||||
b := F.Pipe5(
|
||||
foo,
|
||||
BindTo(T.Of[string]),
|
||||
ApS(T.Push1[string, int], bar),
|
||||
Bind(T.Push2[string, int, string], func(t T.Tuple2[string, int]) IOResult[string] {
|
||||
return Of(fmt.Sprintf("%s%d", t.F1, t.F2))
|
||||
}),
|
||||
ChainFirstIOK(quux),
|
||||
Map(transform),
|
||||
)
|
||||
|
||||
fmt.Println(b())
|
||||
|
||||
// Output:
|
||||
// Right[int](8)
|
||||
}
|
||||
45
v2/ioresult/examples_extract_test.go
Normal file
45
v2/ioresult/examples_extract_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/io"
|
||||
)
|
||||
|
||||
func ExampleIOEither_extraction() {
|
||||
// IOEither
|
||||
someIOEither := Right(42)
|
||||
eitherValue := someIOEither() // E.Right(42)
|
||||
value := E.GetOrElse(F.Constant1[error](0))(eitherValue) // 42
|
||||
|
||||
// Or more directly
|
||||
infaillibleIO := GetOrElse(F.Constant1[error](io.Of(0)))(someIOEither) // => io.Right(42)
|
||||
valueFromIO := infaillibleIO() // => 42
|
||||
|
||||
fmt.Println(eitherValue)
|
||||
fmt.Println(value)
|
||||
fmt.Println(valueFromIO)
|
||||
|
||||
// Output:
|
||||
// Right[int](42)
|
||||
// 42
|
||||
// 42
|
||||
|
||||
}
|
||||
25
v2/ioresult/exec/exec.go
Normal file
25
v2/ioresult/exec/exec.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
// Command executes a command
|
||||
Command = exec.Command
|
||||
)
|
||||
43
v2/ioresult/exec/exec_test.go
Normal file
43
v2/ioresult/exec/exec_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
RA "github.com/IBM/fp-go/v2/array"
|
||||
B "github.com/IBM/fp-go/v2/bytes"
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
"github.com/IBM/fp-go/v2/exec"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOpenSSL(t *testing.T) {
|
||||
// execute the openSSL binary
|
||||
version := F.Pipe1(
|
||||
Command("openssl")(RA.From("version"))(B.Monoid.Empty()),
|
||||
ioeither.Map[error](F.Flow3(
|
||||
exec.StdOut,
|
||||
B.ToString,
|
||||
strings.TrimSpace,
|
||||
)),
|
||||
)
|
||||
|
||||
assert.True(t, E.IsRight(version()))
|
||||
}
|
||||
36
v2/ioresult/file/dir.go
Normal file
36
v2/ioresult/file/dir.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/IBM/fp-go/v2/ioeither/file"
|
||||
)
|
||||
|
||||
// MkdirAll create a sequence of directories, see [os.MkdirAll]
|
||||
//
|
||||
//go:inline
|
||||
func MkdirAll(path string, perm os.FileMode) IOResult[string] {
|
||||
return file.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
// Mkdir create a directory, see [os.Mkdir]
|
||||
//
|
||||
//go:inline
|
||||
func Mkdir(path string, perm os.FileMode) IOResult[string] {
|
||||
return file.Mkdir(path, perm)
|
||||
}
|
||||
53
v2/ioresult/file/file.go
Normal file
53
v2/ioresult/file/file.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/IBM/fp-go/v2/ioeither/file"
|
||||
)
|
||||
|
||||
var (
|
||||
// Open opens a file for reading
|
||||
Open = file.Open
|
||||
// Create opens a file for writing
|
||||
Create = file.Create
|
||||
// ReadFile reads the context of a file
|
||||
ReadFile = file.ReadFile
|
||||
)
|
||||
|
||||
// WriteFile writes a data blob to a file
|
||||
//
|
||||
//go:inline
|
||||
func WriteFile(dstName string, perm os.FileMode) Kleisli[[]byte, []byte] {
|
||||
return file.WriteFile(dstName, perm)
|
||||
}
|
||||
|
||||
// Remove removes a file by name
|
||||
//
|
||||
//go:inline
|
||||
func Remove(name string) IOResult[string] {
|
||||
return file.Remove(name)
|
||||
}
|
||||
|
||||
// Close closes an object
|
||||
//
|
||||
//go:inline
|
||||
func Close[C io.Closer](c C) IOResult[any] {
|
||||
return file.Close(c)
|
||||
}
|
||||
29
v2/ioresult/file/readall.go
Normal file
29
v2/ioresult/file/readall.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/IBM/fp-go/v2/ioeither/file"
|
||||
)
|
||||
|
||||
// ReadAll uses a generator function to create a stream, reads it and closes it
|
||||
//
|
||||
//go:inline
|
||||
func ReadAll[R io.ReadCloser](acquire IOResult[R]) IOResult[[]byte] {
|
||||
return file.ReadAll(acquire)
|
||||
}
|
||||
34
v2/ioresult/file/tempfile.go
Normal file
34
v2/ioresult/file/tempfile.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/IBM/fp-go/v2/ioeither/file"
|
||||
)
|
||||
|
||||
var (
|
||||
// CreateTemp created a temp file with proper parametrization
|
||||
CreateTemp = file.CreateTemp
|
||||
)
|
||||
|
||||
// WithTempFile creates a temporary filthen invokes a callback to create a resource based on the filthen close and remove the temp file
|
||||
//
|
||||
//go:inline
|
||||
func WithTempFile[A any](f Kleisli[*os.File, A]) IOResult[A] {
|
||||
return file.WithTempFile(f)
|
||||
}
|
||||
9
v2/ioresult/file/types.go
Normal file
9
v2/ioresult/file/types.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package file
|
||||
|
||||
import "github.com/IBM/fp-go/v2/ioresult"
|
||||
|
||||
type (
|
||||
IOResult[T any] = ioresult.IOResult[T]
|
||||
Kleisli[A, B any] = ioresult.Kleisli[A, B]
|
||||
Operator[A, B any] = ioresult.Operator[A, B]
|
||||
)
|
||||
36
v2/ioresult/file/write.go
Normal file
36
v2/ioresult/file/write.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/IBM/fp-go/v2/ioeither/file"
|
||||
)
|
||||
|
||||
// WriteAll uses a generator function to create a stream, writes data to it and closes it
|
||||
//
|
||||
//go:inline
|
||||
func WriteAll[W io.WriteCloser](data []byte) Operator[W, []byte] {
|
||||
return file.WriteAll[W](data)
|
||||
}
|
||||
|
||||
// Write uses a generator function to create a stream, writes data to it and closes it
|
||||
//
|
||||
//go:inline
|
||||
func Write[R any, W io.WriteCloser](acquire IOResult[W]) Kleisli[Kleisli[W, R], R] {
|
||||
return file.Write[R](acquire)
|
||||
}
|
||||
989
v2/ioresult/gen.go
Normal file
989
v2/ioresult/gen.go
Normal file
@@ -0,0 +1,989 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2025-03-09 23:53:07.5974468 +0100 CET m=+0.008920801
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
"github.com/IBM/fp-go/v2/tuple"
|
||||
)
|
||||
|
||||
// Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize0[F ~func() (R, error), R any](f F) func() IOResult[R] {
|
||||
return ioeither.Eitherize0(f)
|
||||
}
|
||||
|
||||
// Uneitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize0[F ~func() IOResult[R], R any](f F) func() (R, error) {
|
||||
return ioeither.Uneitherize0(f)
|
||||
}
|
||||
|
||||
// Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize1[F ~func(T1) (R, error), T1, R any](f F) func(T1) IOResult[R] {
|
||||
return ioeither.Eitherize1(f)
|
||||
}
|
||||
|
||||
// Uneitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize1[F ~func(T1) IOResult[R], T1, R any](f F) func(T1) (R, error) {
|
||||
return ioeither.Uneitherize1(f)
|
||||
}
|
||||
|
||||
// SequenceT1 converts 1 [IOResult[T]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT1[T1 any](
|
||||
t1 IOResult[T1],
|
||||
) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.SequenceParT1(t1)
|
||||
}
|
||||
|
||||
// SequenceSeqT1 converts 1 [IOResult[T]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT1[T1 any](
|
||||
t1 IOResult[T1],
|
||||
) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.SequenceSeqT1(t1)
|
||||
}
|
||||
|
||||
// SequenceParT1 converts 1 [IOResult[T]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT1[T1 any](
|
||||
t1 IOResult[T1],
|
||||
) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.SequenceParT1(t1)
|
||||
}
|
||||
|
||||
// SequenceTuple1 converts a [tuple.Tuple1[IOResult[T]]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple1[T1 any](t tuple.Tuple1[IOResult[T1]]) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.SequenceTuple1(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple1 converts a [tuple.Tuple1[IOResult[T]]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple1[T1 any](t tuple.Tuple1[IOResult[T1]]) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.SequenceSeqTuple1(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple1 converts a [tuple.Tuple1[IOResult[T]]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple1[T1 any](t tuple.Tuple1[IOResult[T1]]) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.SequenceParTuple1(t)
|
||||
}
|
||||
|
||||
// TraverseTuple1 converts a [tuple.Tuple1[A1]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple1[F1 ~func(A1) IOResult[T1], T1, A1 any](f1 F1) func(tuple.Tuple1[A1]) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.TraverseTuple1(f1)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple1 converts a [tuple.Tuple1[A1]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple1[F1 ~func(A1) IOResult[T1], T1, A1 any](f1 F1) func(tuple.Tuple1[A1]) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.TraverseSeqTuple1(f1)
|
||||
}
|
||||
|
||||
// TraverseParTuple1 converts a [tuple.Tuple1[A1]] into a [IOResult[tuple.Tuple1[T1]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple1[F1 ~func(A1) IOResult[T1], T1, A1 any](f1 F1) func(tuple.Tuple1[A1]) IOResult[tuple.Tuple1[T1]] {
|
||||
return ioeither.TraverseParTuple1(f1)
|
||||
}
|
||||
|
||||
// Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize2[F ~func(T1, T2) (R, error), T1, T2, R any](f F) func(T1, T2) IOResult[R] {
|
||||
return ioeither.Eitherize2(f)
|
||||
}
|
||||
|
||||
// Uneitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize2[F ~func(T1, T2) IOResult[R], T1, T2, R any](f F) func(T1, T2) (R, error) {
|
||||
return ioeither.Uneitherize2(f)
|
||||
}
|
||||
|
||||
// SequenceT2 converts 2 [IOResult[T]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT2[T1, T2 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.SequenceT2(t1, t2)
|
||||
}
|
||||
|
||||
// SequenceSeqT2 converts 2 [IOResult[T]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT2[T1, T2 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.SequenceSeqT2(t1, t2)
|
||||
}
|
||||
|
||||
// SequenceParT2 converts 2 [IOResult[T]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT2[T1, T2 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.SequenceParT2(t1, t2)
|
||||
}
|
||||
|
||||
// SequenceTuple2 converts a [tuple.Tuple2[IOResult[T]]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple2[T1, T2 any](t tuple.Tuple2[IOResult[T1], IOResult[T2]]) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.SequenceTuple2(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple2 converts a [tuple.Tuple2[IOResult[T]]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple2[T1, T2 any](t tuple.Tuple2[IOResult[T1], IOResult[T2]]) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.SequenceSeqTuple2(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple2 converts a [tuple.Tuple2[IOResult[T]]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple2[T1, T2 any](t tuple.Tuple2[IOResult[T1], IOResult[T2]]) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.SequenceParTuple2(t)
|
||||
}
|
||||
|
||||
// TraverseTuple2 converts a [tuple.Tuple2[A1, A2]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple2[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], T1, T2, A1, A2 any](f1 F1, f2 F2) func(tuple.Tuple2[A1, A2]) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.TraverseTuple2(f1, f2)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple2 converts a [tuple.Tuple2[A1, A2]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple2[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], T1, T2, A1, A2 any](f1 F1, f2 F2) func(tuple.Tuple2[A1, A2]) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.TraverseSeqTuple2(f1, f2)
|
||||
}
|
||||
|
||||
// TraverseParTuple2 converts a [tuple.Tuple2[A1, A2]] into a [IOResult[tuple.Tuple2[T1, T2]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple2[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], T1, T2, A1, A2 any](f1 F1, f2 F2) func(tuple.Tuple2[A1, A2]) IOResult[tuple.Tuple2[T1, T2]] {
|
||||
return ioeither.TraverseParTuple2(f1, f2)
|
||||
}
|
||||
|
||||
// Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize3[F ~func(T1, T2, T3) (R, error), T1, T2, T3, R any](f F) func(T1, T2, T3) IOResult[R] {
|
||||
return ioeither.Eitherize3(f)
|
||||
}
|
||||
|
||||
// Uneitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize3[F ~func(T1, T2, T3) IOResult[R], T1, T2, T3, R any](f F) func(T1, T2, T3) (R, error) {
|
||||
return ioeither.Uneitherize3(f)
|
||||
}
|
||||
|
||||
// SequenceT3 converts 3 [IOResult[T]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT3[T1, T2, T3 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.SequenceT3(t1, t2, t3)
|
||||
}
|
||||
|
||||
// SequenceSeqT3 converts 3 [IOResult[T]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT3[T1, T2, T3 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.SequenceSeqT3(t1, t2, t3)
|
||||
}
|
||||
|
||||
// SequenceParT3 converts 3 [IOResult[T]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT3[T1, T2, T3 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.SequenceParT3(t1, t2, t3)
|
||||
}
|
||||
|
||||
// SequenceTuple3 converts a [tuple.Tuple3[IOResult[T]]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple3[T1, T2, T3 any](t tuple.Tuple3[IOResult[T1], IOResult[T2], IOResult[T3]]) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.SequenceTuple3(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple3 converts a [tuple.Tuple3[IOResult[T]]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple3[T1, T2, T3 any](t tuple.Tuple3[IOResult[T1], IOResult[T2], IOResult[T3]]) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.SequenceSeqTuple3(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple3 converts a [tuple.Tuple3[IOResult[T]]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple3[T1, T2, T3 any](t tuple.Tuple3[IOResult[T1], IOResult[T2], IOResult[T3]]) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.SequenceParTuple3(t)
|
||||
}
|
||||
|
||||
// TraverseTuple3 converts a [tuple.Tuple3[A1, A2, A3]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple3[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], T1, T2, T3, A1, A2, A3 any](f1 F1, f2 F2, f3 F3) func(tuple.Tuple3[A1, A2, A3]) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.TraverseTuple3(f1, f2, f3)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple3 converts a [tuple.Tuple3[A1, A2, A3]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple3[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], T1, T2, T3, A1, A2, A3 any](f1 F1, f2 F2, f3 F3) func(tuple.Tuple3[A1, A2, A3]) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.TraverseSeqTuple3(f1, f2, f3)
|
||||
}
|
||||
|
||||
// TraverseParTuple3 converts a [tuple.Tuple3[A1, A2, A3]] into a [IOResult[tuple.Tuple3[T1, T2, T3]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple3[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], T1, T2, T3, A1, A2, A3 any](f1 F1, f2 F2, f3 F3) func(tuple.Tuple3[A1, A2, A3]) IOResult[tuple.Tuple3[T1, T2, T3]] {
|
||||
return ioeither.TraverseParTuple3(f1, f2, f3)
|
||||
}
|
||||
|
||||
// Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize4[F ~func(T1, T2, T3, T4) (R, error), T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) IOResult[R] {
|
||||
return ioeither.Eitherize4(f)
|
||||
}
|
||||
|
||||
// Uneitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize4[F ~func(T1, T2, T3, T4) IOResult[R], T1, T2, T3, T4, R any](f F) func(T1, T2, T3, T4) (R, error) {
|
||||
return ioeither.Uneitherize4(f)
|
||||
}
|
||||
|
||||
// SequenceT4 converts 4 [IOResult[T]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT4[T1, T2, T3, T4 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.SequenceT4(t1, t2, t3, t4)
|
||||
}
|
||||
|
||||
// SequenceSeqT4 converts 4 [IOResult[T]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT4[T1, T2, T3, T4 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.SequenceSeqT4(t1, t2, t3, t4)
|
||||
}
|
||||
|
||||
// SequenceParT4 converts 4 [IOResult[T]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT4[T1, T2, T3, T4 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.SequenceParT4(t1, t2, t3, t4)
|
||||
}
|
||||
|
||||
// SequenceTuple4 converts a [tuple.Tuple4[IOResult[T]]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple4[T1, T2, T3, T4 any](t tuple.Tuple4[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4]]) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.SequenceTuple4(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple4 converts a [tuple.Tuple4[IOResult[T]]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple4[T1, T2, T3, T4 any](t tuple.Tuple4[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4]]) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.SequenceSeqTuple4(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple4 converts a [tuple.Tuple4[IOResult[T]]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple4[T1, T2, T3, T4 any](t tuple.Tuple4[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4]]) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.SequenceParTuple4(t)
|
||||
}
|
||||
|
||||
// TraverseTuple4 converts a [tuple.Tuple4[A1, A2, A3, A4]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple4[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], T1, T2, T3, T4, A1, A2, A3, A4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(tuple.Tuple4[A1, A2, A3, A4]) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.TraverseTuple4(f1, f2, f3, f4)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple4 converts a [tuple.Tuple4[A1, A2, A3, A4]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple4[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], T1, T2, T3, T4, A1, A2, A3, A4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(tuple.Tuple4[A1, A2, A3, A4]) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.TraverseSeqTuple4(f1, f2, f3, f4)
|
||||
}
|
||||
|
||||
// TraverseParTuple4 converts a [tuple.Tuple4[A1, A2, A3, A4]] into a [IOResult[tuple.Tuple4[T1, T2, T3, T4]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple4[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], T1, T2, T3, T4, A1, A2, A3, A4 any](f1 F1, f2 F2, f3 F3, f4 F4) func(tuple.Tuple4[A1, A2, A3, A4]) IOResult[tuple.Tuple4[T1, T2, T3, T4]] {
|
||||
return ioeither.TraverseParTuple4(f1, f2, f3, f4)
|
||||
}
|
||||
|
||||
// Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize5[F ~func(T1, T2, T3, T4, T5) (R, error), T1, T2, T3, T4, T5, R any](f F) func(T1, T2, T3, T4, T5) IOResult[R] {
|
||||
return ioeither.Eitherize5(f)
|
||||
}
|
||||
|
||||
// Uneitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize5[F ~func(T1, T2, T3, T4, T5) IOResult[R], T1, T2, T3, T4, T5, R any](f F) func(T1, T2, T3, T4, T5) (R, error) {
|
||||
return ioeither.Uneitherize5(f)
|
||||
}
|
||||
|
||||
// SequenceT5 converts 5 [IOResult[T]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT5[T1, T2, T3, T4, T5 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.SequenceT5(t1, t2, t3, t4, t5)
|
||||
}
|
||||
|
||||
// SequenceSeqT5 converts 5 [IOResult[T]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT5[T1, T2, T3, T4, T5 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.SequenceSeqT5(t1, t2, t3, t4, t5)
|
||||
}
|
||||
|
||||
// SequenceParT5 converts 5 [IOResult[T]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT5[T1, T2, T3, T4, T5 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.SequenceParT5(t1, t2, t3, t4, t5)
|
||||
}
|
||||
|
||||
// SequenceTuple5 converts a [tuple.Tuple5[IOResult[T]]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple5[T1, T2, T3, T4, T5 any](t tuple.Tuple5[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5]]) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.SequenceTuple5(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple5 converts a [tuple.Tuple5[IOResult[T]]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple5[T1, T2, T3, T4, T5 any](t tuple.Tuple5[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5]]) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.SequenceSeqTuple5(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple5 converts a [tuple.Tuple5[IOResult[T]]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple5[T1, T2, T3, T4, T5 any](t tuple.Tuple5[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5]]) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.SequenceParTuple5(t)
|
||||
}
|
||||
|
||||
// TraverseTuple5 converts a [tuple.Tuple5[A1, A2, A3, A4, A5]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple5[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], T1, T2, T3, T4, T5, A1, A2, A3, A4, A5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(tuple.Tuple5[A1, A2, A3, A4, A5]) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.TraverseTuple5(f1, f2, f3, f4, f5)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple5 converts a [tuple.Tuple5[A1, A2, A3, A4, A5]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple5[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], T1, T2, T3, T4, T5, A1, A2, A3, A4, A5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(tuple.Tuple5[A1, A2, A3, A4, A5]) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.TraverseSeqTuple5(f1, f2, f3, f4, f5)
|
||||
}
|
||||
|
||||
// TraverseParTuple5 converts a [tuple.Tuple5[A1, A2, A3, A4, A5]] into a [IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple5[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], T1, T2, T3, T4, T5, A1, A2, A3, A4, A5 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5) func(tuple.Tuple5[A1, A2, A3, A4, A5]) IOResult[tuple.Tuple5[T1, T2, T3, T4, T5]] {
|
||||
return ioeither.TraverseParTuple5(f1, f2, f3, f4, f5)
|
||||
}
|
||||
|
||||
// Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize6[F ~func(T1, T2, T3, T4, T5, T6) (R, error), T1, T2, T3, T4, T5, T6, R any](f F) func(T1, T2, T3, T4, T5, T6) IOResult[R] {
|
||||
return ioeither.Eitherize6(f)
|
||||
}
|
||||
|
||||
// Uneitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize6[F ~func(T1, T2, T3, T4, T5, T6) IOResult[R], T1, T2, T3, T4, T5, T6, R any](f F) func(T1, T2, T3, T4, T5, T6) (R, error) {
|
||||
return ioeither.Uneitherize6(f)
|
||||
}
|
||||
|
||||
// SequenceT6 converts 6 [IOResult[T]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT6[T1, T2, T3, T4, T5, T6 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.SequenceT6(t1, t2, t3, t4, t5, t6)
|
||||
}
|
||||
|
||||
// SequenceSeqT6 converts 6 [IOResult[T]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT6[T1, T2, T3, T4, T5, T6 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.SequenceSeqT6(t1, t2, t3, t4, t5, t6)
|
||||
}
|
||||
|
||||
// SequenceParT6 converts 6 [IOResult[T]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT6[T1, T2, T3, T4, T5, T6 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.SequenceParT6(t1, t2, t3, t4, t5, t6)
|
||||
}
|
||||
|
||||
// SequenceTuple6 converts a [tuple.Tuple6[IOResult[T]]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple6[T1, T2, T3, T4, T5, T6 any](t tuple.Tuple6[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6]]) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.SequenceTuple6(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple6 converts a [tuple.Tuple6[IOResult[T]]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple6[T1, T2, T3, T4, T5, T6 any](t tuple.Tuple6[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6]]) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.SequenceSeqTuple6(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple6 converts a [tuple.Tuple6[IOResult[T]]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple6[T1, T2, T3, T4, T5, T6 any](t tuple.Tuple6[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6]]) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.SequenceParTuple6(t)
|
||||
}
|
||||
|
||||
// TraverseTuple6 converts a [tuple.Tuple6[A1, A2, A3, A4, A5, A6]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple6[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], T1, T2, T3, T4, T5, T6, A1, A2, A3, A4, A5, A6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(tuple.Tuple6[A1, A2, A3, A4, A5, A6]) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.TraverseTuple6(f1, f2, f3, f4, f5, f6)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple6 converts a [tuple.Tuple6[A1, A2, A3, A4, A5, A6]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple6[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], T1, T2, T3, T4, T5, T6, A1, A2, A3, A4, A5, A6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(tuple.Tuple6[A1, A2, A3, A4, A5, A6]) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.TraverseSeqTuple6(f1, f2, f3, f4, f5, f6)
|
||||
}
|
||||
|
||||
// TraverseParTuple6 converts a [tuple.Tuple6[A1, A2, A3, A4, A5, A6]] into a [IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple6[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], T1, T2, T3, T4, T5, T6, A1, A2, A3, A4, A5, A6 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6) func(tuple.Tuple6[A1, A2, A3, A4, A5, A6]) IOResult[tuple.Tuple6[T1, T2, T3, T4, T5, T6]] {
|
||||
return ioeither.TraverseParTuple6(f1, f2, f3, f4, f5, f6)
|
||||
}
|
||||
|
||||
// Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize7[F ~func(T1, T2, T3, T4, T5, T6, T7) (R, error), T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T1, T2, T3, T4, T5, T6, T7) IOResult[R] {
|
||||
return ioeither.Eitherize7(f)
|
||||
}
|
||||
|
||||
// Uneitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize7[F ~func(T1, T2, T3, T4, T5, T6, T7) IOResult[R], T1, T2, T3, T4, T5, T6, T7, R any](f F) func(T1, T2, T3, T4, T5, T6, T7) (R, error) {
|
||||
return ioeither.Uneitherize7(f)
|
||||
}
|
||||
|
||||
// SequenceT7 converts 7 [IOResult[T]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT7[T1, T2, T3, T4, T5, T6, T7 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.SequenceT7(t1, t2, t3, t4, t5, t6, t7)
|
||||
}
|
||||
|
||||
// SequenceSeqT7 converts 7 [IOResult[T]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT7[T1, T2, T3, T4, T5, T6, T7 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.SequenceSeqT7(t1, t2, t3, t4, t5, t6, t7)
|
||||
}
|
||||
|
||||
// SequenceParT7 converts 7 [IOResult[T]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT7[T1, T2, T3, T4, T5, T6, T7 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.SequenceParT7(t1, t2, t3, t4, t5, t6, t7)
|
||||
}
|
||||
|
||||
// SequenceTuple7 converts a [tuple.Tuple7[IOResult[T]]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple7[T1, T2, T3, T4, T5, T6, T7 any](t tuple.Tuple7[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7]]) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.SequenceTuple7(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple7 converts a [tuple.Tuple7[IOResult[T]]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple7[T1, T2, T3, T4, T5, T6, T7 any](t tuple.Tuple7[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7]]) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.SequenceSeqTuple7(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple7 converts a [tuple.Tuple7[IOResult[T]]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple7[T1, T2, T3, T4, T5, T6, T7 any](t tuple.Tuple7[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7]]) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.SequenceParTuple7(t)
|
||||
}
|
||||
|
||||
// TraverseTuple7 converts a [tuple.Tuple7[A1, A2, A3, A4, A5, A6, A7]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple7[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], T1, T2, T3, T4, T5, T6, T7, A1, A2, A3, A4, A5, A6, A7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(tuple.Tuple7[A1, A2, A3, A4, A5, A6, A7]) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.TraverseTuple7(f1, f2, f3, f4, f5, f6, f7)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple7 converts a [tuple.Tuple7[A1, A2, A3, A4, A5, A6, A7]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple7[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], T1, T2, T3, T4, T5, T6, T7, A1, A2, A3, A4, A5, A6, A7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(tuple.Tuple7[A1, A2, A3, A4, A5, A6, A7]) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.TraverseSeqTuple7(f1, f2, f3, f4, f5, f6, f7)
|
||||
}
|
||||
|
||||
// TraverseParTuple7 converts a [tuple.Tuple7[A1, A2, A3, A4, A5, A6, A7]] into a [IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple7[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], T1, T2, T3, T4, T5, T6, T7, A1, A2, A3, A4, A5, A6, A7 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7) func(tuple.Tuple7[A1, A2, A3, A4, A5, A6, A7]) IOResult[tuple.Tuple7[T1, T2, T3, T4, T5, T6, T7]] {
|
||||
return ioeither.TraverseParTuple7(f1, f2, f3, f4, f5, f6, f7)
|
||||
}
|
||||
|
||||
// Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize8[F ~func(T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8) IOResult[R] {
|
||||
return ioeither.Eitherize8(f)
|
||||
}
|
||||
|
||||
// Uneitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize8[F ~func(T1, T2, T3, T4, T5, T6, T7, T8) IOResult[R], T1, T2, T3, T4, T5, T6, T7, T8, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8) (R, error) {
|
||||
return ioeither.Uneitherize8(f)
|
||||
}
|
||||
|
||||
// SequenceT8 converts 8 [IOResult[T]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT8[T1, T2, T3, T4, T5, T6, T7, T8 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.SequenceT8(t1, t2, t3, t4, t5, t6, t7, t8)
|
||||
}
|
||||
|
||||
// SequenceSeqT8 converts 8 [IOResult[T]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT8[T1, T2, T3, T4, T5, T6, T7, T8 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.SequenceSeqT8(t1, t2, t3, t4, t5, t6, t7, t8)
|
||||
}
|
||||
|
||||
// SequenceParT8 converts 8 [IOResult[T]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT8[T1, T2, T3, T4, T5, T6, T7, T8 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.SequenceParT8(t1, t2, t3, t4, t5, t6, t7, t8)
|
||||
}
|
||||
|
||||
// SequenceTuple8 converts a [tuple.Tuple8[IOResult[T]]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t tuple.Tuple8[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8]]) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.SequenceTuple8(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple8 converts a [tuple.Tuple8[IOResult[T]]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t tuple.Tuple8[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8]]) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.SequenceSeqTuple8(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple8 converts a [tuple.Tuple8[IOResult[T]]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple8[T1, T2, T3, T4, T5, T6, T7, T8 any](t tuple.Tuple8[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8]]) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.SequenceParTuple8(t)
|
||||
}
|
||||
|
||||
// TraverseTuple8 converts a [tuple.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple8[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], T1, T2, T3, T4, T5, T6, T7, T8, A1, A2, A3, A4, A5, A6, A7, A8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(tuple.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.TraverseTuple8(f1, f2, f3, f4, f5, f6, f7, f8)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple8 converts a [tuple.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple8[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], T1, T2, T3, T4, T5, T6, T7, T8, A1, A2, A3, A4, A5, A6, A7, A8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(tuple.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.TraverseSeqTuple8(f1, f2, f3, f4, f5, f6, f7, f8)
|
||||
}
|
||||
|
||||
// TraverseParTuple8 converts a [tuple.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]] into a [IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple8[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], T1, T2, T3, T4, T5, T6, T7, T8, A1, A2, A3, A4, A5, A6, A7, A8 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8) func(tuple.Tuple8[A1, A2, A3, A4, A5, A6, A7, A8]) IOResult[tuple.Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]] {
|
||||
return ioeither.TraverseParTuple8(f1, f2, f3, f4, f5, f6, f7, f8)
|
||||
}
|
||||
|
||||
// Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize9[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOResult[R] {
|
||||
return ioeither.Eitherize9(f)
|
||||
}
|
||||
|
||||
// Uneitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize9[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9) IOResult[R], T1, T2, T3, T4, T5, T6, T7, T8, T9, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) {
|
||||
return ioeither.Uneitherize9(f)
|
||||
}
|
||||
|
||||
// SequenceT9 converts 9 [IOResult[T]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
t9 IOResult[T9],
|
||||
) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.SequenceT9(t1, t2, t3, t4, t5, t6, t7, t8, t9)
|
||||
}
|
||||
|
||||
// SequenceSeqT9 converts 9 [IOResult[T]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
t9 IOResult[T9],
|
||||
) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.SequenceSeqT9(t1, t2, t3, t4, t5, t6, t7, t8, t9)
|
||||
}
|
||||
|
||||
// SequenceParT9 converts 9 [IOResult[T]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
t9 IOResult[T9],
|
||||
) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.SequenceParT9(t1, t2, t3, t4, t5, t6, t7, t8, t9)
|
||||
}
|
||||
|
||||
// SequenceTuple9 converts a [tuple.Tuple9[IOResult[T]]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t tuple.Tuple9[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8], IOResult[T9]]) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.SequenceTuple9(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple9 converts a [tuple.Tuple9[IOResult[T]]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t tuple.Tuple9[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8], IOResult[T9]]) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.SequenceSeqTuple9(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple9 converts a [tuple.Tuple9[IOResult[T]]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9 any](t tuple.Tuple9[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8], IOResult[T9]]) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.SequenceParTuple9(t)
|
||||
}
|
||||
|
||||
// TraverseTuple9 converts a [tuple.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple9[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], F9 ~func(A9) IOResult[T9], T1, T2, T3, T4, T5, T6, T7, T8, T9, A1, A2, A3, A4, A5, A6, A7, A8, A9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(tuple.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.TraverseTuple9(f1, f2, f3, f4, f5, f6, f7, f8, f9)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple9 converts a [tuple.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple9[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], F9 ~func(A9) IOResult[T9], T1, T2, T3, T4, T5, T6, T7, T8, T9, A1, A2, A3, A4, A5, A6, A7, A8, A9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(tuple.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.TraverseSeqTuple9(f1, f2, f3, f4, f5, f6, f7, f8, f9)
|
||||
}
|
||||
|
||||
// TraverseParTuple9 converts a [tuple.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]] into a [IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple9[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], F9 ~func(A9) IOResult[T9], T1, T2, T3, T4, T5, T6, T7, T8, T9, A1, A2, A3, A4, A5, A6, A7, A8, A9 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9) func(tuple.Tuple9[A1, A2, A3, A4, A5, A6, A7, A8, A9]) IOResult[tuple.Tuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9]] {
|
||||
return ioeither.TraverseParTuple9(f1, f2, f3, f4, f5, f6, f7, f8, f9)
|
||||
}
|
||||
|
||||
// Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize10[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error), T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOResult[R] {
|
||||
return ioeither.Eitherize10(f)
|
||||
}
|
||||
|
||||
// Uneitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [IOResult[R]]
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize10[F ~func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) IOResult[R], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R any](f F) func(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) (R, error) {
|
||||
return ioeither.Uneitherize10(f)
|
||||
}
|
||||
|
||||
// SequenceT10 converts 10 [IOResult[T]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
t9 IOResult[T9],
|
||||
t10 IOResult[T10],
|
||||
) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.SequenceT10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)
|
||||
}
|
||||
|
||||
// SequenceSeqT10 converts 10 [IOResult[T]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
t9 IOResult[T9],
|
||||
t10 IOResult[T10],
|
||||
) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.SequenceSeqT10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)
|
||||
}
|
||||
|
||||
// SequenceParT10 converts 10 [IOResult[T]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParT10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](
|
||||
t1 IOResult[T1],
|
||||
t2 IOResult[T2],
|
||||
t3 IOResult[T3],
|
||||
t4 IOResult[T4],
|
||||
t5 IOResult[T5],
|
||||
t6 IOResult[T6],
|
||||
t7 IOResult[T7],
|
||||
t8 IOResult[T8],
|
||||
t9 IOResult[T9],
|
||||
t10 IOResult[T10],
|
||||
) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.SequenceParT10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)
|
||||
}
|
||||
|
||||
// SequenceTuple10 converts a [tuple.Tuple10[IOResult[T]]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t tuple.Tuple10[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8], IOResult[T9], IOResult[T10]]) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.SequenceTuple10(t)
|
||||
}
|
||||
|
||||
// SequenceSeqTuple10 converts a [tuple.Tuple10[IOResult[T]]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceSeqTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t tuple.Tuple10[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8], IOResult[T9], IOResult[T10]]) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.SequenceSeqTuple10(t)
|
||||
}
|
||||
|
||||
// SequenceParTuple10 converts a [tuple.Tuple10[IOResult[T]]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func SequenceParTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 any](t tuple.Tuple10[IOResult[T1], IOResult[T2], IOResult[T3], IOResult[T4], IOResult[T5], IOResult[T6], IOResult[T7], IOResult[T8], IOResult[T9], IOResult[T10]]) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.SequenceParTuple10(t)
|
||||
}
|
||||
|
||||
// TraverseTuple10 converts a [tuple.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseTuple10[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], F9 ~func(A9) IOResult[T9], F10 ~func(A10) IOResult[T10], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(tuple.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.TraverseTuple10(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)
|
||||
}
|
||||
|
||||
// TraverseSeqTuple10 converts a [tuple.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseSeqTuple10[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], F9 ~func(A9) IOResult[T9], F10 ~func(A10) IOResult[T10], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(tuple.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.TraverseSeqTuple10(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)
|
||||
}
|
||||
|
||||
// TraverseParTuple10 converts a [tuple.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]] into a [IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]]]
|
||||
//
|
||||
//go:inline
|
||||
func TraverseParTuple10[F1 ~func(A1) IOResult[T1], F2 ~func(A2) IOResult[T2], F3 ~func(A3) IOResult[T3], F4 ~func(A4) IOResult[T4], F5 ~func(A5) IOResult[T5], F6 ~func(A6) IOResult[T6], F7 ~func(A7) IOResult[T7], F8 ~func(A8) IOResult[T8], F9 ~func(A9) IOResult[T9], F10 ~func(A10) IOResult[T10], T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10 any](f1 F1, f2 F2, f3 F3, f4 F4, f5 F5, f6 F6, f7 F7, f8 F8, f9 F9, f10 F10) func(tuple.Tuple10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10]) IOResult[tuple.Tuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]] {
|
||||
return ioeither.TraverseParTuple10(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)
|
||||
}
|
||||
27
v2/ioresult/http/builder/builder.go
Normal file
27
v2/ioresult/http/builder/builder.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
R "github.com/IBM/fp-go/v2/http/builder"
|
||||
IOEHB "github.com/IBM/fp-go/v2/ioeither/http/builder"
|
||||
IORH "github.com/IBM/fp-go/v2/ioresult/http"
|
||||
)
|
||||
|
||||
//go:inline
|
||||
func Requester(builder *R.Builder) IORH.Requester {
|
||||
return IOEHB.Requester(builder)
|
||||
}
|
||||
58
v2/ioresult/http/builder/builder_test.go
Normal file
58
v2/ioresult/http/builder/builder_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
R "github.com/IBM/fp-go/v2/http/builder"
|
||||
"github.com/IBM/fp-go/v2/io"
|
||||
"github.com/IBM/fp-go/v2/ioresult"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuilderWithQuery(t *testing.T) {
|
||||
// add some query
|
||||
withLimit := R.WithQueryArg("limit")("10")
|
||||
withURL := R.WithURL("http://www.example.org?a=b")
|
||||
|
||||
b := F.Pipe2(
|
||||
R.Default,
|
||||
withLimit,
|
||||
withURL,
|
||||
)
|
||||
|
||||
req := F.Pipe3(
|
||||
b,
|
||||
Requester,
|
||||
ioresult.Map(func(r *http.Request) *url.URL {
|
||||
return r.URL
|
||||
}),
|
||||
ioresult.ChainFirstIOK(func(u *url.URL) io.IO[any] {
|
||||
return io.FromImpure(func() {
|
||||
q := u.Query()
|
||||
assert.Equal(t, "10", q.Get("limit"))
|
||||
assert.Equal(t, "b", q.Get("a"))
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
assert.True(t, E.IsRight(req()))
|
||||
}
|
||||
24
v2/ioresult/http/builder/types.go
Normal file
24
v2/ioresult/http/builder/types.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package builder
|
||||
|
||||
import "github.com/IBM/fp-go/v2/ioresult"
|
||||
|
||||
type (
|
||||
IOResult[T any] = ioresult.IOResult[T]
|
||||
Kleisli[A, B any] = ioresult.Kleisli[A, B]
|
||||
Operator[A, B any] = ioresult.Operator[A, B]
|
||||
)
|
||||
28
v2/ioresult/http/di/di.go
Normal file
28
v2/ioresult/http/di/di.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package di
|
||||
|
||||
import (
|
||||
IOEHDI "github.com/IBM/fp-go/v2/ioeither/http/di"
|
||||
)
|
||||
|
||||
var (
|
||||
// InjHttpClient is the [DI.InjectionToken] for the [http.DefaultClient]
|
||||
InjHttpClient = IOEHDI.InjHttpClient
|
||||
|
||||
// InjClient is the [DI.InjectionToken] for the default [IOEH.Client]
|
||||
InjClient = IOEHDI.InjClient
|
||||
)
|
||||
78
v2/ioresult/http/request.go
Normal file
78
v2/ioresult/http/request.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
H "github.com/IBM/fp-go/v2/http"
|
||||
IOEH "github.com/IBM/fp-go/v2/ioeither/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Requester is a reader that constructs a request
|
||||
Requester = IOEH.Requester
|
||||
|
||||
Client = IOEH.Client
|
||||
)
|
||||
|
||||
var (
|
||||
// MakeRequest is an eitherized version of [http.NewRequest]
|
||||
MakeRequest = IOEH.MakeRequest
|
||||
|
||||
// specialize
|
||||
MakeGetRequest = IOEH.MakeGetRequest
|
||||
)
|
||||
|
||||
// MakeBodyRequest creates a request that carries a body
|
||||
//
|
||||
//go:inline
|
||||
func MakeBodyRequest(method string, body IOResult[[]byte]) Kleisli[string, *http.Request] {
|
||||
return IOEH.MakeBodyRequest(method, body)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MakeClient(httpClient *http.Client) Client {
|
||||
return IOEH.MakeClient(httpClient)
|
||||
}
|
||||
|
||||
// ReadFullResponse sends a request, reads the response as a byte array and represents the result as a tuple
|
||||
//
|
||||
//go:inline
|
||||
func ReadFullResponse(client Client) Kleisli[Requester, H.FullResponse] {
|
||||
return IOEH.ReadFullResponse(client)
|
||||
}
|
||||
|
||||
// ReadAll sends a request and reads the response as bytes
|
||||
//
|
||||
//go:inline
|
||||
func ReadAll(client Client) Kleisli[Requester, []byte] {
|
||||
return IOEH.ReadAll(client)
|
||||
}
|
||||
|
||||
// ReadText sends a request, reads the response and represents the response as a text string
|
||||
//
|
||||
//go:inline
|
||||
func ReadText(client Client) Kleisli[Requester, string] {
|
||||
return IOEH.ReadText(client)
|
||||
}
|
||||
|
||||
// ReadJSON sends a request, reads the response and parses the response as JSON
|
||||
//
|
||||
//go:inline
|
||||
func ReadJSON[A any](client Client) Kleisli[Requester, A] {
|
||||
return IOEH.ReadJSON[A](client)
|
||||
}
|
||||
71
v2/ioresult/http/retry_test.go
Normal file
71
v2/ioresult/http/retry_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
AR "github.com/IBM/fp-go/v2/array"
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
"github.com/IBM/fp-go/v2/errors"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
R "github.com/IBM/fp-go/v2/retry"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var expLogBackoff = R.ExponentialBackoff(250 * time.Millisecond)
|
||||
|
||||
// our retry policy with a 1s cap
|
||||
var testLogPolicy = R.CapDelay(
|
||||
2*time.Second,
|
||||
R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)),
|
||||
)
|
||||
|
||||
type PostItem struct {
|
||||
UserID uint `json:"userId"`
|
||||
Id uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
func TestRetryHttp(t *testing.T) {
|
||||
// URLs to try, the first URLs have an invalid hostname
|
||||
urls := AR.From("https://jsonplaceholder1.typicode.com/posts/1", "https://jsonplaceholder2.typicode.com/posts/1", "https://jsonplaceholder3.typicode.com/posts/1", "https://jsonplaceholder4.typicode.com/posts/1", "https://jsonplaceholder.typicode.com/posts/1")
|
||||
client := MakeClient(&http.Client{})
|
||||
|
||||
action := func(status R.RetryStatus) IOResult[*PostItem] {
|
||||
return F.Pipe1(
|
||||
MakeGetRequest(urls[status.IterNumber]),
|
||||
ReadJSON[*PostItem](client),
|
||||
)
|
||||
}
|
||||
|
||||
check := E.Fold(
|
||||
F.Flow2(
|
||||
errors.As[*net.DNSError](),
|
||||
O.IsSome[*net.DNSError],
|
||||
),
|
||||
F.Constant1[*PostItem](false),
|
||||
)
|
||||
|
||||
item := ioeither.Retrying(testLogPolicy, action, check)()
|
||||
assert.True(t, E.IsRight(item))
|
||||
}
|
||||
9
v2/ioresult/http/types.go
Normal file
9
v2/ioresult/http/types.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package http
|
||||
|
||||
import "github.com/IBM/fp-go/v2/ioresult"
|
||||
|
||||
type (
|
||||
IOResult[T any] = ioresult.IOResult[T]
|
||||
Kleisli[A, B any] = ioresult.Kleisli[A, B]
|
||||
Operator[A, B any] = ioresult.Operator[A, B]
|
||||
)
|
||||
385
v2/ioresult/ioeither.go
Normal file
385
v2/ioresult/ioeither.go
Normal file
@@ -0,0 +1,385 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
IOO "github.com/IBM/fp-go/v2/iooption"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
)
|
||||
|
||||
//go:inline
|
||||
func Left[A any](l error) IOResult[A] {
|
||||
return ioeither.Left[A](l)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func Right[A any](r A) IOResult[A] {
|
||||
return ioeither.Right[error](r)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func Of[A any](r A) IOResult[A] {
|
||||
return ioeither.Of[error](r)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadOf[A any](r A) IOResult[A] {
|
||||
return ioeither.MonadOf[error](r)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func LeftIO[A any](ml IO[error]) IOResult[A] {
|
||||
return ioeither.LeftIO[A](ml)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func RightIO[A any](mr IO[A]) IOResult[A] {
|
||||
return ioeither.RightIO[error](mr)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func FromEither[A any](e Result[A]) IOResult[A] {
|
||||
return ioeither.FromEither(e)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func FromResult[A any](e Result[A]) IOResult[A] {
|
||||
return ioeither.FromEither(e)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func FromOption[A any](onNone func() error) func(o O.Option[A]) IOResult[A] {
|
||||
return ioeither.FromOption[A](onNone)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func FromIOOption[A any](onNone func() error) func(o IOO.IOOption[A]) IOResult[A] {
|
||||
return ioeither.FromIOOption[A](onNone)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func ChainOptionK[A, B any](onNone func() error) func(func(A) O.Option[B]) Operator[A, B] {
|
||||
return ioeither.ChainOptionK[A, B](onNone)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadChainIOK[A, B any](ma IOResult[A], f func(A) IO[B]) IOResult[B] {
|
||||
return ioeither.MonadChainIOK(ma, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func ChainIOK[A, B any](f func(A) IO[B]) Operator[A, B] {
|
||||
return ioeither.ChainIOK[error](f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func ChainLazyK[A, B any](f func(A) Lazy[B]) Operator[A, B] {
|
||||
return ioeither.ChainLazyK[error](f)
|
||||
}
|
||||
|
||||
// FromIO creates an [IOResult] from an [IO] instancinvoking [IO] for each invocation of [IOResult]
|
||||
//
|
||||
//go:inline
|
||||
func FromIO[A any](mr IO[A]) IOResult[A] {
|
||||
return ioeither.FromIO[error](mr)
|
||||
}
|
||||
|
||||
// FromLazy creates an [IOResult] from a [Lazy] instancinvoking [Lazy] for each invocation of [IOResult]
|
||||
//
|
||||
//go:inline
|
||||
func FromLazy[A any](mr Lazy[A]) IOResult[A] {
|
||||
return ioeither.FromLazy[error](mr)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadMap[A, B any](fa IOResult[A], f func(A) B) IOResult[B] {
|
||||
return ioeither.MonadMap(fa, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func Map[A, B any](f func(A) B) Operator[A, B] {
|
||||
return ioeither.Map[error](f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadMapTo[A, B any](fa IOResult[A], b B) IOResult[B] {
|
||||
return ioeither.MonadMapTo(fa, b)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MapTo[A, B any](b B) Operator[A, B] {
|
||||
return ioeither.MapTo[error, A](b)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadChain[A, B any](fa IOResult[A], f Kleisli[A, B]) IOResult[B] {
|
||||
return ioeither.MonadChain(fa, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||
return ioeither.Chain(f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadChainEitherK[A, B any](ma IOResult[A], f func(A) Result[B]) IOResult[B] {
|
||||
return ioeither.MonadChainEitherK(ma, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadChainResultK[A, B any](ma IOResult[A], f func(A) Result[B]) IOResult[B] {
|
||||
return ioeither.MonadChainEitherK(ma, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func ChainEitherK[A, B any](f func(A) Result[B]) Operator[A, B] {
|
||||
return ioeither.ChainEitherK(f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func ChainResultK[A, B any](f func(A) Result[B]) Operator[A, B] {
|
||||
return ioeither.ChainEitherK(f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadAp[B, A any](mab IOResult[func(A) B], ma IOResult[A]) IOResult[B] {
|
||||
return ioeither.MonadAp(mab, ma)
|
||||
}
|
||||
|
||||
// Ap is an alias of [ApPar]
|
||||
//
|
||||
//go:inline
|
||||
func Ap[B, A any](ma IOResult[A]) Operator[func(A) B, B] {
|
||||
return ioeither.Ap[B](ma)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadApPar[B, A any](mab IOResult[func(A) B], ma IOResult[A]) IOResult[B] {
|
||||
return ioeither.MonadApPar(mab, ma)
|
||||
}
|
||||
|
||||
// ApPar applies function and value in parallel
|
||||
//
|
||||
//go:inline
|
||||
func ApPar[B, A any](ma IOResult[A]) Operator[func(A) B, B] {
|
||||
return ioeither.ApPar[B](ma)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadApSeq[B, A any](mab IOResult[func(A) B], ma IOResult[A]) IOResult[B] {
|
||||
return ioeither.MonadApSeq(mab, ma)
|
||||
}
|
||||
|
||||
// ApSeq applies function and value sequentially
|
||||
//
|
||||
//go:inline
|
||||
func ApSeq[B, A any](ma IOResult[A]) func(IOResult[func(A) B]) IOResult[B] {
|
||||
return ioeither.ApSeq[B](ma)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func Flatten[A any](mma IOResult[IOResult[A]]) IOResult[A] {
|
||||
return ioeither.Flatten(mma)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func TryCatch[A any](f func() (A, error), onThrow Endomorphism[error]) IOResult[A] {
|
||||
return ioeither.TryCatch(f, onThrow)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func TryCatchError[A any](f func() (A, error)) IOResult[A] {
|
||||
return ioeither.TryCatchError(f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func Memoize[A any](ma IOResult[A]) IOResult[A] {
|
||||
return ioeither.Memoize(ma)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadMapLeft[A, E any](fa IOResult[A], f func(error) E) ioeither.IOEither[E, A] {
|
||||
return ioeither.MonadMapLeft(fa, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MapLeft[A, E any](f func(error) E) func(IOResult[A]) ioeither.IOEither[E, A] {
|
||||
return ioeither.MapLeft[A](f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadBiMap[E, A, B any](fa IOResult[A], f func(error) E, g func(A) B) ioeither.IOEither[E, B] {
|
||||
return ioeither.MonadBiMap(fa, f, g)
|
||||
}
|
||||
|
||||
// BiMap maps a pair of functions over the two type arguments of the bifunctor.
|
||||
//
|
||||
//go:inline
|
||||
func BiMap[E, A, B any](f func(error) E, g func(A) B) func(IOResult[A]) ioeither.IOEither[E, B] {
|
||||
return ioeither.BiMap(f, g)
|
||||
}
|
||||
|
||||
// Fold converts an IOResult into an IO
|
||||
//
|
||||
//go:inline
|
||||
func Fold[A, B any](onLeft func(error) IO[B], onRight func(A) IO[B]) func(IOResult[A]) IO[B] {
|
||||
return ioeither.Fold(onLeft, onRight)
|
||||
}
|
||||
|
||||
// GetOrElse extracts the value or maps the error
|
||||
//
|
||||
//go:inline
|
||||
func GetOrElse[A any](onLeft func(error) IO[A]) func(IOResult[A]) IO[A] {
|
||||
return ioeither.GetOrElse(onLeft)
|
||||
}
|
||||
|
||||
// MonadChainTo composes to the second monad ignoring the return value of the first
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainTo[A, B any](fa IOResult[A], fb IOResult[B]) IOResult[B] {
|
||||
return ioeither.MonadChainTo(fa, fb)
|
||||
}
|
||||
|
||||
// ChainTo composes to the second [IOResult] monad ignoring the return value of the first
|
||||
//
|
||||
//go:inline
|
||||
func ChainTo[A, B any](fb IOResult[B]) Operator[A, B] {
|
||||
return ioeither.ChainTo[A](fb)
|
||||
}
|
||||
|
||||
// MonadChainFirst runs the [IOResult] monad returned by the function but returns the result of the original monad
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainFirst[A, B any](ma IOResult[A], f Kleisli[A, B]) IOResult[A] {
|
||||
return ioeither.MonadChainFirst(ma, f)
|
||||
}
|
||||
|
||||
// ChainFirst runs the [IOResult] monad returned by the function but returns the result of the original monad
|
||||
//
|
||||
//go:inline
|
||||
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
||||
return ioeither.ChainFirst(f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadChainFirstEitherK[A, B any](ma IOResult[A], f func(A) Result[B]) IOResult[A] {
|
||||
return ioeither.MonadChainFirstEitherK(ma, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadChainFirstResultK[A, B any](ma IOResult[A], f func(A) Result[B]) IOResult[A] {
|
||||
return ioeither.MonadChainFirstEitherK(ma, f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func ChainFirstEitherK[A, B any](f func(A) Result[B]) Operator[A, A] {
|
||||
return ioeither.ChainFirstEitherK(f)
|
||||
}
|
||||
|
||||
// MonadChainFirstIOK runs [IO] the monad returned by the function but returns the result of the original monad
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainFirstIOK[A, B any](ma IOResult[A], f func(A) IO[B]) IOResult[A] {
|
||||
return ioeither.MonadChainFirstIOK(ma, f)
|
||||
}
|
||||
|
||||
// ChainFirstIOK runs the [IO] monad returned by the function but returns the result of the original monad
|
||||
//
|
||||
//go:inline
|
||||
func ChainFirstIOK[A, B any](f func(A) IO[B]) Operator[A, A] {
|
||||
return ioeither.ChainFirstIOK[error](f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadFold[A, B any](ma IOResult[A], onLeft func(error) IO[B], onRight func(A) IO[B]) IO[B] {
|
||||
return ioeither.MonadFold(ma, onLeft, onRight)
|
||||
}
|
||||
|
||||
// WithResource constructs a function that creates a resourcthen operates on it and then releases the resource
|
||||
//
|
||||
//go:inline
|
||||
func WithResource[A, R, ANY any](onCreate IOResult[R], onRelease Kleisli[R, ANY]) Kleisli[Kleisli[R, A], A] {
|
||||
return ioeither.WithResource[A](onCreate, onRelease)
|
||||
}
|
||||
|
||||
// Swap changes the order of type parameters
|
||||
//
|
||||
//go:inline
|
||||
func Swap[A any](val IOResult[A]) ioeither.IOEither[A, error] {
|
||||
return ioeither.Swap(val)
|
||||
}
|
||||
|
||||
// FromImpure converts a side effect without a return value into a side effect that returns any
|
||||
//
|
||||
//go:inline
|
||||
func FromImpure[E any](f func()) IOResult[any] {
|
||||
return ioeither.FromImpure[error](f)
|
||||
}
|
||||
|
||||
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
||||
//
|
||||
//go:inline
|
||||
func Defer[A any](gen Lazy[IOResult[A]]) IOResult[A] {
|
||||
return ioeither.Defer(gen)
|
||||
}
|
||||
|
||||
// MonadAlt identifies an associative operation on a type constructor
|
||||
//
|
||||
//go:inline
|
||||
func MonadAlt[A any](first IOResult[A], second Lazy[IOResult[A]]) IOResult[A] {
|
||||
return ioeither.MonadAlt(first, second)
|
||||
}
|
||||
|
||||
// Alt identifies an associative operation on a type constructor
|
||||
//
|
||||
//go:inline
|
||||
func Alt[A any](second Lazy[IOResult[A]]) Operator[A, A] {
|
||||
return ioeither.Alt(second)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func MonadFlap[B, A any](fab IOResult[func(A) B], a A) IOResult[B] {
|
||||
return ioeither.MonadFlap(fab, a)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
||||
return ioeither.Flap[error, B](a)
|
||||
}
|
||||
|
||||
// ToIOOption converts an [IOResult] to an [IOO.IOOption]
|
||||
//
|
||||
//go:inline
|
||||
func ToIOOption[A any](ioe IOResult[A]) IOO.IOOption[A] {
|
||||
return ioeither.ToIOOption(ioe)
|
||||
}
|
||||
|
||||
// Delay creates an operation that passes in the value after some delay
|
||||
//
|
||||
//go:inline
|
||||
func Delay[A any](delay time.Duration) Operator[A, A] {
|
||||
return ioeither.Delay[error, A](delay)
|
||||
}
|
||||
|
||||
// After creates an operation that passes after the given [time.Time]
|
||||
//
|
||||
//go:inline
|
||||
func After[A any](timestamp time.Time) Operator[A, A] {
|
||||
return ioeither.After[error, A](timestamp)
|
||||
}
|
||||
152
v2/ioresult/ioeither_test.go
Normal file
152
v2/ioresult/ioeither_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/internal/utils"
|
||||
"github.com/IBM/fp-go/v2/io"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
assert.Equal(t, result.Of(2), F.Pipe1(
|
||||
Of(1),
|
||||
Map(utils.Double),
|
||||
)())
|
||||
|
||||
}
|
||||
|
||||
func TestChainEitherK(t *testing.T) {
|
||||
|
||||
a := errors.New("a")
|
||||
b := errors.New("b")
|
||||
|
||||
f := ChainEitherK(func(n int) Result[int] {
|
||||
if n > 0 {
|
||||
return result.Of(n)
|
||||
}
|
||||
return result.Left[int](a)
|
||||
|
||||
})
|
||||
assert.Equal(t, result.Right(1), f(Right(1))())
|
||||
assert.Equal(t, result.Left[int](a), f(Right(-1))())
|
||||
assert.Equal(t, result.Left[int](b), f(Left[int](b))())
|
||||
}
|
||||
|
||||
func TestChainOptionK(t *testing.T) {
|
||||
|
||||
a := errors.New("a")
|
||||
b := errors.New("b")
|
||||
|
||||
f := ChainOptionK[int, int](F.Constant(a))(func(n int) O.Option[int] {
|
||||
if n > 0 {
|
||||
return O.Some(n)
|
||||
}
|
||||
return O.None[int]()
|
||||
})
|
||||
|
||||
assert.Equal(t, result.Right(1), f(Right(1))())
|
||||
assert.Equal(t, result.Left[int](a), f(Right(-1))())
|
||||
assert.Equal(t, result.Left[int](b), f(Left[int](b))())
|
||||
}
|
||||
|
||||
func TestFromOption(t *testing.T) {
|
||||
|
||||
a := errors.New("a")
|
||||
|
||||
f := FromOption[int](F.Constant(a))
|
||||
assert.Equal(t, result.Right(1), f(O.Some(1))())
|
||||
assert.Equal(t, result.Left[int](a), f(O.None[int]())())
|
||||
}
|
||||
|
||||
func TestChainIOK(t *testing.T) {
|
||||
b := errors.New("b")
|
||||
|
||||
f := ChainIOK(func(n int) io.IO[string] {
|
||||
return func() string {
|
||||
return fmt.Sprintf("%d", n)
|
||||
}
|
||||
})
|
||||
|
||||
assert.Equal(t, result.Right("1"), f(Right(1))())
|
||||
assert.Equal(t, result.Left[string](b), f(Left[int](b))())
|
||||
}
|
||||
|
||||
func TestChainWithIO(t *testing.T) {
|
||||
|
||||
r := F.Pipe1(
|
||||
Of("test"),
|
||||
// sad, we need the generics version ...
|
||||
io.Map(result.IsRight[string]),
|
||||
)
|
||||
|
||||
assert.True(t, r())
|
||||
}
|
||||
|
||||
func TestChainFirst(t *testing.T) {
|
||||
|
||||
foo := errors.New("foo")
|
||||
|
||||
f := func(a string) IOResult[int] {
|
||||
if len(a) > 2 {
|
||||
return Of(len(a))
|
||||
}
|
||||
return Left[int](foo)
|
||||
}
|
||||
good := Of("foo")
|
||||
bad := Of("a")
|
||||
ch := ChainFirst(f)
|
||||
|
||||
assert.Equal(t, result.Of("foo"), F.Pipe1(good, ch)())
|
||||
assert.Equal(t, result.Left[string](foo), F.Pipe1(bad, ch)())
|
||||
}
|
||||
|
||||
func TestChainFirstIOK(t *testing.T) {
|
||||
f := func(a string) io.IO[int] {
|
||||
return io.Of(len(a))
|
||||
}
|
||||
good := Of("foo")
|
||||
ch := ChainFirstIOK(f)
|
||||
|
||||
assert.Equal(t, result.Of("foo"), F.Pipe1(good, ch)())
|
||||
}
|
||||
|
||||
func TestApFirst(t *testing.T) {
|
||||
|
||||
x := F.Pipe1(
|
||||
Of("a"),
|
||||
ApFirst[string](Of("b")),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of("a"), x())
|
||||
}
|
||||
|
||||
func TestApSecond(t *testing.T) {
|
||||
|
||||
x := F.Pipe1(
|
||||
Of("a"),
|
||||
ApSecond[string](Of("b")),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of("b"), x())
|
||||
}
|
||||
28
v2/ioresult/logging.go
Normal file
28
v2/ioresult/logging.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
// LogJSON converts the argument to pretty printed JSON and then logs it via the format string
|
||||
// Can be used with [ChainFirst]
|
||||
//
|
||||
//go:inline
|
||||
func LogJSON[A any](prefix string) Kleisli[A, any] {
|
||||
return ioeither.LogJSON[A](prefix)
|
||||
}
|
||||
42
v2/ioresult/logging_test.go
Normal file
42
v2/ioresult/logging_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogging(t *testing.T) {
|
||||
|
||||
type SomeData struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
src := &SomeData{Key: "key", Value: "value"}
|
||||
|
||||
res := F.Pipe1(
|
||||
Of(src),
|
||||
ChainFirst(LogJSON[*SomeData]("Data: \n%s")),
|
||||
)
|
||||
|
||||
dst := res()
|
||||
assert.Equal(t, E.Of[error](src), dst)
|
||||
}
|
||||
69
v2/ioresult/monad.go
Normal file
69
v2/ioresult/monad.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2024 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/internal/functor"
|
||||
"github.com/IBM/fp-go/v2/internal/monad"
|
||||
"github.com/IBM/fp-go/v2/internal/pointed"
|
||||
)
|
||||
|
||||
type (
|
||||
ioEitherPointed[A any] struct{}
|
||||
|
||||
ioEitherMonad[A, B any] struct{}
|
||||
|
||||
ioEitherFunctor[A, B any] struct{}
|
||||
)
|
||||
|
||||
func (o *ioEitherPointed[A]) Of(a A) IOResult[A] {
|
||||
return Of(a)
|
||||
}
|
||||
|
||||
func (o *ioEitherMonad[A, B]) Of(a A) IOResult[A] {
|
||||
return Of(a)
|
||||
}
|
||||
|
||||
func (o *ioEitherMonad[A, B]) Map(f func(A) B) Operator[A, B] {
|
||||
return Map(f)
|
||||
}
|
||||
|
||||
func (o *ioEitherMonad[A, B]) Chain(f Kleisli[A, B]) Operator[A, B] {
|
||||
return Chain(f)
|
||||
}
|
||||
|
||||
func (o *ioEitherMonad[A, B]) Ap(fa IOResult[A]) Operator[func(A) B, B] {
|
||||
return Ap[B](fa)
|
||||
}
|
||||
|
||||
func (o *ioEitherFunctor[A, B]) Map(f func(A) B) Operator[A, B] {
|
||||
return Map(f)
|
||||
}
|
||||
|
||||
// Pointed implements the pointed operations for [IOResult]
|
||||
func Pointed[A any]() pointed.Pointed[A, IOResult[A]] {
|
||||
return &ioEitherPointed[A]{}
|
||||
}
|
||||
|
||||
// Functor implements the monadic operations for [IOResult]
|
||||
func Functor[A, B any]() functor.Functor[A, B, IOResult[A], IOResult[B]] {
|
||||
return &ioEitherFunctor[A, B]{}
|
||||
}
|
||||
|
||||
// Monad implements the monadic operations for [IOResult]
|
||||
func Monad[A, B any]() monad.Monad[A, B, IOResult[A], IOResult[B], IOResult[func(A) B]] {
|
||||
return &ioEitherMonad[A, B]{}
|
||||
}
|
||||
48
v2/ioresult/monoid.go
Normal file
48
v2/ioresult/monoid.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
"github.com/IBM/fp-go/v2/monoid"
|
||||
)
|
||||
|
||||
// ApplicativeMonoid returns a [Monoid] that concatenates [IOResult] instances via their applicative
|
||||
//
|
||||
//go:inline
|
||||
func ApplicativeMonoid[A any](
|
||||
m monoid.Monoid[A],
|
||||
) Monoid[A] {
|
||||
return ioeither.ApplicativeMonoid[error](m)
|
||||
}
|
||||
|
||||
// ApplicativeMonoid returns a [Monoid] that concatenates [IOResult] instances via their applicative
|
||||
//
|
||||
//go:inline
|
||||
func ApplicativeMonoidSeq[A any](
|
||||
m monoid.Monoid[A],
|
||||
) Monoid[A] {
|
||||
return ioeither.ApplicativeMonoidSeq[error](m)
|
||||
}
|
||||
|
||||
// ApplicativeMonoid returns a [Monoid] that concatenates [IOResult] instances via their applicative
|
||||
//
|
||||
//go:inline
|
||||
func ApplicativeMonoidPar[A any](
|
||||
m monoid.Monoid[A],
|
||||
) Monoid[A] {
|
||||
return ioeither.ApplicativeMonoidPar[error](m)
|
||||
}
|
||||
42
v2/ioresult/monoid_test.go
Normal file
42
v2/ioresult/monoid_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2023 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
S "github.com/IBM/fp-go/v2/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestApplicativeMonoid(t *testing.T) {
|
||||
m := ApplicativeMonoid(S.Monoid)
|
||||
|
||||
// good cases
|
||||
assert.Equal(t, result.Of("ab"), m.Concat(Of("a"), Of("b"))())
|
||||
assert.Equal(t, result.Of("a"), m.Concat(Of("a"), m.Empty())())
|
||||
assert.Equal(t, result.Of("b"), m.Concat(m.Empty(), Of("b"))())
|
||||
|
||||
// bad cases
|
||||
e1 := fmt.Errorf("e1")
|
||||
e2 := fmt.Errorf("e1")
|
||||
|
||||
assert.Equal(t, result.Left[string](e1), m.Concat(Left[string](e1), Of("b"))())
|
||||
assert.Equal(t, result.Left[string](e1), m.Concat(Left[string](e1), Left[string](e2))())
|
||||
assert.Equal(t, result.Left[string](e2), m.Concat(Of("a"), Left[string](e2))())
|
||||
}
|
||||
36
v2/ioresult/retry.go
Normal file
36
v2/ioresult/retry.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
R "github.com/IBM/fp-go/v2/retry"
|
||||
)
|
||||
|
||||
// Retrying will retry the actions according to the check policy
|
||||
//
|
||||
// policy - refers to the retry policy
|
||||
// action - converts a status into an operation to be executed
|
||||
// check - checks if the result of the action needs to be retried
|
||||
//
|
||||
//go:inline
|
||||
func Retrying[A any](
|
||||
policy R.RetryPolicy,
|
||||
action Kleisli[R.RetryStatus, A],
|
||||
check func(Result[A]) bool,
|
||||
) IOResult[A] {
|
||||
return ioeither.Retrying(policy, action, check)
|
||||
}
|
||||
49
v2/ioresult/retry_test.go
Normal file
49
v2/ioresult/retry_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
R "github.com/IBM/fp-go/v2/retry"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var expLogBackoff = R.ExponentialBackoff(10 * time.Millisecond)
|
||||
|
||||
// our retry policy with a 1s cap
|
||||
var testLogPolicy = R.CapDelay(
|
||||
2*time.Second,
|
||||
R.Monoid.Concat(expLogBackoff, R.LimitRetries(20)),
|
||||
)
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
action := func(status R.RetryStatus) IOResult[string] {
|
||||
if status.IterNumber < 5 {
|
||||
return Left[string](fmt.Errorf("retrying %d", status.IterNumber))
|
||||
}
|
||||
return Of(fmt.Sprintf("Retrying %d", status.IterNumber))
|
||||
}
|
||||
check := result.IsLeft[string]
|
||||
|
||||
r := Retrying(testLogPolicy, action, check)
|
||||
|
||||
assert.Equal(t, E.Of[error]("Retrying 5"), r())
|
||||
}
|
||||
27
v2/ioresult/semigroup.go
Normal file
27
v2/ioresult/semigroup.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative
|
||||
//
|
||||
//go:inline
|
||||
func AltSemigroup[A any]() Semigroup[A] {
|
||||
return ioeither.AltSemigroup[error, A]()
|
||||
}
|
||||
83
v2/ioresult/sequence_test.go
Normal file
83
v2/ioresult/sequence_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
A "github.com/IBM/fp-go/v2/array"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
TST "github.com/IBM/fp-go/v2/internal/testing"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMapSeq(t *testing.T) {
|
||||
var results []string
|
||||
|
||||
handler := func(value string) IOResult[string] {
|
||||
return func() result.Result[string] {
|
||||
results = append(results, value)
|
||||
return result.Of(value)
|
||||
}
|
||||
}
|
||||
|
||||
src := A.From("a", "b", "c")
|
||||
|
||||
res := F.Pipe2(
|
||||
src,
|
||||
TraverseArraySeq(handler),
|
||||
Map(func(data []string) bool {
|
||||
return assert.Equal(t, data, results)
|
||||
}),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of(true), res())
|
||||
}
|
||||
|
||||
func TestSequenceArray(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayTest(
|
||||
FromStrictEquals[bool](),
|
||||
Pointed[string](),
|
||||
Pointed[bool](),
|
||||
Functor[[]string, bool](),
|
||||
SequenceArray[string],
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceArrayError(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayErrorTest(
|
||||
FromStrictEquals[bool](),
|
||||
Left[string],
|
||||
Left[bool],
|
||||
Pointed[string](),
|
||||
Pointed[bool](),
|
||||
Functor[[]string, bool](),
|
||||
SequenceArray[string],
|
||||
)
|
||||
|
||||
// run across four bits
|
||||
s(4)(t)
|
||||
}
|
||||
29
v2/ioresult/sync.go
Normal file
29
v2/ioresult/sync.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
// WithLock executes the provided IO operation in the scope of a lock
|
||||
//
|
||||
//go:inline
|
||||
func WithLock[A any](lock IO[context.CancelFunc]) Operator[A, A] {
|
||||
return ioeither.WithLock[error, A](lock)
|
||||
}
|
||||
36
v2/ioresult/testing/laws.go
Normal file
36
v2/ioresult/testing/laws.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/IBM/fp-go/v2/eq"
|
||||
IOET "github.com/IBM/fp-go/v2/ioeither/testing"
|
||||
)
|
||||
|
||||
// AssertLaws asserts the apply monad laws for the `IOEither` monad
|
||||
func AssertLaws[A, B, C any](t *testing.T,
|
||||
eqa eq.Eq[A],
|
||||
eqb eq.Eq[B],
|
||||
eqc eq.Eq[C],
|
||||
|
||||
ab func(A) B,
|
||||
bc func(B) C,
|
||||
) func(a A) bool {
|
||||
|
||||
return IOET.AssertLaws(t, eq.FromStrictEquals[error](), eqa, eqb, eqc, ab, bc)
|
||||
}
|
||||
47
v2/ioresult/testing/laws_test.go
Normal file
47
v2/ioresult/testing/laws_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/IBM/fp-go/v2/eq"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMonadLaws(t *testing.T) {
|
||||
// some comparison
|
||||
eqa := eq.FromStrictEquals[bool]()
|
||||
eqb := eq.FromStrictEquals[int]()
|
||||
eqc := eq.FromStrictEquals[string]()
|
||||
|
||||
ab := func(a bool) int {
|
||||
if a {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
bc := func(b int) string {
|
||||
return fmt.Sprintf("value %d", b)
|
||||
}
|
||||
|
||||
laws := AssertLaws(t, eqa, eqb, eqc, ab, bc)
|
||||
|
||||
assert.True(t, laws(true))
|
||||
assert.True(t, laws(false))
|
||||
}
|
||||
146
v2/ioresult/traverse.go
Normal file
146
v2/ioresult/traverse.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
)
|
||||
|
||||
// TraverseArray transforms an array
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||
return ioeither.TraverseArray(f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndex transforms an array
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayWithIndex[A, B any](f func(int, A) IOResult[B]) Kleisli[[]A, []B] {
|
||||
return ioeither.TraverseArrayWithIndex(f)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||
//
|
||||
//go:inline
|
||||
func SequenceArray[A any](ma []IOResult[A]) IOResult[[]A] {
|
||||
return ioeither.SequenceArray(ma)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms a record
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||
return ioeither.TraverseRecord[K](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndex transforms a record
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) IOResult[B]) Kleisli[map[K]A, map[K]B] {
|
||||
return ioeither.TraverseRecordWithIndex(f)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a homogeneous sequence of either into an either of sequence
|
||||
//
|
||||
//go:inline
|
||||
func SequenceRecord[K comparable, A any](ma map[K]IOResult[A]) IOResult[map[K]A] {
|
||||
return ioeither.SequenceRecord(ma)
|
||||
}
|
||||
|
||||
// TraverseArraySeq transforms an array
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||
return ioeither.TraverseArraySeq(f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexSeq transforms an array
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) IOResult[B]) Kleisli[[]A, []B] {
|
||||
return ioeither.TraverseArrayWithIndexSeq(f)
|
||||
}
|
||||
|
||||
// SequenceArraySeq converts a homogeneous sequence of either into an either of sequence
|
||||
//
|
||||
//go:inline
|
||||
func SequenceArraySeq[A any](ma []IOResult[A]) IOResult[[]A] {
|
||||
return ioeither.SequenceArraySeq(ma)
|
||||
}
|
||||
|
||||
// TraverseRecordSeq transforms a record
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecordSeq[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||
return ioeither.TraverseRecordSeq[K](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexSeq transforms a record
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) IOResult[B]) Kleisli[map[K]A, map[K]B] {
|
||||
return ioeither.TraverseRecordWithIndexSeq(f)
|
||||
}
|
||||
|
||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||
//
|
||||
//go:inline
|
||||
func SequenceRecordSeq[K comparable, A any](ma map[K]IOResult[A]) IOResult[map[K]A] {
|
||||
return ioeither.SequenceRecordSeq(ma)
|
||||
}
|
||||
|
||||
// TraverseArrayPar transforms an array
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||
return ioeither.TraverseArrayPar(f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexPar transforms an array
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayWithIndexPar[A, B any](f func(int, A) IOResult[B]) Kleisli[[]A, []B] {
|
||||
return ioeither.TraverseArrayWithIndexPar(f)
|
||||
}
|
||||
|
||||
// SequenceArrayPar converts a homogeneous Paruence of either into an either of Paruence
|
||||
//
|
||||
//go:inline
|
||||
func SequenceArrayPar[A any](ma []IOResult[A]) IOResult[[]A] {
|
||||
return ioeither.SequenceArrayPar(ma)
|
||||
}
|
||||
|
||||
// TraverseRecordPar transforms a record
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecordPar[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||
return ioeither.TraverseRecordPar[K](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndexPar transforms a record
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) IOResult[B]) Kleisli[map[K]A, map[K]B] {
|
||||
return ioeither.TraverseRecordWithIndexPar(f)
|
||||
}
|
||||
|
||||
// SequenceRecordPar converts a homogeneous Paruence of either into an either of Paruence
|
||||
//
|
||||
//go:inline
|
||||
func SequenceRecordPar[K comparable, A any](ma map[K]IOResult[A]) IOResult[map[K]A] {
|
||||
return ioeither.SequenceRecordPar(ma)
|
||||
}
|
||||
37
v2/ioresult/traverse_test.go
Normal file
37
v2/ioresult/traverse_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache LicensVersion 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/v2/array"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTraverseArray(t *testing.T) {
|
||||
|
||||
src := A.From("A", "B")
|
||||
|
||||
trfrm := TraverseArrayWithIndex(func(idx int, data string) IOResult[string] {
|
||||
return Of(fmt.Sprintf("idx: %d, data: %s", idx, data))
|
||||
})
|
||||
|
||||
assert.Equal(t, result.Of(A.From("idx: 0, data: A", "idx: 1, data: B")), trfrm(src)())
|
||||
|
||||
}
|
||||
29
v2/ioresult/types.go
Normal file
29
v2/ioresult/types.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package ioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
"github.com/IBM/fp-go/v2/endomorphism"
|
||||
"github.com/IBM/fp-go/v2/io"
|
||||
"github.com/IBM/fp-go/v2/lazy"
|
||||
"github.com/IBM/fp-go/v2/monoid"
|
||||
"github.com/IBM/fp-go/v2/reader"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/IBM/fp-go/v2/semigroup"
|
||||
)
|
||||
|
||||
type (
|
||||
IO[A any] = io.IO[A]
|
||||
Lazy[A any] = lazy.Lazy[A]
|
||||
Either[E, A any] = either.Either[E, A]
|
||||
Result[A any] = result.Result[A]
|
||||
Endomorphism[A any] = endomorphism.Endomorphism[A]
|
||||
|
||||
// IOEither represents a synchronous computation that may fail
|
||||
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
|
||||
IOResult[A any] = IO[Result[A]]
|
||||
Monoid[A any] = monoid.Monoid[IOResult[A]]
|
||||
Semigroup[A any] = semigroup.Semigroup[IOResult[A]]
|
||||
|
||||
Kleisli[A, B any] = reader.Reader[A, IOResult[B]]
|
||||
Operator[A, B any] = Kleisli[IOResult[A], B]
|
||||
)
|
||||
@@ -201,7 +201,7 @@ func Flatten[R, A any](mma Reader[R, Reader[R, A]]) Reader[R, A] {
|
||||
// getConfig := func(e Env) Config { return e.Config }
|
||||
// getPort := func(c Config) int { return c.Port }
|
||||
// getPortFromEnv := reader.Compose(getConfig)(getPort)
|
||||
func Compose[R, B, C any](ab Reader[R, B]) func(Reader[B, C]) Reader[R, C] {
|
||||
func Compose[C, R, B any](ab Reader[R, B]) func(Reader[B, C]) Reader[R, C] {
|
||||
return func(bc Reader[B, C]) Reader[R, C] {
|
||||
return function.Flow2(ab, bc)
|
||||
}
|
||||
@@ -265,8 +265,8 @@ func Promap[E, A, D, B any](f func(D) E, g func(A) B) func(Reader[E, A]) Reader[
|
||||
// simplify := func(d DetailedConfig) SimpleConfig { return SimpleConfig{Host: d.Host} }
|
||||
// r := reader.Local(simplify)(getHost)
|
||||
// result := r(DetailedConfig{Host: "localhost", Port: 8080}) // "localhost"
|
||||
func Local[R2, R1, A any](f func(R2) R1) func(Reader[R1, A]) Reader[R2, A] {
|
||||
return Compose[R2, R1, A](f)
|
||||
func Local[A, R2, R1 any](f func(R2) R1) func(Reader[R1, A]) Reader[R2, A] {
|
||||
return Compose[A, R2, R1](f)
|
||||
}
|
||||
|
||||
// Read applies a context to a Reader to obtain its value.
|
||||
|
||||
@@ -128,7 +128,7 @@ func TestCompose(t *testing.T) {
|
||||
env := Env{Config: Config{Port: 8080}}
|
||||
getConfig := func(e Env) Config { return e.Config }
|
||||
getPort := func(c Config) int { return c.Port }
|
||||
getPortFromEnv := Compose[Env, Config, int](getConfig)(getPort)
|
||||
getPortFromEnv := Compose[int](getConfig)(getPort)
|
||||
result := getPortFromEnv(env)
|
||||
assert.Equal(t, 8080, result)
|
||||
}
|
||||
@@ -167,7 +167,7 @@ func TestLocal(t *testing.T) {
|
||||
detailed := DetailedConfig{Host: "localhost", Port: 8080}
|
||||
getHost := func(c SimpleConfig) string { return c.Host }
|
||||
simplify := func(d DetailedConfig) SimpleConfig { return SimpleConfig{Host: d.Host} }
|
||||
r := Local[DetailedConfig, SimpleConfig, string](simplify)(getHost)
|
||||
r := Local[string](simplify)(getHost)
|
||||
result := r(detailed)
|
||||
assert.Equal(t, "localhost", result)
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ func BiMap[E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderEither[E,
|
||||
// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s
|
||||
// `contramap`).
|
||||
func Local[E, A, R2, R1 any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A] {
|
||||
return reader.Local[R2, R1, Either[E, A]](f)
|
||||
return reader.Local[Either[E, A], R2, R1](f)
|
||||
}
|
||||
|
||||
// Read applies a context to a reader to obtain its value
|
||||
|
||||
@@ -626,6 +626,6 @@ func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) Rea
|
||||
// This is similar to Contravariant's contramap operation.
|
||||
//
|
||||
//go:inline
|
||||
func Local[R1, R2, E, A any](f func(R2) R1) func(ReaderIOEither[R1, E, A]) ReaderIOEither[R2, E, A] {
|
||||
return reader.Local[R2, R1, IOEither[E, A]](f)
|
||||
func Local[E, A, R1, R2 any](f func(R2) R1) func(ReaderIOEither[R1, E, A]) ReaderIOEither[R2, E, A] {
|
||||
return reader.Local[IOEither[E, A], R2, R1](f)
|
||||
}
|
||||
|
||||
@@ -603,7 +603,7 @@ func TestLocal(t *testing.T) {
|
||||
ctx2 := testContext{value: 20}
|
||||
|
||||
rdr := Asks[error](func(c testContext) int { return c.value })
|
||||
result := Local[testContext, testContext, error, int](func(c testContext) testContext {
|
||||
result := Local[error, int](func(c testContext) testContext {
|
||||
return testContext{value: c.value * 2}
|
||||
})(rdr)
|
||||
|
||||
@@ -755,7 +755,7 @@ func TestTraverseArrayWithIndex(t *testing.T) {
|
||||
|
||||
func TestTraverseRecord(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := TraverseRecord[testContext, string, error](func(x int) ReaderIOEither[testContext, error, int] {
|
||||
result := TraverseRecord[string, testContext, error](func(x int) ReaderIOEither[testContext, error, int] {
|
||||
return Of[testContext, error](x * 2)
|
||||
})(map[string]int{"a": 1, "b": 2})
|
||||
|
||||
@@ -765,7 +765,7 @@ func TestTraverseRecord(t *testing.T) {
|
||||
|
||||
func TestTraverseRecordWithIndex(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := TraverseRecordWithIndex[testContext, string, error](func(k string, x int) ReaderIOEither[testContext, error, string] {
|
||||
result := TraverseRecordWithIndex[string, testContext, error](func(k string, x int) ReaderIOEither[testContext, error, string] {
|
||||
return Of[testContext, error](fmt.Sprintf("%s:%d", k, x))
|
||||
})(map[string]int{"a": 1, "b": 2})
|
||||
|
||||
@@ -775,7 +775,7 @@ func TestTraverseRecordWithIndex(t *testing.T) {
|
||||
|
||||
func TestSequenceRecord(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := SequenceRecord[testContext, string, error](map[string]ReaderIOEither[testContext, error, int]{
|
||||
result := SequenceRecord[string, testContext, error](map[string]ReaderIOEither[testContext, error, int]{
|
||||
"a": Of[testContext, error](1),
|
||||
"b": Of[testContext, error](2),
|
||||
})
|
||||
|
||||
@@ -47,7 +47,7 @@ import (
|
||||
// // result(cfg)() returns Right([user1, user2, user3]) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArray[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B] {
|
||||
func TraverseArray[R, E, A, B any](f Kleisli[R, E, A, B]) Kleisli[R, E, []A, []B] {
|
||||
return G.TraverseArray[ReaderIOEither[R, E, B], ReaderIOEither[R, E, []B], IOEither[E, B], IOEither[E, []B], []A](f)
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ func SequenceArray[R, E, A any](ma []ReaderIOEither[R, E, A]) ReaderIOEither[R,
|
||||
// result := enrichUsers(map[string]User{"alice": user1, "bob": user2})
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecord[R any, K comparable, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
||||
func TraverseRecord[K comparable, R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
||||
return G.TraverseRecord[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ func TraverseRecord[R any, K comparable, E, A, B any](f func(A) ReaderIOEither[R
|
||||
// })
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecordWithIndex[R any, K comparable, E, A, B any](f func(K, A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
||||
func TraverseRecordWithIndex[K comparable, R, E, A, B any](f func(K, A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
||||
return G.TraverseRecordWithIndex[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||
}
|
||||
|
||||
@@ -201,6 +201,6 @@ func TraverseRecordWithIndex[R any, K comparable, E, A, B any](f func(K, A) Read
|
||||
// // result(cfg)() returns Right(map[string]int{"users": 100, "posts": 50}) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func SequenceRecord[R any, K comparable, E, A any](ma map[K]ReaderIOEither[R, E, A]) ReaderIOEither[R, E, map[K]A] {
|
||||
func SequenceRecord[K comparable, R, E, A any](ma map[K]ReaderIOEither[R, E, A]) ReaderIOEither[R, E, map[K]A] {
|
||||
return G.SequenceRecord[ReaderIOEither[R, E, A], ReaderIOEither[R, E, map[K]A]](ma)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ type (
|
||||
// }
|
||||
ReaderIOEither[R, E, A any] = Reader[R, IOEither[E, A]]
|
||||
|
||||
Kleisli[R, E, A, B any] = reader.Reader[A, ReaderIOEither[R, E, B]]
|
||||
|
||||
// Operator represents a transformation from one ReaderIOEither to another.
|
||||
// It's a Reader that takes a ReaderIOEither[R, E, A] and produces a ReaderIOEither[R, E, B].
|
||||
// This type is commonly used for composing operations in a point-free style.
|
||||
@@ -78,5 +80,5 @@ type (
|
||||
//
|
||||
// Example:
|
||||
// var doubleOp Operator[Config, error, int, int] = Map(func(x int) int { return x * 2 })
|
||||
Operator[R, E, A, B any] = reader.Reader[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]]
|
||||
Operator[R, E, A, B any] = Kleisli[R, E, ReaderIOEither[R, E, A], B]
|
||||
)
|
||||
|
||||
75
v2/readerioresult/array_test.go
Normal file
75
v2/readerioresult/array_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
A "github.com/IBM/fp-go/v2/array"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
TST "github.com/IBM/fp-go/v2/internal/testing"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTraverseArray(t *testing.T) {
|
||||
|
||||
e := errors.New("e")
|
||||
|
||||
f := TraverseArray(func(a string) ReaderIOResult[context.Context, string] {
|
||||
if len(a) > 0 {
|
||||
return Right[context.Context](a + a)
|
||||
}
|
||||
return Left[context.Context, string](e)
|
||||
})
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, result.Of(A.Empty[string]()), F.Pipe1(A.Empty[string](), f)(ctx)())
|
||||
assert.Equal(t, result.Of([]string{"aa", "bb"}), F.Pipe1([]string{"a", "b"}, f)(ctx)())
|
||||
assert.Equal(t, result.Left[[]string](e), F.Pipe1([]string{"a", ""}, f)(ctx)())
|
||||
}
|
||||
|
||||
func TestSequenceArray(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayTest(
|
||||
FromStrictEquals[context.Context, bool]()(context.Background()),
|
||||
Pointed[context.Context, string](),
|
||||
Pointed[context.Context, bool](),
|
||||
Functor[context.Context, []string, bool](),
|
||||
SequenceArray[context.Context, string],
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceArrayError(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayErrorTest(
|
||||
FromStrictEquals[context.Context, bool]()(context.Background()),
|
||||
Left[context.Context],
|
||||
Left[context.Context, bool],
|
||||
Pointed[context.Context, string](),
|
||||
Pointed[context.Context, bool](),
|
||||
Functor[context.Context, []string, bool](),
|
||||
SequenceArray[context.Context, string],
|
||||
)
|
||||
// run across four bits
|
||||
s(4)(t)
|
||||
}
|
||||
322
v2/readerioresult/bind.go
Normal file
322
v2/readerioresult/bind.go
Normal file
@@ -0,0 +1,322 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
)
|
||||
|
||||
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||
// This is the starting point for do-notation style composition.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
// type Env struct {
|
||||
// UserRepo UserRepository
|
||||
// PostRepo PostRepository
|
||||
// }
|
||||
// result := readerioeither.Do[Env, error](State{})
|
||||
//
|
||||
//go:inline
|
||||
func Do[R, S any](
|
||||
empty S,
|
||||
) ReaderIOResult[R, S] {
|
||||
return RIOE.Do[R, error](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
|
||||
// This enables sequential composition where each step can depend on the results of previous steps
|
||||
// and access the shared environment.
|
||||
//
|
||||
// The setter function takes the result of the computation and returns a function that
|
||||
// updates the context from S1 to S2.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
// type Env struct {
|
||||
// UserRepo UserRepository
|
||||
// PostRepo PostRepository
|
||||
// }
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// readerioeither.Do[Env, error](State{}),
|
||||
// readerioeither.Bind(
|
||||
// func(user User) func(State) State {
|
||||
// return func(s State) State { s.User = user; return s }
|
||||
// },
|
||||
// func(s State) readerioeither.ReaderIOResult[Env, error, User] {
|
||||
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||
// return env.UserRepo.FindUser()
|
||||
// })
|
||||
// },
|
||||
// ),
|
||||
// readerioeither.Bind(
|
||||
// func(posts []Post) func(State) State {
|
||||
// return func(s State) State { s.Posts = posts; return s }
|
||||
// },
|
||||
// func(s State) readerioeither.ReaderIOResult[Env, error, []Post] {
|
||||
// // This can access s.User from the previous step
|
||||
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, []Post] {
|
||||
// return env.PostRepo.FindPostsByUser(s.User.ID)
|
||||
// })
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func Bind[R, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f Kleisli[R, S1, T],
|
||||
) Operator[R, S1, S2] {
|
||||
return RIOE.Bind(setter, f)
|
||||
}
|
||||
|
||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||
//
|
||||
//go:inline
|
||||
func Let[R, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) Operator[R, S1, S2] {
|
||||
return RIOE.Let[R, error](setter, f)
|
||||
}
|
||||
|
||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||
//
|
||||
//go:inline
|
||||
func LetTo[R, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
b T,
|
||||
) Operator[R, S1, S2] {
|
||||
return RIOE.LetTo[R, error](setter, b)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state [S1] from a value [T]
|
||||
//
|
||||
//go:inline
|
||||
func BindTo[R, S1, T any](
|
||||
setter func(T) S1,
|
||||
) Operator[R, T, S1] {
|
||||
return RIOE.BindTo[R, error](setter)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering
|
||||
// the context and the value concurrently (using Applicative rather than Monad).
|
||||
// This allows independent computations to be combined without one depending on the result of the other.
|
||||
//
|
||||
// Unlike Bind, which sequences operations, ApS can be used when operations are independent
|
||||
// and can conceptually run in parallel.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
// type Env struct {
|
||||
// UserRepo UserRepository
|
||||
// PostRepo PostRepository
|
||||
// }
|
||||
//
|
||||
// // These operations are independent and can be combined with ApS
|
||||
// getUser := readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||
// return env.UserRepo.FindUser()
|
||||
// })
|
||||
// getPosts := readerioeither.Asks(func(env Env) ioeither.IOEither[error, []Post] {
|
||||
// return env.PostRepo.FindPosts()
|
||||
// })
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// readerioeither.Do[Env, error](State{}),
|
||||
// readerioeither.ApS(
|
||||
// func(user User) func(State) State {
|
||||
// return func(s State) State { s.User = user; return s }
|
||||
// },
|
||||
// getUser,
|
||||
// ),
|
||||
// readerioeither.ApS(
|
||||
// func(posts []Post) func(State) State {
|
||||
// return func(s State) State { s.Posts = posts; return s }
|
||||
// },
|
||||
// getPosts,
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func ApS[R, S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa ReaderIOResult[R, T],
|
||||
) Operator[R, S1, S2] {
|
||||
return RIOE.ApS(setter, fa)
|
||||
}
|
||||
|
||||
// ApSL attaches a value to a context using a lens-based setter.
|
||||
// This is a convenience function that combines ApS with a lens, allowing you to use
|
||||
// optics to update nested structures in a more composable way.
|
||||
//
|
||||
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||
// This eliminates the need to manually write setter functions.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
// type Env struct {
|
||||
// UserRepo UserRepository
|
||||
// PostRepo PostRepository
|
||||
// }
|
||||
//
|
||||
// userLens := lens.MakeLens(
|
||||
// func(s State) User { return s.User },
|
||||
// func(s State, u User) State { s.User = u; return s },
|
||||
// )
|
||||
//
|
||||
// getUser := readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||
// return env.UserRepo.FindUser()
|
||||
// })
|
||||
// result := F.Pipe2(
|
||||
// readerioeither.Of[Env, error](State{}),
|
||||
// readerioeither.ApSL(userLens, getUser),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func ApSL[R, S, T any](
|
||||
lens L.Lens[S, T],
|
||||
fa ReaderIOResult[R, T],
|
||||
) Operator[R, S, S] {
|
||||
return RIOE.ApSL(lens, fa)
|
||||
}
|
||||
|
||||
// BindL is a variant of Bind that uses a lens to focus on a specific part of the context.
|
||||
// This provides a more ergonomic API when working with nested structures, eliminating
|
||||
// the need to manually write setter functions.
|
||||
//
|
||||
// The lens parameter provides both a getter and setter for a field of type T within
|
||||
// the context S. The function f receives the current value of the focused field and
|
||||
// returns a ReaderIOResult computation that produces an updated value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
// type Env struct {
|
||||
// UserRepo UserRepository
|
||||
// PostRepo PostRepository
|
||||
// }
|
||||
//
|
||||
// userLens := lens.MakeLens(
|
||||
// func(s State) User { return s.User },
|
||||
// func(s State, u User) State { s.User = u; return s },
|
||||
// )
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// readerioeither.Do[Env, error](State{}),
|
||||
// readerioeither.BindL(userLens, func(user User) readerioeither.ReaderIOResult[Env, error, User] {
|
||||
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||
// return env.UserRepo.FindUser()
|
||||
// })
|
||||
// }),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func BindL[R, S, T any](
|
||||
lens L.Lens[S, T],
|
||||
f Kleisli[R, T, T],
|
||||
) Operator[R, S, S] {
|
||||
return RIOE.BindL(lens, f)
|
||||
}
|
||||
|
||||
// LetL is a variant of Let that uses a lens to focus on a specific part of the context.
|
||||
// This provides a more ergonomic API when working with nested structures, eliminating
|
||||
// the need to manually write setter functions.
|
||||
//
|
||||
// The lens parameter provides both a getter and setter for a field of type T within
|
||||
// the context S. The function f receives the current value of the focused field and
|
||||
// returns a new value (without wrapping in a ReaderIOResult).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
//
|
||||
// userLens := lens.MakeLens(
|
||||
// func(s State) User { return s.User },
|
||||
// func(s State, u User) State { s.User = u; return s },
|
||||
// )
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// readerioeither.Do[any, error](State{User: User{Name: "Alice"}}),
|
||||
// readerioeither.LetL(userLens, func(user User) User {
|
||||
// user.Name = "Bob"
|
||||
// return user
|
||||
// }),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func LetL[R, S, T any](
|
||||
lens L.Lens[S, T],
|
||||
f func(T) T,
|
||||
) Operator[R, S, S] {
|
||||
return RIOE.LetL[R, error](lens, f)
|
||||
}
|
||||
|
||||
// LetToL is a variant of LetTo that uses a lens to focus on a specific part of the context.
|
||||
// This provides a more ergonomic API when working with nested structures, eliminating
|
||||
// the need to manually write setter functions.
|
||||
//
|
||||
// The lens parameter provides both a getter and setter for a field of type T within
|
||||
// the context S. The value b is set directly to the focused field.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct {
|
||||
// User User
|
||||
// Posts []Post
|
||||
// }
|
||||
//
|
||||
// userLens := lens.MakeLens(
|
||||
// func(s State) User { return s.User },
|
||||
// func(s State, u User) State { s.User = u; return s },
|
||||
// )
|
||||
//
|
||||
// newUser := User{Name: "Bob", ID: 123}
|
||||
// result := F.Pipe2(
|
||||
// readerioeither.Do[any, error](State{}),
|
||||
// readerioeither.LetToL(userLens, newUser),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func LetToL[R, S, T any](
|
||||
lens L.Lens[S, T],
|
||||
b T,
|
||||
) Operator[R, S, S] {
|
||||
return RIOE.LetToL[R, error](lens, b)
|
||||
}
|
||||
58
v2/readerioresult/bind_test.go
Normal file
58
v2/readerioresult/bind_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/internal/utils"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getLastName(s utils.Initial) ReaderIOResult[context.Context, string] {
|
||||
return Of[context.Context]("Doe")
|
||||
}
|
||||
|
||||
func getGivenName(s utils.WithLastName) ReaderIOResult[context.Context, string] {
|
||||
return Of[context.Context]("John")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do[context.Context](utils.Empty),
|
||||
Bind(utils.SetLastName, getLastName),
|
||||
Bind(utils.SetGivenName, getGivenName),
|
||||
Map[context.Context](utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(context.Background())(), result.Of("John Doe"))
|
||||
}
|
||||
|
||||
func TestApS(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do[context.Context](utils.Empty),
|
||||
ApS(utils.SetLastName, Of[context.Context]("Doe")),
|
||||
ApS(utils.SetGivenName, Of[context.Context]("John")),
|
||||
Map[context.Context](utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res(context.Background())(), result.Of("John Doe"))
|
||||
}
|
||||
33
v2/readerioresult/bracket.go
Normal file
33
v2/readerioresult/bracket.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
)
|
||||
|
||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||
// whether the body action returns and error or not.
|
||||
//
|
||||
//go:inline
|
||||
func Bracket[
|
||||
R, A, B, ANY any](
|
||||
acquire ReaderIOResult[R, A],
|
||||
use Kleisli[R, A, B],
|
||||
release func(A, Result[B]) ReaderIOResult[R, ANY],
|
||||
) ReaderIOResult[R, B] {
|
||||
return RIOE.Bracket(acquire, use, release)
|
||||
}
|
||||
127
v2/readerioresult/coverage.out
Normal file
127
v2/readerioresult/coverage.out
Normal file
@@ -0,0 +1,127 @@
|
||||
mode: set
|
||||
github.com/IBM/fp-go/v2/readerioeither/bind.go:26.27,28.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/bind.go:34.26,36.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/bind.go:42.26,44.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/bind.go:50.26,52.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/bind.go:57.25,59.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/bind.go:65.26,67.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/bracket.go:31.27,33.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/eq.go:25.92,27.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/eq.go:30.88,32.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:14.92,16.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:20.90,22.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:26.92,28.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:32.102,34.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:38.100,40.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:44.102,46.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:50.114,52.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:56.112,58.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:62.114,64.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:68.126,70.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:74.124,76.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:80.126,82.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:86.138,88.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:92.136,94.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:98.138,100.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:104.150,106.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:110.148,112.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:116.150,118.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:122.162,124.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:128.160,130.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:134.162,136.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:140.174,142.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:146.172,148.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:152.174,154.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:158.186,160.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:164.184,166.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:170.186,172.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:176.198,178.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:182.196,184.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:188.198,190.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:194.211,196.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:200.209,202.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/gen.go:206.211,208.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/monad.go:26.73,28.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/monad.go:31.104,33.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/monad.go:36.131,38.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:40.92,46.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:50.90,52.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:55.76,60.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:63.75,68.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:73.96,75.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:79.60,81.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:85.90,87.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:91.54,93.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:98.120,104.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:108.125,114.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:118.123,125.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:129.87,135.2 1 0
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:139.128,147.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:151.92,158.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:162.116,169.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:173.80,179.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:183.124,190.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:194.88,200.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:204.108,211.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:215.72,221.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:225.113,233.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:237.77,244.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:248.99,254.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:258.119,265.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:268.122,275.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:278.122,285.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:289.119,291.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:295.84,300.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:304.89,309.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:312.54,314.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:317.53,319.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:323.59,325.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:329.51,331.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:335.102,337.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:341.77,343.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:346.72,348.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:351.71,353.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:357.71,359.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:362.64,364.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:367.63,369.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:373.63,375.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:379.79,381.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:385.89,387.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:391.46,393.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:397.64,399.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:403.89,405.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:409.103,411.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:415.135,417.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:421.105,423.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:427.129,429.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:433.120,440.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:444.120,449.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:453.117,455.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:459.77,461.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:465.86,467.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:471.104,472.34 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:472.34,474.3 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:479.123,487.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:491.84,498.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:503.68,505.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:509.98,511.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:515.94,517.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:520.106,522.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:526.103,528.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/reader.go:533.101,535.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/resource.go:21.181,22.73 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/resource.go:22.73,23.44 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/resource.go:23.44,27.41 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/resource.go:27.41,29.6 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/resource.go:30.40,32.5 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/sequence.go:25.91,30.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/sequence.go:32.124,38.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/sequence.go:40.157,47.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/sequence.go:49.190,57.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/sync.go:26.81,28.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/traverse.go:23.107,25.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/traverse.go:28.121,30.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/traverse.go:33.89,35.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/traverse.go:38.134,40.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/traverse.go:43.146,45.2 1 1
|
||||
github.com/IBM/fp-go/v2/readerioeither/traverse.go:48.116,50.2 1 1
|
||||
100
v2/readerioresult/doc.go
Normal file
100
v2/readerioresult/doc.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// package readerioresult provides a functional programming abstraction that combines
|
||||
// three powerful concepts: Reader, IO, and Either monads.
|
||||
//
|
||||
// # ReaderIOResult
|
||||
//
|
||||
// ReaderIOResult[R, A] represents a computation that:
|
||||
// - Depends on some context/environment of type R (Reader)
|
||||
// - Performs side effects (IO)
|
||||
// - Can fail with an error of type E or succeed with a value of type A (Either)
|
||||
//
|
||||
// This is particularly useful for:
|
||||
// - Dependency injection patterns
|
||||
// - Error handling in effectful computations
|
||||
// - Composing operations that need access to shared configuration or context
|
||||
//
|
||||
// # Core Operations
|
||||
//
|
||||
// Construction:
|
||||
// - Of/Right: Create a successful computation
|
||||
// - Left/ThrowError: Create a failed computation
|
||||
// - FromEither: Lift an Either into ReaderIOResult
|
||||
// - FromIO: Lift an IO into ReaderIOResult
|
||||
// - FromReader: Lift a Reader into ReaderIOResult
|
||||
// - FromIOEither: Lift an IOEither into ReaderIOResult
|
||||
// - TryCatch: Wrap error-returning functions
|
||||
//
|
||||
// Transformation:
|
||||
// - Map: Transform the success value
|
||||
// - MapLeft: Transform the error value
|
||||
// - BiMap: Transform both success and error values
|
||||
// - Chain/Bind: Sequence dependent computations
|
||||
// - Flatten: Flatten nested ReaderIOResult
|
||||
//
|
||||
// Combination:
|
||||
// - Ap: Apply a function in a context to a value in a context
|
||||
// - SequenceArray: Convert array of ReaderIOResult to ReaderIOResult of array
|
||||
// - TraverseArray: Map and sequence in one operation
|
||||
//
|
||||
// Error Handling:
|
||||
// - Fold: Handle both success and error cases
|
||||
// - GetOrElse: Provide a default value on error
|
||||
// - OrElse: Try an alternative computation on error
|
||||
// - Alt: Choose the first successful computation
|
||||
//
|
||||
// Context Access:
|
||||
// - Ask: Get the current context
|
||||
// - Asks: Get a value derived from the context
|
||||
// - Local: Run a computation with a modified context
|
||||
//
|
||||
// Resource Management:
|
||||
// - Bracket: Ensure resource cleanup
|
||||
// - WithResource: Manage resource lifecycle
|
||||
//
|
||||
// # Example Usage
|
||||
//
|
||||
// type Config struct {
|
||||
// BaseURL string
|
||||
// Timeout time.Duration
|
||||
// }
|
||||
//
|
||||
// // A computation that depends on Config, performs IO, and can fail
|
||||
// func fetchUser(id int) readerioeither.ReaderIOResult[Config, error, User] {
|
||||
// return func(cfg Config) ioeither.IOEither[error, User] {
|
||||
// return func() either.Either[error, User] {
|
||||
// // Use cfg.BaseURL and cfg.Timeout to fetch user
|
||||
// // Return either.Right(user) or either.Left(err)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Compose operations
|
||||
// result := function.Pipe2(
|
||||
// fetchUser(123),
|
||||
// readerioeither.Map[Config, error](func(u User) string { return u.Name }),
|
||||
// readerioeither.Chain[Config, error](func(name string) readerioeither.ReaderIOResult[Config, error, string] {
|
||||
// return readerioeither.Of[Config, error]("Hello, " + name)
|
||||
// }),
|
||||
// )
|
||||
//
|
||||
// // Execute with config
|
||||
// config := Config{BaseURL: "https://api.example.com", Timeout: 30 * time.Second}
|
||||
// outcome := result(config)() // Returns either.Either[error, string]
|
||||
package readerioresult
|
||||
|
||||
//go:generate go run .. readerioeither --count 10 --filename gen.go
|
||||
35
v2/readerioresult/eq.go
Normal file
35
v2/readerioresult/eq.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/eq"
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
)
|
||||
|
||||
// Eq implements the equals predicate for values contained in the IOEither monad
|
||||
//
|
||||
//go:inline
|
||||
func Eq[R, A any](eq eq.Eq[Result[A]]) func(R) eq.Eq[ReaderIOResult[R, A]] {
|
||||
return RIOE.Eq[R](eq)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||
//
|
||||
//go:inline
|
||||
func FromStrictEquals[R any, A comparable]() func(R) eq.Eq[ReaderIOResult[R, A]] {
|
||||
return RIOE.FromStrictEquals[R, error, A]()
|
||||
}
|
||||
273
v2/readerioresult/gen.go
Normal file
273
v2/readerioresult/gen.go
Normal file
@@ -0,0 +1,273 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at
|
||||
// 2025-03-09 23:53:13.769132 +0100 CET m=+0.013697001
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
G "github.com/IBM/fp-go/v2/readerioeither/generic"
|
||||
)
|
||||
|
||||
// From0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From0[F ~func(C) func() (R, error), C, R any](f F) func() ReaderIOResult[C, R] {
|
||||
return G.From0[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize0[F ~func(C) (R, error), C, R any](f F) func() ReaderIOResult[C, R] {
|
||||
return G.Eitherize0[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize0 converts a function with 1 parameters returning a [ReaderIOResult[C, R]] into a function with 0 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize0[F ~func() ReaderIOResult[C, R], C, R any](f F) func(C) (R, error) {
|
||||
return G.Uneitherize0[ReaderIOResult[C, R], func(C) (R, error)](f)
|
||||
}
|
||||
|
||||
// From1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From1[F ~func(C, T0) func() (R, error), T0, C, R any](f F) func(T0) ReaderIOResult[C, R] {
|
||||
return G.From1[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize1[F ~func(C, T0) (R, error), T0, C, R any](f F) func(T0) ReaderIOResult[C, R] {
|
||||
return G.Eitherize1[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize1 converts a function with 2 parameters returning a [ReaderIOResult[C, R]] into a function with 1 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize1[F ~func(T0) ReaderIOResult[C, R], T0, C, R any](f F) func(C, T0) (R, error) {
|
||||
return G.Uneitherize1[ReaderIOResult[C, R], func(C, T0) (R, error)](f)
|
||||
}
|
||||
|
||||
// From2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From2[F ~func(C, T0, T1) func() (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOResult[C, R] {
|
||||
return G.From2[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize2[F ~func(C, T0, T1) (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOResult[C, R] {
|
||||
return G.Eitherize2[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize2 converts a function with 3 parameters returning a [ReaderIOResult[C, R]] into a function with 2 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize2[F ~func(T0, T1) ReaderIOResult[C, R], T0, T1, C, R any](f F) func(C, T0, T1) (R, error) {
|
||||
return G.Uneitherize2[ReaderIOResult[C, R], func(C, T0, T1) (R, error)](f)
|
||||
}
|
||||
|
||||
// From3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From3[F ~func(C, T0, T1, T2) func() (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOResult[C, R] {
|
||||
return G.From3[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize3[F ~func(C, T0, T1, T2) (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOResult[C, R] {
|
||||
return G.Eitherize3[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize3 converts a function with 4 parameters returning a [ReaderIOResult[C, R]] into a function with 3 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize3[F ~func(T0, T1, T2) ReaderIOResult[C, R], T0, T1, T2, C, R any](f F) func(C, T0, T1, T2) (R, error) {
|
||||
return G.Uneitherize3[ReaderIOResult[C, R], func(C, T0, T1, T2) (R, error)](f)
|
||||
}
|
||||
|
||||
// From4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From4[F ~func(C, T0, T1, T2, T3) func() (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOResult[C, R] {
|
||||
return G.From4[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize4[F ~func(C, T0, T1, T2, T3) (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOResult[C, R] {
|
||||
return G.Eitherize4[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize4 converts a function with 5 parameters returning a [ReaderIOResult[C, R]] into a function with 4 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOResult[C, R], T0, T1, T2, T3, C, R any](f F) func(C, T0, T1, T2, T3) (R, error) {
|
||||
return G.Uneitherize4[ReaderIOResult[C, R], func(C, T0, T1, T2, T3) (R, error)](f)
|
||||
}
|
||||
|
||||
// From5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From5[F ~func(C, T0, T1, T2, T3, T4) func() (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOResult[C, R] {
|
||||
return G.From5[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize5[F ~func(C, T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOResult[C, R] {
|
||||
return G.Eitherize5[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize5 converts a function with 6 parameters returning a [ReaderIOResult[C, R]] into a function with 5 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOResult[C, R], T0, T1, T2, T3, T4, C, R any](f F) func(C, T0, T1, T2, T3, T4) (R, error) {
|
||||
return G.Uneitherize5[ReaderIOResult[C, R], func(C, T0, T1, T2, T3, T4) (R, error)](f)
|
||||
}
|
||||
|
||||
// From6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From6[F ~func(C, T0, T1, T2, T3, T4, T5) func() (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOResult[C, R] {
|
||||
return G.From6[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize6[F ~func(C, T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOResult[C, R] {
|
||||
return G.Eitherize6[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize6 converts a function with 7 parameters returning a [ReaderIOResult[C, R]] into a function with 6 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOResult[C, R], T0, T1, T2, T3, T4, T5, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5) (R, error) {
|
||||
return G.Uneitherize6[ReaderIOResult[C, R], func(C, T0, T1, T2, T3, T4, T5) (R, error)](f)
|
||||
}
|
||||
|
||||
// From7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) func() (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOResult[C, R] {
|
||||
return G.From7[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOResult[C, R] {
|
||||
return G.Eitherize7[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize7 converts a function with 8 parameters returning a [ReaderIOResult[C, R]] into a function with 7 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOResult[C, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6) (R, error) {
|
||||
return G.Uneitherize7[ReaderIOResult[C, R], func(C, T0, T1, T2, T3, T4, T5, T6) (R, error)](f)
|
||||
}
|
||||
|
||||
// From8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOResult[C, R] {
|
||||
return G.From8[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOResult[C, R] {
|
||||
return G.Eitherize8[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize8 converts a function with 9 parameters returning a [ReaderIOResult[C, R]] into a function with 8 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOResult[C, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error) {
|
||||
return G.Uneitherize8[ReaderIOResult[C, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error)](f)
|
||||
}
|
||||
|
||||
// From9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOResult[C, R] {
|
||||
return G.From9[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOResult[C, R] {
|
||||
return G.Eitherize9[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize9 converts a function with 10 parameters returning a [ReaderIOResult[C, R]] into a function with 9 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOResult[C, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) {
|
||||
return G.Uneitherize9[ReaderIOResult[C, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error)](f)
|
||||
}
|
||||
|
||||
// From10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOResult[R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func From10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOResult[C, R] {
|
||||
return G.From10[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOResult[C, R]]
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Eitherize10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOResult[C, R] {
|
||||
return G.Eitherize10[ReaderIOResult[C, R]](f)
|
||||
}
|
||||
|
||||
// Uneitherize10 converts a function with 11 parameters returning a [ReaderIOResult[C, R]] into a function with 10 parameters returning a tuple.
|
||||
// The first parameter is considered to be the context [C].
|
||||
//
|
||||
//go:inline
|
||||
func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOResult[C, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) {
|
||||
return G.Uneitherize10[ReaderIOResult[C, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error)](f)
|
||||
}
|
||||
164
v2/readerioresult/gen_test.go
Normal file
164
v2/readerioresult/gen_test.go
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEitherize0(t *testing.T) {
|
||||
f := func(ctx context.Context) (int, error) {
|
||||
return ctx.Value("key").(int), nil
|
||||
}
|
||||
|
||||
result := Eitherize0(f)()
|
||||
ctx := context.WithValue(context.Background(), "key", 42)
|
||||
assert.Equal(t, E.Right[error](42), result(ctx)())
|
||||
}
|
||||
|
||||
func TestEitherize1(t *testing.T) {
|
||||
f := func(ctx context.Context, x int) (int, error) {
|
||||
return x * 2, nil
|
||||
}
|
||||
|
||||
result := Eitherize1(f)(5)
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, E.Right[error](10), result(ctx)())
|
||||
}
|
||||
|
||||
func TestEitherize2(t *testing.T) {
|
||||
f := func(ctx context.Context, x, y int) (int, error) {
|
||||
return x + y, nil
|
||||
}
|
||||
|
||||
result := Eitherize2(f)(5, 3)
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, E.Right[error](8), result(ctx)())
|
||||
}
|
||||
|
||||
func TestEitherize3(t *testing.T) {
|
||||
f := func(ctx context.Context, x, y, z int) (int, error) {
|
||||
return x + y + z, nil
|
||||
}
|
||||
|
||||
result := Eitherize3(f)(5, 3, 2)
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, E.Right[error](10), result(ctx)())
|
||||
}
|
||||
|
||||
func TestUneitherize0(t *testing.T) {
|
||||
f := func() ReaderIOResult[context.Context, int] {
|
||||
return Of[context.Context](42)
|
||||
}
|
||||
|
||||
result := Uneitherize0(f)
|
||||
ctx := context.Background()
|
||||
val, err := result(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 42, val)
|
||||
}
|
||||
|
||||
func TestUneitherize1(t *testing.T) {
|
||||
f := func(x int) ReaderIOResult[context.Context, int] {
|
||||
return Of[context.Context](x * 2)
|
||||
}
|
||||
|
||||
result := Uneitherize1(f)
|
||||
ctx := context.Background()
|
||||
val, err := result(ctx, 5)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 10, val)
|
||||
}
|
||||
|
||||
func TestUneitherize2(t *testing.T) {
|
||||
f := func(x, y int) ReaderIOResult[context.Context, int] {
|
||||
return Of[context.Context](x + y)
|
||||
}
|
||||
|
||||
result := Uneitherize2(f)
|
||||
ctx := context.Background()
|
||||
val, err := result(ctx, 5, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 8, val)
|
||||
}
|
||||
|
||||
func TestFrom0(t *testing.T) {
|
||||
f := func(ctx context.Context) func() (int, error) {
|
||||
return func() (int, error) {
|
||||
return 42, nil
|
||||
}
|
||||
}
|
||||
|
||||
result := From0(f)()
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, E.Right[error](42), result(ctx)())
|
||||
}
|
||||
|
||||
func TestFrom1(t *testing.T) {
|
||||
f := func(ctx context.Context, x int) func() (int, error) {
|
||||
return func() (int, error) {
|
||||
return x * 2, nil
|
||||
}
|
||||
}
|
||||
|
||||
result := From1(f)(5)
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, E.Right[error](10), result(ctx)())
|
||||
}
|
||||
|
||||
func TestFrom2(t *testing.T) {
|
||||
f := func(ctx context.Context, x, y int) func() (int, error) {
|
||||
return func() (int, error) {
|
||||
return x + y, nil
|
||||
}
|
||||
}
|
||||
|
||||
result := From2(f)(5, 3)
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, E.Right[error](8), result(ctx)())
|
||||
}
|
||||
|
||||
func TestEitherizeWithError(t *testing.T) {
|
||||
f := func(ctx context.Context, x int) (int, error) {
|
||||
if x < 0 {
|
||||
return 0, errors.New("negative value")
|
||||
}
|
||||
return x * 2, nil
|
||||
}
|
||||
|
||||
result := Eitherize1(f)(-5)
|
||||
ctx := context.Background()
|
||||
assert.True(t, E.IsLeft(result(ctx)()))
|
||||
}
|
||||
|
||||
func TestUneitherizeWithError(t *testing.T) {
|
||||
f := func(x int) ReaderIOResult[context.Context, int] {
|
||||
if x < 0 {
|
||||
return Left[context.Context, int](errors.New("negative value"))
|
||||
}
|
||||
return Of[context.Context](x * 2)
|
||||
}
|
||||
|
||||
result := Uneitherize1(f)
|
||||
ctx := context.Background()
|
||||
_, err := result(ctx, -5)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
44
v2/readerioresult/monad.go
Normal file
44
v2/readerioresult/monad.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2024 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/internal/functor"
|
||||
"github.com/IBM/fp-go/v2/internal/monad"
|
||||
"github.com/IBM/fp-go/v2/internal/pointed"
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
)
|
||||
|
||||
// Pointed returns the pointed operations for [ReaderIOResult]
|
||||
//
|
||||
//go:inline
|
||||
func Pointed[R, A any]() pointed.Pointed[A, ReaderIOResult[R, A]] {
|
||||
return RIOE.Pointed[R, error, A]()
|
||||
}
|
||||
|
||||
// Functor returns the functor operations for [ReaderIOResult]
|
||||
//
|
||||
//go:inline
|
||||
func Functor[R, A, B any]() functor.Functor[A, B, ReaderIOResult[R, A], ReaderIOResult[R, B]] {
|
||||
return RIOE.Functor[R, error, A, B]()
|
||||
}
|
||||
|
||||
// Monad returns the monadic operations for [ReaderIOResult]
|
||||
//
|
||||
//go:inline
|
||||
func Monad[R, A, B any]() monad.Monad[A, B, ReaderIOResult[R, A], ReaderIOResult[R, B], ReaderIOResult[R, func(A) B]] {
|
||||
return RIOE.Monad[R, error, A, B]()
|
||||
}
|
||||
586
v2/readerioresult/reader.go
Normal file
586
v2/readerioresult/reader.go
Normal file
@@ -0,0 +1,586 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
RE "github.com/IBM/fp-go/v2/readereither"
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
)
|
||||
|
||||
// MonadFromReaderIO creates a ReaderIOResult from a value and a function that produces a ReaderIO.
|
||||
// The ReaderIO result is lifted into the Right side of the Either.
|
||||
func MonadFromReaderIO[R, A any](a A, f func(A) ReaderIO[R, A]) ReaderIOResult[R, A] {
|
||||
return RIOE.MonadFromReaderIO[R, error](a, f)
|
||||
}
|
||||
|
||||
// FromReaderIO creates a function that lifts a ReaderIO-producing function into ReaderIOResult.
|
||||
// The ReaderIO result is placed in the Right side of the Either.
|
||||
func FromReaderIO[R, A any](f func(A) ReaderIO[R, A]) Kleisli[R, A, A] {
|
||||
return RIOE.FromReaderIO[R, error](f)
|
||||
}
|
||||
|
||||
// RightReaderIO lifts a ReaderIO into a ReaderIOResult, placing the result in the Right side.
|
||||
//
|
||||
//go:inline
|
||||
func RightReaderIO[R, A any](ma ReaderIO[R, A]) ReaderIOResult[R, A] {
|
||||
return RIOE.RightReaderIO[R, error](ma)
|
||||
}
|
||||
|
||||
// LeftReaderIO lifts a ReaderIO into a ReaderIOResult, placing the result in the Left (error) side.
|
||||
//
|
||||
//go:inline
|
||||
func LeftReaderIO[A, R any](me ReaderIO[R, error]) ReaderIOResult[R, A] {
|
||||
return RIOE.LeftReaderIO[A](me)
|
||||
}
|
||||
|
||||
// MonadMap applies a function to the value inside a ReaderIOResult context.
|
||||
// If the computation is successful (Right), the function is applied to the value.
|
||||
// If it's an error (Left), the error is propagated unchanged.
|
||||
//
|
||||
//go:inline
|
||||
func MonadMap[R, A, B any](fa ReaderIOResult[R, A], f func(A) B) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadMap(fa, f)
|
||||
}
|
||||
|
||||
// Map returns a function that applies a transformation to the success value of a ReaderIOResult.
|
||||
// This is the curried version of MonadMap, useful for function composition.
|
||||
//
|
||||
//go:inline
|
||||
func Map[R, A, B any](f func(A) B) Operator[R, A, B] {
|
||||
return RIOE.Map[R, error](f)
|
||||
}
|
||||
|
||||
// MonadMapTo replaces the success value with a constant value.
|
||||
// Useful when you want to discard the result but keep the effect.
|
||||
//
|
||||
//go:inline
|
||||
func MonadMapTo[R, A, B any](fa ReaderIOResult[R, A], b B) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadMapTo(fa, b)
|
||||
}
|
||||
|
||||
// MapTo returns a function that replaces the success value with a constant.
|
||||
// This is the curried version of MonadMapTo.
|
||||
//
|
||||
//go:inline
|
||||
func MapTo[R, A, B any](b B) Operator[R, A, B] {
|
||||
return RIOE.MapTo[R, error, A](b)
|
||||
}
|
||||
|
||||
// MonadChain sequences two computations where the second depends on the result of the first.
|
||||
// This is the fundamental operation for composing dependent effectful computations.
|
||||
// If the first computation fails, the second is not executed.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChain[R, A, B any](fa ReaderIOResult[R, A], f Kleisli[R, A, B]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadChain(fa, f)
|
||||
}
|
||||
|
||||
// MonadChainFirst sequences two computations but keeps the result of the first.
|
||||
// Useful for performing side effects while preserving the original value.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainFirst[R, A, B any](fa ReaderIOResult[R, A], f Kleisli[R, A, B]) ReaderIOResult[R, A] {
|
||||
return RIOE.MonadChainFirst(fa, f)
|
||||
}
|
||||
|
||||
// MonadChainEitherK chains a computation that returns an Either into a ReaderIOResult.
|
||||
// The Either is automatically lifted into the ReaderIOResult context.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainEitherK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadChainEitherK(ma, f)
|
||||
}
|
||||
|
||||
// MonadChainEitherK chains a computation that returns an Either into a ReaderIOResult.
|
||||
// The Either is automatically lifted into the ReaderIOResult context.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainResultK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadChainEitherK(ma, f)
|
||||
}
|
||||
|
||||
// ChainEitherK returns a function that chains an Either-returning function into ReaderIOResult.
|
||||
// This is the curried version of MonadChainEitherK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainEitherK[R, A, B any](f func(A) Result[B]) Operator[R, A, B] {
|
||||
return RIOE.ChainEitherK[R](f)
|
||||
}
|
||||
|
||||
// ChainResultK returns a function that chains an Either-returning function into ReaderIOResult.
|
||||
// This is the curried version of MonadChainEitherK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainResultK[R, A, B any](f func(A) Result[B]) Operator[R, A, B] {
|
||||
return RIOE.ChainEitherK[R](f)
|
||||
}
|
||||
|
||||
// MonadChainFirstEitherK chains an Either-returning computation but keeps the original value.
|
||||
// Useful for validation or side effects that return Either.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainFirstEitherK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, A] {
|
||||
return RIOE.MonadChainFirstEitherK(ma, f)
|
||||
}
|
||||
|
||||
// ChainFirstEitherK returns a function that chains an Either computation while preserving the original value.
|
||||
// This is the curried version of MonadChainFirstEitherK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainFirstEitherK[R, A, B any](f func(A) Result[B]) Operator[R, A, A] {
|
||||
return RIOE.ChainFirstEitherK[R](f)
|
||||
}
|
||||
|
||||
// MonadChainFirstEitherK chains an Either-returning computation but keeps the original value.
|
||||
// Useful for validation or side effects that return Either.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainFirstResultK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, A] {
|
||||
return RIOE.MonadChainFirstEitherK(ma, f)
|
||||
}
|
||||
|
||||
// ChainFirstEitherK returns a function that chains an Either computation while preserving the original value.
|
||||
// This is the curried version of MonadChainFirstEitherK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainFirstResultK[R, A, B any](f func(A) Result[B]) Operator[R, A, A] {
|
||||
return RIOE.ChainFirstEitherK[R](f)
|
||||
}
|
||||
|
||||
// MonadChainReaderK chains a Reader-returning computation into a ReaderIOResult.
|
||||
// The Reader is automatically lifted into the ReaderIOResult context.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainReaderK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Reader[R, B]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadChainReaderK(ma, f)
|
||||
}
|
||||
|
||||
// ChainReaderK returns a function that chains a Reader-returning function into ReaderIOResult.
|
||||
// This is the curried version of MonadChainReaderK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainReaderK[R, A, B any](f func(A) Reader[R, B]) Operator[R, A, B] {
|
||||
return RIOE.ChainReaderK[error](f)
|
||||
}
|
||||
|
||||
// MonadChainIOEitherK chains an IOEither-returning computation into a ReaderIOResult.
|
||||
// The IOEither is automatically lifted into the ReaderIOResult context.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainIOEitherK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IOResult[B]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadChainIOEitherK(ma, f)
|
||||
}
|
||||
|
||||
// MonadChainIOEitherK chains an IOEither-returning computation into a ReaderIOResult.
|
||||
// The IOEither is automatically lifted into the ReaderIOResult context.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainIOResultK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IOResult[B]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadChainIOEitherK(ma, f)
|
||||
}
|
||||
|
||||
// ChainIOEitherK returns a function that chains an IOEither-returning function into ReaderIOResult.
|
||||
// This is the curried version of MonadChainIOEitherK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainIOEitherK[R, A, B any](f func(A) IOResult[B]) Operator[R, A, B] {
|
||||
return RIOE.ChainIOEitherK[R](f)
|
||||
}
|
||||
|
||||
// ChainIOEitherK returns a function that chains an IOEither-returning function into ReaderIOResult.
|
||||
// This is the curried version of MonadChainIOEitherK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainIOResultK[R, A, B any](f func(A) IOResult[B]) Operator[R, A, B] {
|
||||
return RIOE.ChainIOEitherK[R](f)
|
||||
}
|
||||
|
||||
// MonadChainIOK chains an IO-returning computation into a ReaderIOResult.
|
||||
// The IO is automatically lifted into the ReaderIOResult context (always succeeds).
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainIOK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IO[B]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadChainIOK(ma, f)
|
||||
}
|
||||
|
||||
// ChainIOK returns a function that chains an IO-returning function into ReaderIOResult.
|
||||
// This is the curried version of MonadChainIOK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainIOK[R, A, B any](f func(A) IO[B]) Operator[R, A, B] {
|
||||
return RIOE.ChainIOK[R, error](f)
|
||||
}
|
||||
|
||||
// MonadChainFirstIOK chains an IO computation but keeps the original value.
|
||||
// Useful for performing IO side effects while preserving the original value.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainFirstIOK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IO[B]) ReaderIOResult[R, A] {
|
||||
return RIOE.MonadChainFirstIOK(ma, f)
|
||||
}
|
||||
|
||||
// ChainFirstIOK returns a function that chains an IO computation while preserving the original value.
|
||||
// This is the curried version of MonadChainFirstIOK.
|
||||
//
|
||||
//go:inline
|
||||
func ChainFirstIOK[R, A, B any](f func(A) IO[B]) Operator[R, A, A] {
|
||||
return RIOE.ChainFirstIOK[R, error](f)
|
||||
}
|
||||
|
||||
// ChainOptionK returns a function that chains an Option-returning function into ReaderIOResult.
|
||||
// If the Option is None, the provided error function is called to produce the error value.
|
||||
//
|
||||
//go:inline
|
||||
func ChainOptionK[R, A, B any](onNone func() error) func(func(A) Option[B]) Operator[R, A, B] {
|
||||
return RIOE.ChainOptionK[R, A, B](onNone)
|
||||
}
|
||||
|
||||
// MonadAp applies a function wrapped in a context to a value wrapped in a context.
|
||||
// Both computations are executed (default behavior may be sequential or parallel depending on implementation).
|
||||
//
|
||||
//go:inline
|
||||
func MonadAp[R, A, B any](fab ReaderIOResult[R, func(A) B], fa ReaderIOResult[R, A]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadAp(fab, fa)
|
||||
}
|
||||
|
||||
// MonadApSeq applies a function in a context to a value in a context, executing them sequentially.
|
||||
//
|
||||
//go:inline
|
||||
func MonadApSeq[R, A, B any](fab ReaderIOResult[R, func(A) B], fa ReaderIOResult[R, A]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadApSeq(fab, fa)
|
||||
}
|
||||
|
||||
// MonadApPar applies a function in a context to a value in a context, executing them in parallel.
|
||||
//
|
||||
//go:inline
|
||||
func MonadApPar[R, A, B any](fab ReaderIOResult[R, func(A) B], fa ReaderIOResult[R, A]) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadApPar(fab, fa)
|
||||
}
|
||||
|
||||
// Ap returns a function that applies a function in a context to a value in a context.
|
||||
// This is the curried version of MonadAp.
|
||||
//
|
||||
//go:inline
|
||||
func Ap[B, R, A any](fa ReaderIOResult[R, A]) Operator[R, func(A) B, B] {
|
||||
return RIOE.Ap[B](fa)
|
||||
|
||||
}
|
||||
|
||||
// Chain returns a function that sequences computations where the second depends on the first.
|
||||
// This is the curried version of MonadChain.
|
||||
//
|
||||
//go:inline
|
||||
func Chain[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, B] {
|
||||
return RIOE.Chain(f)
|
||||
}
|
||||
|
||||
// ChainFirst returns a function that sequences computations but keeps the first result.
|
||||
// This is the curried version of MonadChainFirst.
|
||||
//
|
||||
//go:inline
|
||||
func ChainFirst[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, A] {
|
||||
return RIOE.ChainFirst(f)
|
||||
}
|
||||
|
||||
// Right creates a successful ReaderIOResult with the given value.
|
||||
//
|
||||
//go:inline
|
||||
func Right[R, A any](a A) ReaderIOResult[R, A] {
|
||||
return RIOE.Right[R, error](a)
|
||||
}
|
||||
|
||||
// Left creates a failed ReaderIOResult with the given error.
|
||||
//
|
||||
//go:inline
|
||||
func Left[R, A any](e error) ReaderIOResult[R, A] {
|
||||
return RIOE.Left[R, A](e)
|
||||
}
|
||||
|
||||
// ThrowError creates a failed ReaderIOResult with the given error.
|
||||
// This is an alias for Left, following the naming convention from other functional libraries.
|
||||
//
|
||||
//go:inline
|
||||
func ThrowError[R, A any](e error) ReaderIOResult[R, A] {
|
||||
return RIOE.ThrowError[R, A](e)
|
||||
}
|
||||
|
||||
// Of creates a successful ReaderIOResult with the given value.
|
||||
// This is the pointed functor operation, lifting a pure value into the ReaderIOResult context.
|
||||
//
|
||||
//go:inline
|
||||
func Of[R, A any](a A) ReaderIOResult[R, A] {
|
||||
return RIOE.Of[R, error](a)
|
||||
}
|
||||
|
||||
// Flatten removes one level of nesting from a nested ReaderIOResult.
|
||||
// Converts ReaderIOResult[R, ReaderIOResult[R, A]] to ReaderIOResult[R, A].
|
||||
//
|
||||
//go:inline
|
||||
func Flatten[R, A any](mma ReaderIOResult[R, ReaderIOResult[R, A]]) ReaderIOResult[R, A] {
|
||||
return RIOE.Flatten(mma)
|
||||
}
|
||||
|
||||
// FromEither lifts an Either into a ReaderIOResult context.
|
||||
// The Either value is independent of any context or IO effects.
|
||||
//
|
||||
//go:inline
|
||||
func FromEither[R, A any](t Result[A]) ReaderIOResult[R, A] {
|
||||
return RIOE.FromEither[R](t)
|
||||
}
|
||||
|
||||
// FromResult lifts an Either into a ReaderIOResult context.
|
||||
// The Either value is independent of any context or IO effects.
|
||||
//
|
||||
//go:inline
|
||||
func FromResult[R, A any](t Result[A]) ReaderIOResult[R, A] {
|
||||
return RIOE.FromEither[R](t)
|
||||
}
|
||||
|
||||
// RightReader lifts a Reader into a ReaderIOResult, placing the result in the Right side.
|
||||
//
|
||||
//go:inline
|
||||
func RightReader[R, A any](ma Reader[R, A]) ReaderIOResult[R, A] {
|
||||
return RIOE.RightReader[error](ma)
|
||||
}
|
||||
|
||||
// LeftReader lifts a Reader into a ReaderIOResult, placing the result in the Left (error) side.
|
||||
//
|
||||
//go:inline
|
||||
func LeftReader[A, R any](ma Reader[R, error]) ReaderIOResult[R, A] {
|
||||
return RIOE.LeftReader[A](ma)
|
||||
}
|
||||
|
||||
// FromReader lifts a Reader into a ReaderIOResult context.
|
||||
// The Reader result is placed in the Right side (success).
|
||||
//
|
||||
//go:inline
|
||||
func FromReader[R, A any](ma Reader[R, A]) ReaderIOResult[R, A] {
|
||||
return RIOE.FromReader[error](ma)
|
||||
}
|
||||
|
||||
// RightIO lifts an IO into a ReaderIOResult, placing the result in the Right side.
|
||||
//
|
||||
//go:inline
|
||||
func RightIO[R, A any](ma IO[A]) ReaderIOResult[R, A] {
|
||||
return RIOE.RightIO[R, error](ma)
|
||||
}
|
||||
|
||||
// LeftIO lifts an IO into a ReaderIOResult, placing the result in the Left (error) side.
|
||||
//
|
||||
//go:inline
|
||||
func LeftIO[R, A any](ma IO[error]) ReaderIOResult[R, A] {
|
||||
return RIOE.LeftIO[R, A](ma)
|
||||
}
|
||||
|
||||
// FromIO lifts an IO into a ReaderIOResult context.
|
||||
// The IO result is placed in the Right side (success).
|
||||
//
|
||||
//go:inline
|
||||
func FromIO[R, A any](ma IO[A]) ReaderIOResult[R, A] {
|
||||
return RIOE.FromIO[R, error](ma)
|
||||
}
|
||||
|
||||
// FromIOEither lifts an IOEither into a ReaderIOResult context.
|
||||
// The computation becomes independent of any reader context.
|
||||
//
|
||||
//go:inline
|
||||
func FromIOEither[R, A any](ma IOResult[A]) ReaderIOResult[R, A] {
|
||||
return RIOE.FromIOEither[R](ma)
|
||||
}
|
||||
|
||||
// FromIOEither lifts an IOEither into a ReaderIOResult context.
|
||||
// The computation becomes independent of any reader context.
|
||||
//
|
||||
//go:inline
|
||||
func FromIOResult[R, A any](ma IOResult[A]) ReaderIOResult[R, A] {
|
||||
return RIOE.FromIOEither[R](ma)
|
||||
}
|
||||
|
||||
// FromReaderEither lifts a ReaderEither into a ReaderIOResult context.
|
||||
// The Either result is lifted into an IO effect.
|
||||
//
|
||||
//go:inline
|
||||
func FromReaderEither[R, A any](ma RE.ReaderEither[R, error, A]) ReaderIOResult[R, A] {
|
||||
return RIOE.FromReaderEither(ma)
|
||||
}
|
||||
|
||||
// Ask returns a ReaderIOResult that retrieves the current context.
|
||||
// Useful for accessing configuration or dependencies.
|
||||
//
|
||||
//go:inline
|
||||
func Ask[R any]() ReaderIOResult[R, R] {
|
||||
return RIOE.Ask[R, error]()
|
||||
}
|
||||
|
||||
// Asks returns a ReaderIOResult that retrieves a value derived from the context.
|
||||
// This is useful for extracting specific fields from a configuration object.
|
||||
//
|
||||
//go:inline
|
||||
func Asks[R, A any](r Reader[R, A]) ReaderIOResult[R, A] {
|
||||
return RIOE.Asks[error](r)
|
||||
}
|
||||
|
||||
// FromOption converts an Option to a ReaderIOResult.
|
||||
// If the Option is None, the provided function is called to produce the error.
|
||||
//
|
||||
//go:inline
|
||||
func FromOption[R, A any](onNone func() error) Kleisli[R, Option[A], A] {
|
||||
return RIOE.FromOption[R, A](onNone)
|
||||
}
|
||||
|
||||
// FromPredicate creates a ReaderIOResult from a predicate.
|
||||
// If the predicate returns false, the onFalse function is called to produce the error.
|
||||
//
|
||||
//go:inline
|
||||
func FromPredicate[R, A any](pred func(A) bool, onFalse func(A) error) Kleisli[R, A, A] {
|
||||
return RIOE.FromPredicate[R](pred, onFalse)
|
||||
}
|
||||
|
||||
// Fold handles both success and error cases, producing a ReaderIO.
|
||||
// This is useful for converting a ReaderIOResult into a ReaderIO by handling all cases.
|
||||
//
|
||||
//go:inline
|
||||
func Fold[R, A, B any](onLeft func(error) ReaderIO[R, B], onRight func(A) ReaderIO[R, B]) func(ReaderIOResult[R, A]) ReaderIO[R, B] {
|
||||
return RIOE.Fold(onLeft, onRight)
|
||||
}
|
||||
|
||||
// GetOrElse provides a default value in case of error.
|
||||
// The default is computed lazily via a ReaderIO.
|
||||
//
|
||||
//go:inline
|
||||
func GetOrElse[R, A any](onLeft func(error) ReaderIO[R, A]) func(ReaderIOResult[R, A]) ReaderIO[R, A] {
|
||||
return RIOE.GetOrElse(onLeft)
|
||||
}
|
||||
|
||||
// OrElse tries an alternative computation if the first one fails.
|
||||
// The alternative can produce a different error type.
|
||||
//
|
||||
//go:inline
|
||||
func OrElse[R, A, E any](onLeft func(error) RIOE.ReaderIOEither[R, E, A]) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, A] {
|
||||
return RIOE.OrElse(onLeft)
|
||||
}
|
||||
|
||||
// OrLeft transforms the error using a ReaderIO if the computation fails.
|
||||
// The success value is preserved unchanged.
|
||||
//
|
||||
//go:inline
|
||||
func OrLeft[A, R, E any](onLeft func(error) ReaderIO[R, E]) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, A] {
|
||||
return RIOE.OrLeft[A](onLeft)
|
||||
}
|
||||
|
||||
// MonadBiMap applies two functions: one to the error, one to the success value.
|
||||
// This allows transforming both channels simultaneously.
|
||||
//
|
||||
//go:inline
|
||||
func MonadBiMap[R, E, A, B any](fa ReaderIOResult[R, A], f func(error) E, g func(A) B) RIOE.ReaderIOEither[R, E, B] {
|
||||
return RIOE.MonadBiMap(fa, f, g)
|
||||
}
|
||||
|
||||
// BiMap returns a function that maps over both the error and success channels.
|
||||
// This is the curried version of MonadBiMap.
|
||||
//
|
||||
//go:inline
|
||||
func BiMap[R, E, A, B any](f func(error) E, g func(A) B) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, B] {
|
||||
return RIOE.BiMap[R](f, g)
|
||||
}
|
||||
|
||||
// Swap exchanges the error and success types.
|
||||
// Left becomes Right and Right becomes Left.
|
||||
//
|
||||
//go:inline
|
||||
func Swap[R, A any](val ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, A, error] {
|
||||
return RIOE.Swap(val)
|
||||
}
|
||||
|
||||
// Defer creates a ReaderIOResult lazily via a generator function.
|
||||
// The generator is called each time the ReaderIOResult is executed.
|
||||
//
|
||||
//go:inline
|
||||
func Defer[R, A any](gen Lazy[ReaderIOResult[R, A]]) ReaderIOResult[R, A] {
|
||||
return RIOE.Defer(gen)
|
||||
}
|
||||
|
||||
// TryCatch wraps a function that returns (value, error) into a ReaderIOResult.
|
||||
// The onThrow function converts the error into the desired error type.
|
||||
//
|
||||
//go:inline
|
||||
func TryCatch[R, A any](f func(R) func() (A, error), onThrow Endomorphism[error]) ReaderIOResult[R, A] {
|
||||
return RIOE.TryCatch(f, onThrow)
|
||||
}
|
||||
|
||||
// MonadAlt tries the first computation, and if it fails, tries the second.
|
||||
// This implements the Alternative pattern for error recovery.
|
||||
//
|
||||
//go:inline
|
||||
func MonadAlt[R, A any](first ReaderIOResult[R, A], second Lazy[ReaderIOResult[R, A]]) ReaderIOResult[R, A] {
|
||||
return RIOE.MonadAlt(first, second)
|
||||
}
|
||||
|
||||
// Alt returns a function that tries an alternative computation if the first fails.
|
||||
// This is the curried version of MonadAlt.
|
||||
//
|
||||
//go:inline
|
||||
func Alt[R, A any](second Lazy[ReaderIOResult[R, A]]) Operator[R, A, A] {
|
||||
return RIOE.Alt(second)
|
||||
}
|
||||
|
||||
// Memoize computes the value of the ReaderIOResult lazily but exactly once.
|
||||
// The context used is from the first call. Do not use if the value depends on the context.
|
||||
//
|
||||
//go:inline
|
||||
func Memoize[R, A any](rdr ReaderIOResult[R, A]) ReaderIOResult[R, A] {
|
||||
return RIOE.Memoize(rdr)
|
||||
}
|
||||
|
||||
// MonadFlap applies a value to a function wrapped in a context.
|
||||
// This is the reverse of Ap - the value is fixed and the function varies.
|
||||
//
|
||||
//go:inline
|
||||
func MonadFlap[R, B, A any](fab ReaderIOResult[R, func(A) B], a A) ReaderIOResult[R, B] {
|
||||
return RIOE.MonadFlap(fab, a)
|
||||
}
|
||||
|
||||
// Flap returns a function that applies a fixed value to a function in a context.
|
||||
// This is the curried version of MonadFlap.
|
||||
//
|
||||
//go:inline
|
||||
func Flap[R, B, A any](a A) Operator[R, func(A) B, B] {
|
||||
return RIOE.Flap[R, error, B](a)
|
||||
}
|
||||
|
||||
// MonadMapLeft applies a function to the error value, leaving success unchanged.
|
||||
//
|
||||
//go:inline
|
||||
func MonadMapLeft[R, E, A any](fa ReaderIOResult[R, A], f func(error) E) RIOE.ReaderIOEither[R, E, A] {
|
||||
return RIOE.MonadMapLeft(fa, f)
|
||||
}
|
||||
|
||||
// MapLeft returns a function that transforms the error channel.
|
||||
// This is the curried version of MonadMapLeft.
|
||||
//
|
||||
//go:inline
|
||||
func MapLeft[R, A, E any](f func(error) E) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, A] {
|
||||
return RIOE.MapLeft[R, A](f)
|
||||
}
|
||||
|
||||
// Local runs a computation with a modified context.
|
||||
// The function f transforms the context before passing it to the computation.
|
||||
// This is similar to Contravariant's contramap operation.
|
||||
//
|
||||
//go:inline
|
||||
func Local[A, R1, R2 any](f func(R2) R1) func(ReaderIOResult[R1, A]) ReaderIOResult[R2, A] {
|
||||
return RIOE.Local[error, A](f)
|
||||
}
|
||||
59
v2/readerioresult/reader_test.go
Normal file
59
v2/readerioresult/reader_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/internal/utils"
|
||||
R "github.com/IBM/fp-go/v2/reader"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
|
||||
g := F.Pipe1(
|
||||
Of[context.Context](1),
|
||||
Map[context.Context](utils.Double),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of(2), g(context.Background())())
|
||||
}
|
||||
|
||||
func TestAp(t *testing.T) {
|
||||
g := F.Pipe1(
|
||||
Right[context.Context](utils.Double),
|
||||
Ap[int](Right[context.Context](1)),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of(2), g(context.Background())())
|
||||
}
|
||||
|
||||
func TestChainReaderK(t *testing.T) {
|
||||
|
||||
g := F.Pipe1(
|
||||
Of[context.Context](1),
|
||||
ChainReaderK(func(v int) R.Reader[context.Context, string] {
|
||||
return R.Of[context.Context](fmt.Sprintf("%d", v))
|
||||
}),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of("1"), g(context.Background())())
|
||||
}
|
||||
791
v2/readerioresult/readerioeither_test.go
Normal file
791
v2/readerioresult/readerioeither_test.go
Normal file
@@ -0,0 +1,791 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
E "github.com/IBM/fp-go/v2/either"
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/ioresult"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
R "github.com/IBM/fp-go/v2/reader"
|
||||
RE "github.com/IBM/fp-go/v2/readereither"
|
||||
RIO "github.com/IBM/fp-go/v2/readerio"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testContext struct {
|
||||
value int
|
||||
}
|
||||
|
||||
func TestMonadMap(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadMap(Of[testContext](5), func(x int) int { return x * 2 })
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadMapTo(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadMapTo(Of[testContext](5), 42)
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMapTo(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(Of[testContext](5), MapTo[testContext, int](42))
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadChainFirst(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadChainFirst(
|
||||
Of[testContext](5),
|
||||
func(x int) ReaderIOResult[testContext, string] {
|
||||
return Of[testContext](fmt.Sprintf("%d", x))
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestChainFirst(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
ChainFirst(func(x int) ReaderIOResult[testContext, string] {
|
||||
return Of[testContext](fmt.Sprintf("%d", x))
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadChainEitherK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadChainEitherK(
|
||||
Of[testContext](5),
|
||||
func(x int) E.Either[error, int] {
|
||||
return result.Of(x * 2)
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadChainFirstEitherK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadChainFirstEitherK(
|
||||
Of[testContext](5),
|
||||
func(x int) E.Either[error, string] {
|
||||
return result.Of(fmt.Sprintf("%d", x))
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestChainFirstEitherK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
ChainFirstEitherK[testContext](func(x int) E.Either[error, string] {
|
||||
return result.Of(fmt.Sprintf("%d", x))
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadChainReaderK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadChainReaderK(
|
||||
Of[testContext](5),
|
||||
func(x int) R.Reader[testContext, int] {
|
||||
return func(c testContext) int { return x + c.value }
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(15), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadChainIOEitherK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadChainIOEitherK(
|
||||
Of[testContext](5),
|
||||
func(x int) IOResult[int] {
|
||||
return ioresult.Of(x * 2)
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestChainIOEitherK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
ChainIOEitherK[testContext](func(x int) IOResult[int] {
|
||||
return ioresult.Of(x * 2)
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadChainIOK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadChainIOK(
|
||||
Of[testContext](5),
|
||||
func(x int) IO[int] {
|
||||
return func() int { return x * 2 }
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestChainIOK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
ChainIOK[testContext](func(x int) IO[int] {
|
||||
return func() int { return x * 2 }
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadChainFirstIOK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadChainFirstIOK(
|
||||
Of[testContext](5),
|
||||
func(x int) IO[string] {
|
||||
return func() string { return fmt.Sprintf("%d", x) }
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestChainFirstIOK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
ChainFirstIOK[testContext](func(x int) IO[string] {
|
||||
return func() string { return fmt.Sprintf("%d", x) }
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestChainOptionK(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test with Some
|
||||
resultSome := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
ChainOptionK[testContext, int, int](func() error {
|
||||
return errors.New("none")
|
||||
})(func(x int) Option[int] {
|
||||
return O.Some(x * 2)
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(10), resultSome(ctx)())
|
||||
|
||||
// Test with None
|
||||
resultNone := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
ChainOptionK[testContext, int, int](func() error {
|
||||
return errors.New("none")
|
||||
})(func(x int) Option[int] {
|
||||
return O.None[int]()
|
||||
}),
|
||||
)
|
||||
assert.True(t, E.IsLeft(resultNone(ctx)()))
|
||||
}
|
||||
|
||||
func TestMonadApSeq(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
fab := Of[testContext](func(x int) int { return x * 2 })
|
||||
fa := Of[testContext](5)
|
||||
res := MonadApSeq(fab, fa)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadApPar(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
fab := Of[testContext](func(x int) int { return x * 2 })
|
||||
fa := Of[testContext](5)
|
||||
res := MonadApPar(fab, fa)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestChain(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
Chain(func(x int) ReaderIOResult[testContext, int] {
|
||||
return Of[testContext](x * 2)
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestThrowError(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := ThrowError[testContext, int](errors.New("test error"))
|
||||
assert.True(t, E.IsLeft(result(ctx)()))
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
nested := Of[testContext](Of[testContext](5))
|
||||
res := Flatten(nested)
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestFromEither(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := FromEither[testContext](result.Of(5))
|
||||
assert.Equal(t, result.Of(5), res(ctx)())
|
||||
}
|
||||
|
||||
func TestRightReader(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
rdr := func(c testContext) int { return c.value }
|
||||
res := RightReader(rdr)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestLeftReader(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
reader := func(c testContext) error { return errors.New("test") }
|
||||
res := LeftReader[int](reader)
|
||||
assert.True(t, E.IsLeft(res(ctx)()))
|
||||
}
|
||||
|
||||
func TestRightIO(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
ioVal := func() int { return 42 }
|
||||
res := RightIO[testContext](ioVal)
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
}
|
||||
|
||||
func TestLeftIO(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
ioVal := func() error { return errors.New("test") }
|
||||
res := LeftIO[testContext, int](ioVal)
|
||||
assert.True(t, E.IsLeft(res(ctx)()))
|
||||
}
|
||||
|
||||
func TestFromIO(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
ioVal := func() int { return 42 }
|
||||
res := FromIO[testContext](ioVal)
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
}
|
||||
|
||||
func TestFromIOEither(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
ioe := ioresult.Of(42)
|
||||
res := FromIOEither[testContext](ioe)
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
}
|
||||
|
||||
func TestFromReaderEither(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
re := RE.Of[testContext, error](42)
|
||||
res := FromReaderEither(re)
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
}
|
||||
|
||||
func TestAsk(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := Ask[testContext]()
|
||||
assert.Equal(t, result.Of(ctx), res(ctx)())
|
||||
}
|
||||
|
||||
func TestAsks(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := Asks(func(c testContext) int { return c.value })
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestFromOption(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test with Some
|
||||
resultSome := FromOption[testContext, int](func() error {
|
||||
return errors.New("none")
|
||||
})(O.Some(42))
|
||||
assert.Equal(t, result.Of(42), resultSome(ctx)())
|
||||
|
||||
// Test with None
|
||||
resultNone := FromOption[testContext, int](func() error {
|
||||
return errors.New("none")
|
||||
})(O.None[int]())
|
||||
assert.True(t, E.IsLeft(resultNone(ctx)()))
|
||||
}
|
||||
|
||||
func TestFromPredicate(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test predicate true
|
||||
resultTrue := FromPredicate[testContext](
|
||||
func(x int) bool { return x > 0 },
|
||||
func(x int) error { return errors.New("negative") },
|
||||
)(5)
|
||||
assert.Equal(t, result.Of(5), resultTrue(ctx)())
|
||||
|
||||
// Test predicate false
|
||||
resultFalse := FromPredicate[testContext](
|
||||
func(x int) bool { return x > 0 },
|
||||
func(x int) error { return errors.New("negative") },
|
||||
)(-5)
|
||||
assert.True(t, E.IsLeft(resultFalse(ctx)()))
|
||||
}
|
||||
|
||||
func TestFold(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test Right case
|
||||
resultRight := Fold(
|
||||
func(e error) RIO.ReaderIO[testContext, string] {
|
||||
return RIO.Of[testContext]("error: " + e.Error())
|
||||
},
|
||||
func(x int) RIO.ReaderIO[testContext, string] {
|
||||
return RIO.Of[testContext](fmt.Sprintf("value: %d", x))
|
||||
},
|
||||
)(Of[testContext](42))
|
||||
assert.Equal(t, "value: 42", resultRight(ctx)())
|
||||
|
||||
// Test Left case
|
||||
resultLeft := Fold(
|
||||
func(e error) RIO.ReaderIO[testContext, string] {
|
||||
return RIO.Of[testContext]("error: " + e.Error())
|
||||
},
|
||||
func(x int) RIO.ReaderIO[testContext, string] {
|
||||
return RIO.Of[testContext](fmt.Sprintf("value: %d", x))
|
||||
},
|
||||
)(Left[testContext, int](errors.New("test")))
|
||||
assert.Equal(t, "error: test", resultLeft(ctx)())
|
||||
}
|
||||
|
||||
func TestGetOrElse(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test Right case
|
||||
resultRight := GetOrElse(func(e error) RIO.ReaderIO[testContext, int] {
|
||||
return RIO.Of[testContext](0)
|
||||
})(Of[testContext](42))
|
||||
assert.Equal(t, 42, resultRight(ctx)())
|
||||
|
||||
// Test Left case
|
||||
resultLeft := GetOrElse(func(e error) RIO.ReaderIO[testContext, int] {
|
||||
return RIO.Of[testContext](0)
|
||||
})(Left[testContext, int](errors.New("test")))
|
||||
assert.Equal(t, 0, resultLeft(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadBiMap(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test Right case
|
||||
resultRight := MonadBiMap(
|
||||
Of[testContext](5),
|
||||
func(e error) string { return e.Error() },
|
||||
func(x int) string { return fmt.Sprintf("%d", x) },
|
||||
)
|
||||
assert.Equal(t, E.Of[string]("5"), resultRight(ctx)())
|
||||
|
||||
// Test Left case
|
||||
resultLeft := MonadBiMap(
|
||||
Left[testContext, int](errors.New("test")),
|
||||
func(e error) string { return e.Error() },
|
||||
func(x int) string { return fmt.Sprintf("%d", x) },
|
||||
)
|
||||
assert.Equal(t, E.Left[string]("test"), resultLeft(ctx)())
|
||||
}
|
||||
|
||||
func TestBiMap(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](5),
|
||||
BiMap[testContext](
|
||||
func(e error) string { return e.Error() },
|
||||
func(x int) string { return fmt.Sprintf("%d", x) },
|
||||
),
|
||||
)
|
||||
assert.Equal(t, E.Of[string]("5"), res(ctx)())
|
||||
}
|
||||
|
||||
func TestSwap(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test Right becomes Left
|
||||
resultRight := Swap(Of[testContext](5))
|
||||
res := resultRight(ctx)()
|
||||
assert.True(t, E.IsLeft(res))
|
||||
|
||||
// Test Left becomes Right
|
||||
resultLeft := Swap(Left[testContext, int](errors.New("test")))
|
||||
assert.True(t, E.IsRight(resultLeft(ctx)()))
|
||||
}
|
||||
|
||||
func TestDefer(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
callCount := 0
|
||||
res := Defer(func() ReaderIOResult[testContext, int] {
|
||||
callCount++
|
||||
return Of[testContext](42)
|
||||
})
|
||||
|
||||
// First call
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
assert.Equal(t, 1, callCount)
|
||||
|
||||
// Second call
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
assert.Equal(t, 2, callCount)
|
||||
}
|
||||
|
||||
func TestMonadAlt(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
|
||||
// Test first succeeds
|
||||
resultFirst := MonadAlt(
|
||||
Of[testContext](42),
|
||||
func() ReaderIOResult[testContext, int] {
|
||||
return Of[testContext](99)
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(42), resultFirst(ctx)())
|
||||
|
||||
// Test first fails, second succeeds
|
||||
resultSecond := MonadAlt(
|
||||
Left[testContext, int](errors.New("first")),
|
||||
func() ReaderIOResult[testContext, int] {
|
||||
return Of[testContext](99)
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(99), resultSecond(ctx)())
|
||||
}
|
||||
|
||||
func TestAlt(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Left[testContext, int](errors.New("first")),
|
||||
Alt(func() ReaderIOResult[testContext, int] {
|
||||
return Of[testContext](99)
|
||||
}),
|
||||
)
|
||||
assert.Equal(t, result.Of(99), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMemoize(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
callCount := 0
|
||||
res := Memoize(func(c testContext) IOResult[int] {
|
||||
return func() E.Either[error, int] {
|
||||
callCount++
|
||||
return result.Of(c.value * 2)
|
||||
}
|
||||
})
|
||||
|
||||
// First call
|
||||
assert.Equal(t, result.Of(20), res(ctx)())
|
||||
assert.Equal(t, 1, callCount)
|
||||
|
||||
// Second call should use memoized value
|
||||
assert.Equal(t, result.Of(20), res(ctx)())
|
||||
assert.Equal(t, 1, callCount)
|
||||
}
|
||||
|
||||
func TestMonadFlap(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
fab := Of[testContext](func(x int) int { return x * 2 })
|
||||
res := MonadFlap(fab, 5)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestFlap(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := F.Pipe1(
|
||||
Of[testContext](func(x int) int { return x * 2 }),
|
||||
Flap[testContext, int](5),
|
||||
)
|
||||
assert.Equal(t, result.Of(10), res(ctx)())
|
||||
}
|
||||
|
||||
func TestMonadMapLeft(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := MonadMapLeft(
|
||||
Left[testContext, int](errors.New("test")),
|
||||
func(e error) string { return e.Error() + "!" },
|
||||
)
|
||||
res := result(ctx)()
|
||||
assert.True(t, E.IsLeft(res))
|
||||
// Verify the error was transformed
|
||||
E.Fold(
|
||||
func(s string) int {
|
||||
assert.Equal(t, "test!", s)
|
||||
return 0
|
||||
},
|
||||
func(i int) int { return i },
|
||||
)(res)
|
||||
}
|
||||
|
||||
func TestMapLeft(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := F.Pipe1(
|
||||
Left[testContext, int](errors.New("test")),
|
||||
MapLeft[testContext, int](func(e error) string { return e.Error() + "!" }),
|
||||
)
|
||||
res := result(ctx)()
|
||||
assert.True(t, E.IsLeft(res))
|
||||
// Verify the error was transformed
|
||||
E.Fold(
|
||||
func(s string) int {
|
||||
assert.Equal(t, "test!", s)
|
||||
return 0
|
||||
},
|
||||
func(i int) int { return i },
|
||||
)(res)
|
||||
}
|
||||
|
||||
func TestLocal(t *testing.T) {
|
||||
ctx2 := testContext{value: 20}
|
||||
|
||||
rdr := Asks(func(c testContext) int { return c.value })
|
||||
res := Local[int](func(c testContext) testContext {
|
||||
return testContext{value: c.value * 2}
|
||||
})(rdr)
|
||||
|
||||
assert.Equal(t, result.Of(40), res(ctx2)())
|
||||
}
|
||||
|
||||
func TestMonadFromReaderIO(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := MonadFromReaderIO(
|
||||
5,
|
||||
func(x int) RIO.ReaderIO[testContext, int] {
|
||||
return func(c testContext) IO[int] {
|
||||
return func() int { return x + c.value }
|
||||
}
|
||||
},
|
||||
)
|
||||
assert.Equal(t, result.Of(15), res(ctx)())
|
||||
}
|
||||
|
||||
func TestFromReaderIO(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := FromReaderIO(func(x int) RIO.ReaderIO[testContext, int] {
|
||||
return func(c testContext) IO[int] {
|
||||
return func() int { return x + c.value }
|
||||
}
|
||||
})(5)
|
||||
assert.Equal(t, result.Of(15), res(ctx)())
|
||||
}
|
||||
|
||||
func TestRightReaderIO(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
rio := func(c testContext) IO[int] {
|
||||
return func() int { return c.value * 2 }
|
||||
}
|
||||
res := RightReaderIO(rio)
|
||||
assert.Equal(t, result.Of(20), res(ctx)())
|
||||
}
|
||||
|
||||
func TestLeftReaderIO(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
rio := func(c testContext) IO[error] {
|
||||
return func() error { return errors.New("test") }
|
||||
}
|
||||
res := LeftReaderIO[int](rio)
|
||||
assert.True(t, E.IsLeft(res(ctx)()))
|
||||
}
|
||||
|
||||
func TestLet(t *testing.T) {
|
||||
type State struct {
|
||||
a int
|
||||
b string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
res := F.Pipe2(
|
||||
Do[context.Context](State{}),
|
||||
Let[context.Context](func(b string) func(State) State {
|
||||
return func(s State) State { return State{a: s.a, b: b} }
|
||||
}, func(s State) string { return "test" }),
|
||||
Map[context.Context](func(s State) string { return s.b }),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of("test"), res(ctx)())
|
||||
}
|
||||
|
||||
func TestLetTo(t *testing.T) {
|
||||
type State struct {
|
||||
a int
|
||||
b string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
res := F.Pipe2(
|
||||
Do[context.Context](State{}),
|
||||
LetTo[context.Context](func(b string) func(State) State {
|
||||
return func(s State) State { return State{a: s.a, b: b} }
|
||||
}, "constant"),
|
||||
Map[context.Context](func(s State) string { return s.b }),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of("constant"), res(ctx)())
|
||||
}
|
||||
|
||||
func TestBindTo(t *testing.T) {
|
||||
type State struct {
|
||||
value int
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
res := F.Pipe2(
|
||||
Of[context.Context](42),
|
||||
BindTo[context.Context](func(v int) State { return State{value: v} }),
|
||||
Map[context.Context](func(s State) int { return s.value }),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
}
|
||||
|
||||
func TestBracket(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
released := false
|
||||
|
||||
res := Bracket(
|
||||
Of[testContext](42),
|
||||
func(x int) ReaderIOResult[testContext, string] {
|
||||
return Of[testContext](fmt.Sprintf("%d", x))
|
||||
},
|
||||
func(x int, result E.Either[error, string]) ReaderIOResult[testContext, int] {
|
||||
released = true
|
||||
return Of[testContext](0)
|
||||
},
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of("42"), res(ctx)())
|
||||
assert.True(t, released)
|
||||
}
|
||||
|
||||
func TestWithResource(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
released := false
|
||||
|
||||
res := WithResource[string](
|
||||
Of[testContext](42),
|
||||
func(x int) ReaderIOResult[testContext, int] {
|
||||
released = true
|
||||
return Of[testContext](0)
|
||||
},
|
||||
)(func(x int) ReaderIOResult[testContext, string] {
|
||||
return Of[testContext](fmt.Sprintf("%d", x))
|
||||
})
|
||||
|
||||
assert.Equal(t, result.Of("42"), res(ctx)())
|
||||
assert.True(t, released)
|
||||
}
|
||||
|
||||
func TestMonad(t *testing.T) {
|
||||
m := Monad[testContext, int, string]()
|
||||
assert.NotNil(t, m)
|
||||
}
|
||||
|
||||
func TestTraverseArrayWithIndex(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := TraverseArrayWithIndex(func(i int, x int) ReaderIOResult[testContext, int] {
|
||||
return Of[testContext](x + i)
|
||||
})([]int{1, 2, 3})
|
||||
|
||||
assert.Equal(t, result.Of([]int{1, 3, 5}), res(ctx)())
|
||||
}
|
||||
|
||||
func TestTraverseRecord(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := TraverseRecord[string](func(x int) ReaderIOResult[testContext, int] {
|
||||
return Of[testContext](x * 2)
|
||||
})(map[string]int{"a": 1, "b": 2})
|
||||
|
||||
expected := map[string]int{"a": 2, "b": 4}
|
||||
assert.Equal(t, result.Of(expected), res(ctx)())
|
||||
}
|
||||
|
||||
func TestTraverseRecordWithIndex(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := TraverseRecordWithIndex(func(k string, x int) ReaderIOResult[testContext, string] {
|
||||
return Of[testContext](fmt.Sprintf("%s:%d", k, x))
|
||||
})(map[string]int{"a": 1, "b": 2})
|
||||
|
||||
assert.True(t, E.IsRight(res(ctx)()))
|
||||
}
|
||||
|
||||
func TestSequenceRecord(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
res := SequenceRecord(map[string]ReaderIOResult[testContext, int]{
|
||||
"a": Of[testContext](1),
|
||||
"b": Of[testContext](2),
|
||||
})
|
||||
|
||||
expected := map[string]int{"a": 1, "b": 2}
|
||||
assert.Equal(t, result.Of(expected), res(ctx)())
|
||||
}
|
||||
|
||||
func TestSequenceT1(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := SequenceT1(Of[testContext](42))
|
||||
res := result(ctx)()
|
||||
assert.True(t, E.IsRight(res))
|
||||
}
|
||||
|
||||
func TestSequenceT3(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := SequenceT3(
|
||||
Of[testContext](1),
|
||||
Of[testContext]("a"),
|
||||
Of[testContext](true),
|
||||
)
|
||||
res := result(ctx)()
|
||||
assert.True(t, E.IsRight(res))
|
||||
}
|
||||
|
||||
func TestSequenceT4(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
result := SequenceT4(
|
||||
Of[testContext](1),
|
||||
Of[testContext]("a"),
|
||||
Of[testContext](true),
|
||||
Of[testContext](3.14),
|
||||
)
|
||||
res := result(ctx)()
|
||||
assert.True(t, E.IsRight(res))
|
||||
}
|
||||
|
||||
func TestWithLock(t *testing.T) {
|
||||
ctx := testContext{value: 10}
|
||||
unlocked := false
|
||||
|
||||
res := F.Pipe1(
|
||||
Of[testContext](42),
|
||||
WithLock[testContext, int](func() context.CancelFunc {
|
||||
return func() { unlocked = true }
|
||||
}),
|
||||
)
|
||||
|
||||
assert.Equal(t, result.Of(42), res(ctx)())
|
||||
assert.True(t, unlocked)
|
||||
}
|
||||
58
v2/readerioresult/resource.go
Normal file
58
v2/readerioresult/resource.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
)
|
||||
|
||||
// WithResource constructs a function that creates a resource, operates on it, and then releases the resource.
|
||||
// This ensures proper resource cleanup even in the presence of errors, following the Resource Acquisition Is Initialization (RAII) pattern.
|
||||
//
|
||||
// The resource lifecycle is:
|
||||
// 1. onCreate: Acquires the resource
|
||||
// 2. use: Operates on the resource (provided as argument to the returned function)
|
||||
// 3. onRelease: Releases the resource (called regardless of success or failure)
|
||||
//
|
||||
// Type parameters:
|
||||
// - A: The type of the result produced by using the resource
|
||||
// - L: The context type
|
||||
// - E: The error type
|
||||
// - R: The resource type
|
||||
// - ANY: The type returned by the release function (typically ignored)
|
||||
//
|
||||
// Parameters:
|
||||
// - onCreate: A computation that acquires the resource
|
||||
// - onRelease: A function that releases the resource, called with the resource and executed regardless of errors
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// A function that takes a resource-using function and returns a ReaderIOResult that manages the resource lifecycle
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// withFile := WithResource(
|
||||
// openFile("data.txt"),
|
||||
// func(f *File) ReaderIOResult[Config, error, int] {
|
||||
// return closeFile(f)
|
||||
// },
|
||||
// )
|
||||
// result := withFile(func(f *File) ReaderIOResult[Config, error, string] {
|
||||
// return readContent(f)
|
||||
// })
|
||||
func WithResource[A, L, R, ANY any](onCreate ReaderIOResult[L, R], onRelease Kleisli[L, R, ANY]) Kleisli[L, Kleisli[L, R, A], A] {
|
||||
return RIOE.WithResource[A](onCreate, onRelease)
|
||||
}
|
||||
93
v2/readerioresult/sequence.go
Normal file
93
v2/readerioresult/sequence.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
T "github.com/IBM/fp-go/v2/tuple"
|
||||
)
|
||||
|
||||
// SequenceT1 converts a single ReaderIOResult into a ReaderIOResult of a 1-tuple.
|
||||
// This is useful for uniformly handling computations with different arities.
|
||||
//
|
||||
// If the input computation fails, the result will be a Left with the error.
|
||||
// If it succeeds, the result will be a Right with a tuple containing the value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := SequenceT1(Of[Config, error](42))
|
||||
// // result(cfg)() returns Right(Tuple1{42})
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT1[R, A any](a ReaderIOResult[R, A]) ReaderIOResult[R, T.Tuple1[A]] {
|
||||
return RIOE.SequenceT1(a)
|
||||
}
|
||||
|
||||
// SequenceT2 combines two ReaderIOResult computations into a single ReaderIOResult of a 2-tuple.
|
||||
// Both computations are executed, and if both succeed, their results are combined into a tuple.
|
||||
// If either fails, the result is a Left with the first error encountered.
|
||||
//
|
||||
// This is useful for running multiple independent computations and collecting their results.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := SequenceT2(
|
||||
// fetchUser(123),
|
||||
// fetchProfile(123),
|
||||
// )
|
||||
// // result(cfg)() returns Right(Tuple2{user, profile}) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT2[R, A, B any](a ReaderIOResult[R, A], b ReaderIOResult[R, B]) ReaderIOResult[R, T.Tuple2[A, B]] {
|
||||
return RIOE.SequenceT2(a, b)
|
||||
}
|
||||
|
||||
// SequenceT3 combines three ReaderIOResult computations into a single ReaderIOResult of a 3-tuple.
|
||||
// All three computations are executed, and if all succeed, their results are combined into a tuple.
|
||||
// If any fails, the result is a Left with the first error encountered.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := SequenceT3(
|
||||
// fetchUser(123),
|
||||
// fetchProfile(123),
|
||||
// fetchSettings(123),
|
||||
// )
|
||||
// // result(cfg)() returns Right(Tuple3{user, profile, settings}) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT3[R, A, B, C any](a ReaderIOResult[R, A], b ReaderIOResult[R, B], c ReaderIOResult[R, C]) ReaderIOResult[R, T.Tuple3[A, B, C]] {
|
||||
return RIOE.SequenceT3(a, b, c)
|
||||
}
|
||||
|
||||
// SequenceT4 combines four ReaderIOResult computations into a single ReaderIOResult of a 4-tuple.
|
||||
// All four computations are executed, and if all succeed, their results are combined into a tuple.
|
||||
// If any fails, the result is a Left with the first error encountered.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := SequenceT4(
|
||||
// fetchUser(123),
|
||||
// fetchProfile(123),
|
||||
// fetchSettings(123),
|
||||
// fetchPreferences(123),
|
||||
// )
|
||||
// // result(cfg)() returns Right(Tuple4{user, profile, settings, prefs}) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func SequenceT4[R, A, B, C, D any](a ReaderIOResult[R, A], b ReaderIOResult[R, B], c ReaderIOResult[R, C], d ReaderIOResult[R, D]) ReaderIOResult[R, T.Tuple4[A, B, C, D]] {
|
||||
return RIOE.SequenceT4(a, b, c, d)
|
||||
}
|
||||
38
v2/readerioresult/sequence_test.go
Normal file
38
v2/readerioresult/sequence_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
T "github.com/IBM/fp-go/v2/tuple"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSequence2(t *testing.T) {
|
||||
// two readers of heterogeneous types
|
||||
first := Of[context.Context]("a")
|
||||
second := Of[context.Context](1)
|
||||
|
||||
// compose
|
||||
s2 := SequenceT2[context.Context, string, int]
|
||||
res := s2(first, second)
|
||||
|
||||
ctx := context.Background()
|
||||
assert.Equal(t, either.Right[error](T.MakeTuple2("a", 1)), res(ctx)())
|
||||
}
|
||||
57
v2/readerioresult/sync.go
Normal file
57
v2/readerioresult/sync.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/IBM/fp-go/v2/readerio"
|
||||
)
|
||||
|
||||
// WithLock executes a ReaderIOResult operation within the scope of a lock.
|
||||
// The lock is acquired before the operation executes and released after it completes,
|
||||
// regardless of whether the operation succeeds or fails.
|
||||
//
|
||||
// This is useful for ensuring thread-safe access to shared resources or for
|
||||
// implementing critical sections in concurrent code.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - E: The error type
|
||||
// - A: The value type
|
||||
//
|
||||
// Parameters:
|
||||
// - lock: A function that acquires a lock and returns a CancelFunc to release it
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// An Operator that wraps the computation with lock acquisition and release
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var mu sync.Mutex
|
||||
// safeFetch := F.Pipe1(
|
||||
// fetchData(),
|
||||
// WithLock[Config, error, Data](func() context.CancelFunc {
|
||||
// mu.Lock()
|
||||
// return func() { mu.Unlock() }
|
||||
// }),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func WithLock[R, A any](lock func() context.CancelFunc) Operator[R, A, A] {
|
||||
return readerio.WithLock[R, Result[A]](lock)
|
||||
}
|
||||
206
v2/readerioresult/traverse.go
Normal file
206
v2/readerioresult/traverse.go
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||
)
|
||||
|
||||
// TraverseArray transforms each element of an array using a function that returns a ReaderIOResult,
|
||||
// then collects the results into a single ReaderIOResult containing an array.
|
||||
//
|
||||
// If any transformation fails, the entire operation fails with the first error encountered.
|
||||
// All transformations are executed sequentially.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - E: The error type
|
||||
// - A: The input element type
|
||||
// - B: The output element type
|
||||
//
|
||||
// Parameters:
|
||||
// - f: A function that transforms each element into a ReaderIOResult
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// A function that takes an array and returns a ReaderIOResult of an array
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// fetchUsers := TraverseArray(func(id int) ReaderIOResult[Config, error, User] {
|
||||
// return fetchUser(id)
|
||||
// })
|
||||
// result := fetchUsers([]int{1, 2, 3})
|
||||
// // result(cfg)() returns Right([user1, user2, user3]) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArray[R, A, B any](f Kleisli[R, A, B]) Kleisli[R, []A, []B] {
|
||||
return RIOE.TraverseArray(f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndex is like TraverseArray but the transformation function also receives the index.
|
||||
//
|
||||
// This is useful when the transformation depends on the element's position in the array.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - E: The error type
|
||||
// - A: The input element type
|
||||
// - B: The output element type
|
||||
//
|
||||
// Parameters:
|
||||
// - f: A function that transforms each element and its index into a ReaderIOResult
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// A function that takes an array and returns a ReaderIOResult of an array
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// processWithIndex := TraverseArrayWithIndex(func(i int, val string) ReaderIOResult[Config, error, string] {
|
||||
// return Of[Config, error](fmt.Sprintf("%d: %s", i, val))
|
||||
// })
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayWithIndex[R, A, B any](f func(int, A) ReaderIOResult[R, B]) Kleisli[R, []A, []B] {
|
||||
return RIOE.TraverseArrayWithIndex(f)
|
||||
}
|
||||
|
||||
// SequenceArray converts an array of ReaderIOResult into a ReaderIOResult of an array.
|
||||
//
|
||||
// This is useful when you have multiple independent computations and want to execute them all
|
||||
// and collect their results. If any computation fails, the entire operation fails with the first error.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - E: The error type
|
||||
// - A: The element type
|
||||
//
|
||||
// Parameters:
|
||||
// - ma: An array of ReaderIOResult computations
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// A ReaderIOResult that produces an array of results
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// computations := []ReaderIOResult[Config, error, int]{
|
||||
// fetchCount("users"),
|
||||
// fetchCount("posts"),
|
||||
// fetchCount("comments"),
|
||||
// }
|
||||
// result := SequenceArray(computations)
|
||||
// // result(cfg)() returns Right([userCount, postCount, commentCount]) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func SequenceArray[R, A any](ma []ReaderIOResult[R, A]) ReaderIOResult[R, []A] {
|
||||
return RIOE.SequenceArray(ma)
|
||||
}
|
||||
|
||||
// TraverseRecord transforms each value in a map using a function that returns a ReaderIOResult,
|
||||
// then collects the results into a single ReaderIOResult containing a map.
|
||||
//
|
||||
// If any transformation fails, the entire operation fails with the first error encountered.
|
||||
// The keys are preserved in the output map.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - K: The key type (must be comparable)
|
||||
// - E: The error type
|
||||
// - A: The input value type
|
||||
// - B: The output value type
|
||||
//
|
||||
// Parameters:
|
||||
// - f: A function that transforms each value into a ReaderIOResult
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// A function that takes a map and returns a ReaderIOResult of a map
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// enrichUsers := TraverseRecord(func(user User) ReaderIOResult[Config, error, EnrichedUser] {
|
||||
// return enrichUser(user)
|
||||
// })
|
||||
// result := enrichUsers(map[string]User{"alice": user1, "bob": user2})
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecord[K comparable, R, A, B any](f Kleisli[R, A, B]) Kleisli[R, map[K]A, map[K]B] {
|
||||
return RIOE.TraverseRecord[K](f)
|
||||
}
|
||||
|
||||
// TraverseRecordWithIndex is like TraverseRecord but the transformation function also receives the key.
|
||||
//
|
||||
// This is useful when the transformation depends on the key associated with each value.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - K: The key type (must be comparable)
|
||||
// - E: The error type
|
||||
// - A: The input value type
|
||||
// - B: The output value type
|
||||
//
|
||||
// Parameters:
|
||||
// - f: A function that transforms each key-value pair into a ReaderIOResult
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// A function that takes a map and returns a ReaderIOResult of a map
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// processWithKey := TraverseRecordWithIndex(func(key string, val int) ReaderIOResult[Config, error, string] {
|
||||
// return Of[Config, error](fmt.Sprintf("%s: %d", key, val))
|
||||
// })
|
||||
//
|
||||
//go:inline
|
||||
func TraverseRecordWithIndex[K comparable, R, A, B any](f func(K, A) ReaderIOResult[R, B]) Kleisli[R, map[K]A, map[K]B] {
|
||||
return RIOE.TraverseRecordWithIndex[K](f)
|
||||
}
|
||||
|
||||
// SequenceRecord converts a map of ReaderIOResult into a ReaderIOResult of a map.
|
||||
//
|
||||
// This is useful when you have multiple independent computations keyed by some identifier
|
||||
// and want to execute them all and collect their results. If any computation fails,
|
||||
// the entire operation fails with the first error.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - K: The key type (must be comparable)
|
||||
// - E: The error type
|
||||
// - A: The value type
|
||||
//
|
||||
// Parameters:
|
||||
// - ma: A map of ReaderIOResult computations
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// A ReaderIOResult that produces a map of results
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// computations := map[string]ReaderIOResult[Config, error, int]{
|
||||
// "users": fetchCount("users"),
|
||||
// "posts": fetchCount("posts"),
|
||||
// }
|
||||
// result := SequenceRecord(computations)
|
||||
// // result(cfg)() returns Right(map[string]int{"users": 100, "posts": 50}) or Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func SequenceRecord[K comparable, R, A any](ma map[K]ReaderIOResult[R, A]) ReaderIOResult[R, map[K]A] {
|
||||
return RIOE.SequenceRecord(ma)
|
||||
}
|
||||
100
v2/readerioresult/types.go
Normal file
100
v2/readerioresult/types.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package readerioresult
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/endomorphism"
|
||||
"github.com/IBM/fp-go/v2/io"
|
||||
"github.com/IBM/fp-go/v2/ioeither"
|
||||
"github.com/IBM/fp-go/v2/ioresult"
|
||||
"github.com/IBM/fp-go/v2/lazy"
|
||||
"github.com/IBM/fp-go/v2/option"
|
||||
"github.com/IBM/fp-go/v2/reader"
|
||||
"github.com/IBM/fp-go/v2/readerio"
|
||||
"github.com/IBM/fp-go/v2/result"
|
||||
)
|
||||
|
||||
type (
|
||||
// Either represents a value of one of two possible types (a disjoint union).
|
||||
// An instance of Either is either Left (representing an error) or Right (representing a success).
|
||||
Either[E, A any] = Result[A]
|
||||
|
||||
Result[A any] = result.Result[A]
|
||||
|
||||
// Reader represents a computation that depends on some context/environment of type R
|
||||
// and produces a value of type A. It's useful for dependency injection patterns.
|
||||
Reader[R, A any] = reader.Reader[R, A]
|
||||
|
||||
// ReaderIO represents a computation that depends on some context R and performs
|
||||
// side effects to produce a value of type A.
|
||||
ReaderIO[R, A any] = readerio.ReaderIO[R, A]
|
||||
|
||||
// IOEither represents a computation that performs side effects and can either
|
||||
// fail with an error of type E or succeed with a value of type A.
|
||||
IOEither[E, A any] = ioeither.IOEither[E, A]
|
||||
|
||||
IOResult[A any] = ioresult.IOResult[A]
|
||||
|
||||
IO[A any] = io.IO[A]
|
||||
|
||||
Lazy[A any] = lazy.Lazy[A]
|
||||
|
||||
Option[A any] = option.Option[A]
|
||||
|
||||
Endomorphism[A any] = endomorphism.Endomorphism[A]
|
||||
|
||||
// ReaderIOResult represents a computation that:
|
||||
// - Depends on some context/environment of type R (Reader)
|
||||
// - Performs side effects (IO)
|
||||
// - Can fail with an error of type E or succeed with a value of type A (Either)
|
||||
//
|
||||
// It combines three powerful functional programming concepts:
|
||||
// 1. Reader monad for dependency injection
|
||||
// 2. IO monad for side effects
|
||||
// 3. Either monad for error handling
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The type of the context/environment (e.g., configuration, dependencies)
|
||||
// - E: The type of errors that can occur
|
||||
// - A: The type of the success value
|
||||
//
|
||||
// Example:
|
||||
// type Config struct { BaseURL string }
|
||||
// func fetchUser(id int) ReaderIOResult[Config, error, User] {
|
||||
// return func(cfg Config) IOEither[error, User] {
|
||||
// return func() Either[error, User] {
|
||||
// // Use cfg.BaseURL to fetch user
|
||||
// // Return either.Right(user) or either.Left(err)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
ReaderIOResult[R, A any] = Reader[R, IOResult[A]]
|
||||
|
||||
Kleisli[R, A, B any] = reader.Reader[A, ReaderIOResult[R, B]]
|
||||
|
||||
// Operator represents a transformation from one ReaderIOResult to another.
|
||||
// It's a Reader that takes a ReaderIOResult[R, A] and produces a ReaderIOResult[R, B].
|
||||
// This type is commonly used for composing operations in a point-free style.
|
||||
//
|
||||
// Type parameters:
|
||||
// - R: The context type
|
||||
// - A: The input value type
|
||||
// - B: The output value type
|
||||
//
|
||||
// Example:
|
||||
// var doubleOp Operator[Config, error, int, int] = Map(func(x int) int { return x * 2 })
|
||||
Operator[R, A, B any] = Kleisli[R, ReaderIOResult[R, A], B]
|
||||
)
|
||||
51
v2/result/apply.go
Normal file
51
v2/result/apply.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
M "github.com/IBM/fp-go/v2/monoid"
|
||||
S "github.com/IBM/fp-go/v2/semigroup"
|
||||
)
|
||||
|
||||
// ApplySemigroup lifts a Semigroup over the Right values of Either.
|
||||
// Combines two Right values using the provided Semigroup.
|
||||
// If either value is Left, returns the first Left encountered.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// intAdd := semigroup.MakeSemigroup(func(a, b int) int { return a + b })
|
||||
// eitherSemi := either.ApplySemigroup[error](intAdd)
|
||||
// result := eitherSemi.Concat(either.Right[error](2), either.Right[error](3)) // Right(5)
|
||||
//
|
||||
//go:inline
|
||||
func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Result[A]] {
|
||||
return either.ApplySemigroup[error](s)
|
||||
}
|
||||
|
||||
// ApplicativeMonoid returns a Monoid that concatenates Either instances via their applicative.
|
||||
// Provides an empty Either (Right with monoid's empty value) and combines Right values using the monoid.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// intAddMonoid := monoid.MakeMonoid(0, func(a, b int) int { return a + b })
|
||||
// eitherMon := either.ApplicativeMonoid[error](intAddMonoid)
|
||||
// empty := eitherMon.Empty() // Right(0)
|
||||
//
|
||||
//go:inline
|
||||
func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Result[A]] {
|
||||
return either.ApplicativeMonoid[error](m)
|
||||
}
|
||||
64
v2/result/apply_test.go
Normal file
64
v2/result/apply_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
M "github.com/IBM/fp-go/v2/monoid/testing"
|
||||
N "github.com/IBM/fp-go/v2/number"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestApplySemigroup(t *testing.T) {
|
||||
|
||||
sg := ApplySemigroup(N.SemigroupSum[int]())
|
||||
|
||||
la := Left[int](errors.New("a"))
|
||||
lb := Left[int](errors.New("b"))
|
||||
r1 := Right(1)
|
||||
r2 := Right(2)
|
||||
r3 := Right(3)
|
||||
|
||||
assert.Equal(t, la, sg.Concat(la, lb))
|
||||
assert.Equal(t, lb, sg.Concat(r1, lb))
|
||||
assert.Equal(t, la, sg.Concat(la, r2))
|
||||
assert.Equal(t, lb, sg.Concat(r1, lb))
|
||||
assert.Equal(t, r3, sg.Concat(r1, r2))
|
||||
}
|
||||
|
||||
func TestApplicativeMonoid(t *testing.T) {
|
||||
|
||||
m := ApplicativeMonoid(N.MonoidSum[int]())
|
||||
|
||||
la := Left[int](errors.New("a"))
|
||||
lb := Left[int](errors.New("b"))
|
||||
r1 := Right(1)
|
||||
r2 := Right(2)
|
||||
r3 := Right(3)
|
||||
|
||||
assert.Equal(t, la, m.Concat(la, m.Empty()))
|
||||
assert.Equal(t, lb, m.Concat(m.Empty(), lb))
|
||||
assert.Equal(t, r1, m.Concat(r1, m.Empty()))
|
||||
assert.Equal(t, r2, m.Concat(m.Empty(), r2))
|
||||
assert.Equal(t, r3, m.Concat(r1, r2))
|
||||
}
|
||||
|
||||
func TestApplicativeMonoidLaws(t *testing.T) {
|
||||
m := ApplicativeMonoid(N.MonoidSum[int]())
|
||||
M.AssertLaws(t, m)([]Result[int]{Left[int](errors.New("a")), Right(1)})
|
||||
}
|
||||
157
v2/result/array.go
Normal file
157
v2/result/array.go
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
)
|
||||
|
||||
// TraverseArrayG transforms an array by applying a function that returns an Either to each element.
|
||||
// If any element produces a Left, the entire result is that Left (short-circuits).
|
||||
// Otherwise, returns Right containing the array of all Right values.
|
||||
// The G suffix indicates support for generic slice types.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// parse := func(s string) either.Result[int] {
|
||||
// v, err := strconv.Atoi(s)
|
||||
// return either.FromError(v, err)
|
||||
// }
|
||||
// result := either.TraverseArrayG[[]string, []int](parse)([]string{"1", "2", "3"})
|
||||
// // result is Right([]int{1, 2, 3})
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayG[GA ~[]A, GB ~[]B, A, B any](f Kleisli[A, B]) Kleisli[GA, GB] {
|
||||
return either.TraverseArrayG[GA, GB](f)
|
||||
}
|
||||
|
||||
// TraverseArray transforms an array by applying a function that returns an Either to each element.
|
||||
// If any element produces a Left, the entire result is that Left (short-circuits).
|
||||
// Otherwise, returns Right containing the array of all Right values.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// parse := func(s string) either.Result[int] {
|
||||
// v, err := strconv.Atoi(s)
|
||||
// return either.FromError(v, err)
|
||||
// }
|
||||
// result := either.TraverseArray(parse)([]string{"1", "2", "3"})
|
||||
// // result is Right([]int{1, 2, 3})
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||
return either.TraverseArray(f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndexG transforms an array by applying an indexed function that returns an Either.
|
||||
// The function receives both the index and the element.
|
||||
// If any element produces a Left, the entire result is that Left (short-circuits).
|
||||
// The G suffix indicates support for generic slice types.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// validate := func(i int, s string) either.Result[string] {
|
||||
// if len(s) > 0 {
|
||||
// return either.Right[error](fmt.Sprintf("%d:%s", i, s))
|
||||
// }
|
||||
// return either.Left[string](fmt.Errorf("empty at index %d", i))
|
||||
// }
|
||||
// result := either.TraverseArrayWithIndexG[[]string, []string](validate)([]string{"a", "b"})
|
||||
// // result is Right([]string{"0:a", "1:b"})
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, A, B any](f func(int, A) Result[B]) Kleisli[GA, GB] {
|
||||
return either.TraverseArrayWithIndexG[GA, GB](f)
|
||||
}
|
||||
|
||||
// TraverseArrayWithIndex transforms an array by applying an indexed function that returns an Either.
|
||||
// The function receives both the index and the element.
|
||||
// If any element produces a Left, the entire result is that Left (short-circuits).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// validate := func(i int, s string) either.Result[string] {
|
||||
// if len(s) > 0 {
|
||||
// return either.Right[error](fmt.Sprintf("%d:%s", i, s))
|
||||
// }
|
||||
// return either.Left[string](fmt.Errorf("empty at index %d", i))
|
||||
// }
|
||||
// result := either.TraverseArrayWithIndex(validate)([]string{"a", "b"})
|
||||
// // result is Right([]string{"0:a", "1:b"})
|
||||
//
|
||||
//go:inline
|
||||
func TraverseArrayWithIndex[A, B any](f func(int, A) Result[B]) Kleisli[[]A, []B] {
|
||||
return either.TraverseArrayWithIndex(f)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func SequenceArrayG[GA ~[]A, GOA ~[]Result[A], A any](ma GOA) Result[GA] {
|
||||
return either.SequenceArrayG[GA](ma)
|
||||
}
|
||||
|
||||
// SequenceArray converts a homogeneous sequence of Either into an Either of sequence.
|
||||
// If any element is Left, returns that Left (short-circuits).
|
||||
// Otherwise, returns Right containing all the Right values.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// eithers := []either.Result[int]{
|
||||
// either.Right[error](1),
|
||||
// either.Right[error](2),
|
||||
// either.Right[error](3),
|
||||
// }
|
||||
// result := either.SequenceArray(eithers)
|
||||
// // result is Right([]int{1, 2, 3})
|
||||
//
|
||||
//go:inline
|
||||
func SequenceArray[A any](ma []Result[A]) Result[[]A] {
|
||||
return either.SequenceArray(ma)
|
||||
}
|
||||
|
||||
// CompactArrayG discards all Left values and keeps only the Right values.
|
||||
// The G suffix indicates support for generic slice types.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// eithers := []either.Result[int]{
|
||||
// either.Right[error](1),
|
||||
// either.Left[int](errors.New("error")),
|
||||
// either.Right[error](3),
|
||||
// }
|
||||
// result := either.CompactArrayG[[]either.Result[int], []int](eithers)
|
||||
// // result is []int{1, 3}
|
||||
//
|
||||
//go:inline
|
||||
func CompactArrayG[A1 ~[]Result[A], A2 ~[]A, A any](fa A1) A2 {
|
||||
return either.CompactArrayG[A1, A2](fa)
|
||||
}
|
||||
|
||||
// CompactArray discards all Left values and keeps only the Right values.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// eithers := []either.Result[int]{
|
||||
// either.Right[error](1),
|
||||
// either.Left[int](errors.New("error")),
|
||||
// either.Right[error](3),
|
||||
// }
|
||||
// result := either.CompactArray(eithers)
|
||||
// // result is []int{1, 3}
|
||||
//
|
||||
//go:inline
|
||||
func CompactArray[A any](fa []Result[A]) []A {
|
||||
return either.CompactArray(fa)
|
||||
}
|
||||
51
v2/result/array_test.go
Normal file
51
v2/result/array_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package result
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
TST "github.com/IBM/fp-go/v2/internal/testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompactArray(t *testing.T) {
|
||||
ar := []Result[string]{
|
||||
Of("ok"),
|
||||
Left[string](errors.New("err")),
|
||||
Of("ok"),
|
||||
}
|
||||
|
||||
res := CompactArray(ar)
|
||||
assert.Equal(t, 2, len(res))
|
||||
}
|
||||
|
||||
func TestSequenceArray(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayTest(
|
||||
FromStrictEquals[bool](),
|
||||
Pointed[string](),
|
||||
Pointed[bool](),
|
||||
Functor[[]string, bool](),
|
||||
SequenceArray[string],
|
||||
)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSequenceArrayError(t *testing.T) {
|
||||
|
||||
s := TST.SequenceArrayErrorTest(
|
||||
FromStrictEquals[bool](),
|
||||
Left[string],
|
||||
Left[bool],
|
||||
Pointed[string](),
|
||||
Pointed[bool](),
|
||||
Functor[[]string, bool](),
|
||||
SequenceArray[string],
|
||||
)
|
||||
// run across four bits
|
||||
s(4)(t)
|
||||
}
|
||||
351
v2/result/bind.go
Normal file
351
v2/result/bind.go
Normal file
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
)
|
||||
|
||||
// Do creates an empty context of type S to be used with the Bind operation.
|
||||
// This is the starting point for do-notation style computations.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct { x, y int }
|
||||
// result := either.Do[error](State{})
|
||||
//
|
||||
//go:inline
|
||||
func Do[S any](
|
||||
empty S,
|
||||
) Result[S] {
|
||||
return either.Do[error](empty)
|
||||
}
|
||||
|
||||
// Bind attaches the result of a computation to a context S1 to produce a context S2.
|
||||
// This enables building up complex computations in a pipeline.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct { value int }
|
||||
// result := F.Pipe2(
|
||||
// either.Do[error](State{}),
|
||||
// either.Bind(
|
||||
// func(v int) func(State) State {
|
||||
// return func(s State) State { return State{value: v} }
|
||||
// },
|
||||
// func(s State) either.Result[int] {
|
||||
// return either.Right[error](42)
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
//go:inline
|
||||
func Bind[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
f Kleisli[S1, T],
|
||||
) Operator[S1, S2] {
|
||||
return either.Bind(setter, f)
|
||||
}
|
||||
|
||||
// Let attaches the result of a pure computation to a context S1 to produce a context S2.
|
||||
// Similar to Bind but for pure (non-Either) computations.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct { value int }
|
||||
// result := F.Pipe2(
|
||||
// either.Right[error](State{value: 10}),
|
||||
// either.Let(
|
||||
// func(v int) func(State) State {
|
||||
// return func(s State) State { return State{value: s.value + v} }
|
||||
// },
|
||||
// func(s State) int { return 32 },
|
||||
// ),
|
||||
// ) // Right(State{value: 42})
|
||||
//
|
||||
//go:inline
|
||||
func Let[S1, S2, T any](
|
||||
key func(T) func(S1) S2,
|
||||
f func(S1) T,
|
||||
) Operator[S1, S2] {
|
||||
return either.Let[error](key, f)
|
||||
}
|
||||
|
||||
// LetTo attaches a constant value to a context S1 to produce a context S2.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct { name string }
|
||||
// result := F.Pipe2(
|
||||
// either.Right[error](State{}),
|
||||
// either.LetTo(
|
||||
// func(n string) func(State) State {
|
||||
// return func(s State) State { return State{name: n} }
|
||||
// },
|
||||
// "Alice",
|
||||
// ),
|
||||
// ) // Right(State{name: "Alice"})
|
||||
//
|
||||
//go:inline
|
||||
func LetTo[S1, S2, T any](
|
||||
key func(T) func(S1) S2,
|
||||
b T,
|
||||
) Operator[S1, S2] {
|
||||
return either.LetTo[error](key, b)
|
||||
}
|
||||
|
||||
// BindTo initializes a new state S1 from a value T.
|
||||
// This is typically used to start a bind chain.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct { value int }
|
||||
// result := F.Pipe2(
|
||||
// either.Right[error](42),
|
||||
// either.BindTo(func(v int) State { return State{value: v} }),
|
||||
// ) // Right(State{value: 42})
|
||||
//
|
||||
//go:inline
|
||||
func BindTo[S1, T any](
|
||||
setter func(T) S1,
|
||||
) Operator[T, S1] {
|
||||
return either.BindTo[error](setter)
|
||||
}
|
||||
|
||||
// ApS attaches a value to a context S1 to produce a context S2 by considering the context and the value concurrently.
|
||||
// Uses applicative semantics rather than monadic sequencing.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type State struct { x, y int }
|
||||
// result := F.Pipe2(
|
||||
// either.Right[error](State{x: 10}),
|
||||
// either.ApS(
|
||||
// func(y int) func(State) State {
|
||||
// return func(s State) State { return State{x: s.x, y: y} }
|
||||
// },
|
||||
// either.Right[error](32),
|
||||
// ),
|
||||
// ) // Right(State{x: 10, y: 32})
|
||||
//
|
||||
//go:inline
|
||||
func ApS[S1, S2, T any](
|
||||
setter func(T) func(S1) S2,
|
||||
fa Result[T],
|
||||
) Operator[S1, S2] {
|
||||
return either.ApS(setter, fa)
|
||||
}
|
||||
|
||||
// ApSL attaches a value to a context using a lens-based setter.
|
||||
// This is a convenience function that combines ApS with a lens, allowing you to use
|
||||
// optics to update nested structures in a more composable way.
|
||||
//
|
||||
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||
// This eliminates the need to manually write setter functions and enables working with
|
||||
// nested fields in a type-safe manner.
|
||||
//
|
||||
// Unlike BindL, ApSL uses applicative semantics, meaning the computation fa is independent
|
||||
// of the current state and can be evaluated concurrently.
|
||||
//
|
||||
// Type Parameters:
|
||||
// - E: Error type for the Either
|
||||
// - S: Structure type containing the field to update
|
||||
// - T: Type of the field being updated
|
||||
//
|
||||
// Parameters:
|
||||
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||
// - fa: An Result[T] computation that produces the value to set
|
||||
//
|
||||
// Returns:
|
||||
// - An endomorphism that updates the focused field in the Either context
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Person struct {
|
||||
// Name string
|
||||
// Age int
|
||||
// }
|
||||
//
|
||||
// ageLens := lens.MakeLens(
|
||||
// func(p Person) int { return p.Age },
|
||||
// func(p Person, a int) Person { p.Age = a; return p },
|
||||
// )
|
||||
//
|
||||
// result := F.Pipe2(
|
||||
// either.Right[error](Person{Name: "Alice", Age: 25}),
|
||||
// either.ApSL(ageLens, either.Right[error](30)),
|
||||
// ) // Right(Person{Name: "Alice", Age: 30})
|
||||
//
|
||||
//go:inline
|
||||
func ApSL[S, T any](
|
||||
lens Lens[S, T],
|
||||
fa Result[T],
|
||||
) Operator[S, S] {
|
||||
return either.ApSL(lens, fa)
|
||||
}
|
||||
|
||||
// BindL attaches the result of a computation to a context using a lens-based setter.
|
||||
// This is a convenience function that combines Bind with a lens, allowing you to use
|
||||
// optics to update nested structures based on their current values.
|
||||
//
|
||||
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||
// The computation function f receives the current value of the focused field and returns
|
||||
// an Either that produces the new value.
|
||||
//
|
||||
// Unlike ApSL, BindL uses monadic sequencing, meaning the computation f can depend on
|
||||
// the current value of the focused field.
|
||||
//
|
||||
// Type Parameters:
|
||||
// - E: Error type for the Either
|
||||
// - S: Structure type containing the field to update
|
||||
// - T: Type of the field being updated
|
||||
//
|
||||
// Parameters:
|
||||
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||
// - f: A function that takes the current field value and returns an Result[T]
|
||||
//
|
||||
// Returns:
|
||||
// - An endomorphism that updates the focused field based on its current value
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Counter struct {
|
||||
// Value int
|
||||
// }
|
||||
//
|
||||
// valueLens := lens.MakeLens(
|
||||
// func(c Counter) int { return c.Value },
|
||||
// func(c Counter, v int) Counter { c.Value = v; return c },
|
||||
// )
|
||||
//
|
||||
// // Increment the counter, but fail if it would exceed 100
|
||||
// increment := func(v int) either.Result[int] {
|
||||
// if v >= 100 {
|
||||
// return either.Left[int](errors.New("counter overflow"))
|
||||
// }
|
||||
// return either.Right[error](v + 1)
|
||||
// }
|
||||
//
|
||||
// result := F.Pipe1(
|
||||
// either.Right[error](Counter{Value: 42}),
|
||||
// either.BindL(valueLens, increment),
|
||||
// ) // Right(Counter{Value: 43})
|
||||
//
|
||||
//go:inline
|
||||
func BindL[S, T any](
|
||||
lens Lens[S, T],
|
||||
f Kleisli[T, T],
|
||||
) Operator[S, S] {
|
||||
return either.BindL(lens, f)
|
||||
}
|
||||
|
||||
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||
// This is a convenience function that combines Let with a lens, allowing you to use
|
||||
// optics to update nested structures with pure transformations.
|
||||
//
|
||||
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||
// The transformation function f receives the current value of the focused field and returns
|
||||
// the new value directly (not wrapped in Either).
|
||||
//
|
||||
// This is useful for pure transformations that cannot fail, such as mathematical operations,
|
||||
// string manipulations, or other deterministic updates.
|
||||
//
|
||||
// Type Parameters:
|
||||
// - E: Error type for the Either
|
||||
// - S: Structure type containing the field to update
|
||||
// - T: Type of the field being updated
|
||||
//
|
||||
// Parameters:
|
||||
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||
// - f: An endomorphism (T → T) that transforms the current field value
|
||||
//
|
||||
// Returns:
|
||||
// - An endomorphism that updates the focused field with the transformed value
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Counter struct {
|
||||
// Value int
|
||||
// }
|
||||
//
|
||||
// valueLens := lens.MakeLens(
|
||||
// func(c Counter) int { return c.Value },
|
||||
// func(c Counter, v int) Counter { c.Value = v; return c },
|
||||
// )
|
||||
//
|
||||
// // Double the counter value
|
||||
// double := func(v int) int { return v * 2 }
|
||||
//
|
||||
// result := F.Pipe1(
|
||||
// either.Right[error](Counter{Value: 21}),
|
||||
// either.LetL(valueLens, double),
|
||||
// ) // Right(Counter{Value: 42})
|
||||
//
|
||||
//go:inline
|
||||
func LetL[S, T any](
|
||||
lens Lens[S, T],
|
||||
f Endomorphism[T],
|
||||
) Operator[S, S] {
|
||||
return either.LetL[error](lens, f)
|
||||
}
|
||||
|
||||
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||
// This is a convenience function that combines LetTo with a lens, allowing you to use
|
||||
// optics to set nested fields to specific values.
|
||||
//
|
||||
// The lens parameter provides the setter for a field within the structure S.
|
||||
// Unlike LetL which transforms the current value, LetToL simply replaces it with
|
||||
// the provided constant value b.
|
||||
//
|
||||
// This is useful for resetting fields, initializing values, or setting fields to
|
||||
// predetermined constants.
|
||||
//
|
||||
// Type Parameters:
|
||||
// - E: Error type for the Either
|
||||
// - S: Structure type containing the field to update
|
||||
// - T: Type of the field being updated
|
||||
//
|
||||
// Parameters:
|
||||
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||
// - b: The constant value to set the field to
|
||||
//
|
||||
// Returns:
|
||||
// - An endomorphism that sets the focused field to the constant value
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type Config struct {
|
||||
// Debug bool
|
||||
// Timeout int
|
||||
// }
|
||||
//
|
||||
// debugLens := lens.MakeLens(
|
||||
// func(c Config) bool { return c.Debug },
|
||||
// func(c Config, d bool) Config { c.Debug = d; return c },
|
||||
// )
|
||||
//
|
||||
// result := F.Pipe1(
|
||||
// either.Right[error](Config{Debug: true, Timeout: 30}),
|
||||
// either.LetToL(debugLens, false),
|
||||
// ) // Right(Config{Debug: false, Timeout: 30})
|
||||
//
|
||||
//go:inline
|
||||
func LetToL[S, T any](
|
||||
lens Lens[S, T],
|
||||
b T,
|
||||
) Operator[S, S] {
|
||||
return either.LetToL[error](lens, b)
|
||||
}
|
||||
361
v2/result/bind_test.go
Normal file
361
v2/result/bind_test.go
Normal file
@@ -0,0 +1,361 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/internal/utils"
|
||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getLastName(s utils.Initial) Result[string] {
|
||||
return Of("Doe")
|
||||
}
|
||||
|
||||
func getGivenName(s utils.WithLastName) Result[string] {
|
||||
return Of("John")
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
Bind(utils.SetLastName, getLastName),
|
||||
Bind(utils.SetGivenName, getGivenName),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res, Of("John Doe"))
|
||||
}
|
||||
|
||||
func TestApS(t *testing.T) {
|
||||
|
||||
res := F.Pipe3(
|
||||
Do(utils.Empty),
|
||||
ApS(utils.SetLastName, Of("Doe")),
|
||||
ApS(utils.SetGivenName, Of("John")),
|
||||
Map(utils.GetFullName),
|
||||
)
|
||||
|
||||
assert.Equal(t, res, Of("John Doe"))
|
||||
}
|
||||
|
||||
// Test types for lens-based operations
|
||||
type Counter struct {
|
||||
Value int
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Debug bool
|
||||
Timeout int
|
||||
}
|
||||
|
||||
func TestApSL(t *testing.T) {
|
||||
// Create a lens for the Age field
|
||||
ageLens := L.MakeLens(
|
||||
func(p Person) int { return p.Age },
|
||||
func(p Person, a int) Person { p.Age = a; return p },
|
||||
)
|
||||
|
||||
t.Run("ApSL with Right value", func(t *testing.T) {
|
||||
result := F.Pipe1(
|
||||
Right(Person{Name: "Alice", Age: 25}),
|
||||
ApSL(ageLens, Right(30)),
|
||||
)
|
||||
|
||||
expected := Right(Person{Name: "Alice", Age: 30})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("ApSL with Left in context", func(t *testing.T) {
|
||||
result := F.Pipe1(
|
||||
Left[Person](assert.AnError),
|
||||
ApSL(ageLens, Right(30)),
|
||||
)
|
||||
|
||||
expected := Left[Person](assert.AnError)
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("ApSL with Left in value", func(t *testing.T) {
|
||||
result := F.Pipe1(
|
||||
Right(Person{Name: "Alice", Age: 25}),
|
||||
ApSL(ageLens, Left[int](assert.AnError)),
|
||||
)
|
||||
|
||||
expected := Left[Person](assert.AnError)
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("ApSL with both Left", func(t *testing.T) {
|
||||
result := F.Pipe1(
|
||||
Left[Person](assert.AnError),
|
||||
ApSL(ageLens, Left[int](assert.AnError)),
|
||||
)
|
||||
|
||||
expected := Left[Person](assert.AnError)
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBindL(t *testing.T) {
|
||||
// Create a lens for the Value field
|
||||
valueLens := L.MakeLens(
|
||||
func(c Counter) int { return c.Value },
|
||||
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||
)
|
||||
|
||||
t.Run("BindL with successful transformation", func(t *testing.T) {
|
||||
// Increment the counter, but fail if it would exceed 100
|
||||
increment := func(v int) Result[int] {
|
||||
if v >= 100 {
|
||||
return Left[int](assert.AnError)
|
||||
}
|
||||
return Right(v + 1)
|
||||
}
|
||||
|
||||
result := F.Pipe1(
|
||||
Right(Counter{Value: 42}),
|
||||
BindL(valueLens, increment),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 43})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("BindL with failing transformation", func(t *testing.T) {
|
||||
increment := func(v int) Result[int] {
|
||||
if v >= 100 {
|
||||
return Left[int](assert.AnError)
|
||||
}
|
||||
return Right(v + 1)
|
||||
}
|
||||
|
||||
result := F.Pipe1(
|
||||
Right(Counter{Value: 100}),
|
||||
BindL(valueLens, increment),
|
||||
)
|
||||
|
||||
expected := Left[Counter](assert.AnError)
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("BindL with Left input", func(t *testing.T) {
|
||||
increment := func(v int) Result[int] {
|
||||
return Right(v + 1)
|
||||
}
|
||||
|
||||
result := F.Pipe1(
|
||||
Left[Counter](assert.AnError),
|
||||
BindL(valueLens, increment),
|
||||
)
|
||||
|
||||
expected := Left[Counter](assert.AnError)
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("BindL with multiple operations", func(t *testing.T) {
|
||||
double := func(v int) Result[int] {
|
||||
return Right(v * 2)
|
||||
}
|
||||
|
||||
addTen := func(v int) Result[int] {
|
||||
return Right(v + 10)
|
||||
}
|
||||
|
||||
result := F.Pipe2(
|
||||
Right(Counter{Value: 5}),
|
||||
BindL(valueLens, double),
|
||||
BindL(valueLens, addTen),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 20})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLetL(t *testing.T) {
|
||||
// Create a lens for the Value field
|
||||
valueLens := L.MakeLens(
|
||||
func(c Counter) int { return c.Value },
|
||||
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||
)
|
||||
|
||||
t.Run("LetL with pure transformation", func(t *testing.T) {
|
||||
double := func(v int) int { return v * 2 }
|
||||
|
||||
result := F.Pipe1(
|
||||
Right(Counter{Value: 21}),
|
||||
LetL(valueLens, double),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 42})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("LetL with Left input", func(t *testing.T) {
|
||||
double := func(v int) int { return v * 2 }
|
||||
|
||||
result := F.Pipe1(
|
||||
Left[Counter](assert.AnError),
|
||||
LetL(valueLens, double),
|
||||
)
|
||||
|
||||
expected := Left[Counter](assert.AnError)
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("LetL with multiple transformations", func(t *testing.T) {
|
||||
double := func(v int) int { return v * 2 }
|
||||
addTen := func(v int) int { return v + 10 }
|
||||
|
||||
result := F.Pipe2(
|
||||
Right(Counter{Value: 5}),
|
||||
LetL(valueLens, double),
|
||||
LetL(valueLens, addTen),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 20})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("LetL with identity transformation", func(t *testing.T) {
|
||||
identity := func(v int) int { return v }
|
||||
|
||||
result := F.Pipe1(
|
||||
Right(Counter{Value: 42}),
|
||||
LetL(valueLens, identity),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 42})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLetToL(t *testing.T) {
|
||||
// Create a lens for the Debug field
|
||||
debugLens := L.MakeLens(
|
||||
func(c Config) bool { return c.Debug },
|
||||
func(c Config, d bool) Config { c.Debug = d; return c },
|
||||
)
|
||||
|
||||
t.Run("LetToL with constant value", func(t *testing.T) {
|
||||
result := F.Pipe1(
|
||||
Right(Config{Debug: true, Timeout: 30}),
|
||||
LetToL(debugLens, false),
|
||||
)
|
||||
|
||||
expected := Right(Config{Debug: false, Timeout: 30})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("LetToL with Left input", func(t *testing.T) {
|
||||
result := F.Pipe1(
|
||||
Left[Config](assert.AnError),
|
||||
LetToL(debugLens, false),
|
||||
)
|
||||
|
||||
expected := Left[Config](assert.AnError)
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("LetToL with multiple fields", func(t *testing.T) {
|
||||
timeoutLens := L.MakeLens(
|
||||
func(c Config) int { return c.Timeout },
|
||||
func(c Config, t int) Config { c.Timeout = t; return c },
|
||||
)
|
||||
|
||||
result := F.Pipe2(
|
||||
Right(Config{Debug: true, Timeout: 30}),
|
||||
LetToL(debugLens, false),
|
||||
LetToL(timeoutLens, 60),
|
||||
)
|
||||
|
||||
expected := Right(Config{Debug: false, Timeout: 60})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("LetToL setting same value", func(t *testing.T) {
|
||||
result := F.Pipe1(
|
||||
Right(Config{Debug: false, Timeout: 30}),
|
||||
LetToL(debugLens, false),
|
||||
)
|
||||
|
||||
expected := Right(Config{Debug: false, Timeout: 30})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLensOperationsCombined(t *testing.T) {
|
||||
// Test combining different lens operations
|
||||
valueLens := L.MakeLens(
|
||||
func(c Counter) int { return c.Value },
|
||||
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||
)
|
||||
|
||||
t.Run("Combine LetToL and LetL", func(t *testing.T) {
|
||||
double := func(v int) int { return v * 2 }
|
||||
|
||||
result := F.Pipe2(
|
||||
Right(Counter{Value: 100}),
|
||||
LetToL(valueLens, 10),
|
||||
LetL(valueLens, double),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 20})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("Combine LetL and BindL", func(t *testing.T) {
|
||||
double := func(v int) int { return v * 2 }
|
||||
validate := func(v int) Result[int] {
|
||||
if v > 100 {
|
||||
return Left[int](assert.AnError)
|
||||
}
|
||||
return Right(v)
|
||||
}
|
||||
|
||||
result := F.Pipe2(
|
||||
Right(Counter{Value: 25}),
|
||||
LetL(valueLens, double),
|
||||
BindL(valueLens, validate),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 50})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
|
||||
t.Run("Combine ApSL and LetL", func(t *testing.T) {
|
||||
addFive := func(v int) int { return v + 5 }
|
||||
|
||||
result := F.Pipe2(
|
||||
Right(Counter{Value: 10}),
|
||||
ApSL(valueLens, Right(20)),
|
||||
LetL(valueLens, addFive),
|
||||
)
|
||||
|
||||
expected := Right(Counter{Value: 25})
|
||||
assert.Equal(t, expected, result)
|
||||
})
|
||||
}
|
||||
101
v2/result/core.go
Normal file
101
v2/result/core.go
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import "github.com/IBM/fp-go/v2/either"
|
||||
|
||||
// IsLeft tests if the Either is a Left value.
|
||||
// Rather use [Fold] or [MonadFold] if you need to access the values.
|
||||
// Inverse is [IsRight].
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// either.IsLeft(either.Left[int](errors.New("err"))) // true
|
||||
// either.IsLeft(either.Right[error](42)) // false
|
||||
//
|
||||
//go:inline
|
||||
func IsLeft[A any](val Result[A]) bool {
|
||||
return either.IsLeft(val)
|
||||
}
|
||||
|
||||
// IsRight tests if the Either is a Right value.
|
||||
// Rather use [Fold] or [MonadFold] if you need to access the values.
|
||||
// Inverse is [IsLeft].
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// either.IsRight(either.Right[error](42)) // true
|
||||
// either.IsRight(either.Left[int](errors.New("err"))) // false
|
||||
//
|
||||
//go:inline
|
||||
func IsRight[A any](val Result[A]) bool {
|
||||
return either.IsRight(val)
|
||||
}
|
||||
|
||||
// Left creates a new Either representing a Left (error/failure) value.
|
||||
// By convention, Left represents the error case.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.Left[int](errors.New("something went wrong"))
|
||||
//
|
||||
//go:inline
|
||||
func Left[A any](value error) Result[A] {
|
||||
return either.Left[A](value)
|
||||
|
||||
}
|
||||
|
||||
// Right creates a new Either representing a Right (success) value.
|
||||
// By convention, Right represents the success case.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.Right[error](42)
|
||||
//
|
||||
//go:inline
|
||||
func Right[A any](value A) Result[A] {
|
||||
return either.Right[error](value)
|
||||
}
|
||||
|
||||
// MonadFold extracts the value from an Either by providing handlers for both cases.
|
||||
// This is the fundamental pattern matching operation for Either.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadFold(
|
||||
// either.Right[error](42),
|
||||
// func(err error) string { return "Error: " + err.Error() },
|
||||
// func(n int) string { return fmt.Sprintf("Value: %d", n) },
|
||||
// ) // "Value: 42"
|
||||
//
|
||||
//go:inline
|
||||
func MonadFold[A, B any](ma Result[A], onLeft func(e error) B, onRight func(a A) B) B {
|
||||
return either.MonadFold(ma, onLeft, onRight)
|
||||
}
|
||||
|
||||
// Unwrap converts an Either into the idiomatic Go tuple (value, error).
|
||||
// For Right values, returns (value, zero-error).
|
||||
// For Left values, returns (zero-value, error).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// val, err := either.Unwrap(either.Right[error](42)) // 42, nil
|
||||
// val, err := either.Unwrap(either.Left[int](errors.New("fail"))) // 0, error
|
||||
//
|
||||
//go:inline
|
||||
func Unwrap[A any](ma Result[A]) (A, error) {
|
||||
return either.Unwrap(ma)
|
||||
}
|
||||
103
v2/result/curry.go
Normal file
103
v2/result/curry.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
)
|
||||
|
||||
// Curry0 converts a Go function that returns (R, error) into a curried version that returns Result[R].
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// getConfig := func() (string, error) { return "config", nil }
|
||||
// curried := either.Curry0(getConfig)
|
||||
// result := curried() // Right("config")
|
||||
func Curry0[R any](f func() (R, error)) func() Result[R] {
|
||||
return either.Curry0(f)
|
||||
}
|
||||
|
||||
// Curry1 converts a Go function that returns (R, error) into a curried version that returns Result[R].
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// parse := func(s string) (int, error) { return strconv.Atoi(s) }
|
||||
// curried := either.Curry1(parse)
|
||||
// result := curried("42") // Right(42)
|
||||
func Curry1[T1, R any](f func(T1) (R, error)) func(T1) Result[R] {
|
||||
return either.Curry1(f)
|
||||
}
|
||||
|
||||
// Curry2 converts a 2-argument Go function that returns (R, error) into a curried version.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// divide := func(a, b int) (int, error) {
|
||||
// if b == 0 { return 0, errors.New("div by zero") }
|
||||
// return a / b, nil
|
||||
// }
|
||||
// curried := either.Curry2(divide)
|
||||
// result := curried(10)(2) // Right(5)
|
||||
func Curry2[T1, T2, R any](f func(T1, T2) (R, error)) func(T1) func(T2) Result[R] {
|
||||
return either.Curry2(f)
|
||||
}
|
||||
|
||||
// Curry3 converts a 3-argument Go function that returns (R, error) into a curried version.
|
||||
func Curry3[T1, T2, T3, R any](f func(T1, T2, T3) (R, error)) func(T1) func(T2) func(T3) Result[R] {
|
||||
return either.Curry3(f)
|
||||
}
|
||||
|
||||
// Curry4 converts a 4-argument Go function that returns (R, error) into a curried version.
|
||||
func Curry4[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) (R, error)) func(T1) func(T2) func(T3) func(T4) Result[R] {
|
||||
return either.Curry4(f)
|
||||
}
|
||||
|
||||
// Uncurry0 converts a function returning Result[R] back to Go's (R, error) style.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// curried := func() either.Result[string] { return either.Right[error]("value") }
|
||||
// uncurried := either.Uncurry0(curried)
|
||||
// result, err := uncurried() // "value", nil
|
||||
func Uncurry0[R any](f func() Result[R]) func() (R, error) {
|
||||
return either.Uncurry0(f)
|
||||
}
|
||||
|
||||
// Uncurry1 converts a function returning Result[R] back to Go's (R, error) style.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// curried := func(x int) either.Result[string] { return either.Right[error](strconv.Itoa(x)) }
|
||||
// uncurried := either.Uncurry1(curried)
|
||||
// result, err := uncurried(42) // "42", nil
|
||||
func Uncurry1[T1, R any](f func(T1) Result[R]) func(T1) (R, error) {
|
||||
return either.Uncurry1(f)
|
||||
}
|
||||
|
||||
// Uncurry2 converts a curried function returning Result[R] back to Go's (R, error) style.
|
||||
func Uncurry2[T1, T2, R any](f func(T1) func(T2) Result[R]) func(T1, T2) (R, error) {
|
||||
return either.Uncurry2(f)
|
||||
}
|
||||
|
||||
// Uncurry3 converts a curried function returning Result[R] back to Go's (R, error) style.
|
||||
func Uncurry3[T1, T2, T3, R any](f func(T1) func(T2) func(T3) Result[R]) func(T1, T2, T3) (R, error) {
|
||||
return either.Uncurry3(f)
|
||||
}
|
||||
|
||||
// Uncurry4 converts a curried function returning Result[R] back to Go's (R, error) style.
|
||||
func Uncurry4[T1, T2, T3, T4, R any](f func(T1) func(T2) func(T3) func(T4) Result[R]) func(T1, T2, T3, T4) (R, error) {
|
||||
return either.Uncurry4(f)
|
||||
}
|
||||
65
v2/result/doc.go
Normal file
65
v2/result/doc.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// package result provides the Either monad, a data structure representing a value of one of two possible types.
|
||||
//
|
||||
// Either is commonly used for error handling, where by convention:
|
||||
// - Left represents an error or failure case (type E)
|
||||
// - Right represents a success case (type A)
|
||||
//
|
||||
// # Core Concepts
|
||||
//
|
||||
// The Either type is a discriminated union that can hold either a Left value (typically an error)
|
||||
// or a Right value (typically a successful result). This makes it ideal for computations that may fail.
|
||||
//
|
||||
// # Basic Usage
|
||||
//
|
||||
// // Creating Either values
|
||||
// success := either.Right[error](42) // Right value
|
||||
// failure := either.Left[int](errors.New("oops")) // Left value
|
||||
//
|
||||
// // Pattern matching with Fold
|
||||
// result := either.Fold(
|
||||
// func(err error) string { return "Error: " + err.Error() },
|
||||
// func(n int) string { return fmt.Sprintf("Success: %d", n) },
|
||||
// )(success)
|
||||
//
|
||||
// // Chaining operations (short-circuits on Left)
|
||||
// result := either.Chain(func(n int) either.Result[int] {
|
||||
// return either.Right[error](n * 2)
|
||||
// })(success)
|
||||
//
|
||||
// # Monadic Operations
|
||||
//
|
||||
// Either implements the Monad interface, providing:
|
||||
// - Map: Transform the Right value
|
||||
// - Chain (FlatMap): Chain computations that may fail
|
||||
// - Ap: Apply a function wrapped in Either
|
||||
//
|
||||
// # Error Handling
|
||||
//
|
||||
// Either provides utilities for working with Go's error type:
|
||||
// - TryCatchError: Convert (value, error) tuples to Either
|
||||
// - UnwrapError: Convert Either back to (value, error) tuple
|
||||
// - FromError: Create Either from error-returning functions
|
||||
//
|
||||
// # Subpackages
|
||||
//
|
||||
// - either/exec: Execute system commands returning Either
|
||||
// - either/http: HTTP request builders returning Either
|
||||
// - either/testing: Testing utilities for Either laws
|
||||
package result
|
||||
|
||||
//go:generate go run .. either --count 15 --filename gen.go
|
||||
556
v2/result/either.go
Normal file
556
v2/result/either.go
Normal file
@@ -0,0 +1,556 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// package result implements the Either monad
|
||||
//
|
||||
// A data type that can be of either of two types but not both. This is
|
||||
// typically used to carry an error or a return value
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
"github.com/IBM/fp-go/v2/option"
|
||||
)
|
||||
|
||||
// Of constructs a Right value containing the given value.
|
||||
// This is the monadic return/pure operation for Either.
|
||||
// Equivalent to [Right].
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.Of[error](42) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func Of[A any](value A) Result[A] {
|
||||
return either.Of[error](value)
|
||||
}
|
||||
|
||||
// FromIO executes an IO operation and wraps the result in a Right value.
|
||||
// This is useful for lifting pure IO operations into the Either context.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// getValue := func() int { return 42 }
|
||||
// result := either.FromIO[error](getValue) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func FromIO[IO ~func() A, A any](f IO) Result[A] {
|
||||
return either.FromIO[error](f)
|
||||
}
|
||||
|
||||
// MonadAp applies a function wrapped in Either to a value wrapped in Either.
|
||||
// If either the function or the value is Left, returns Left.
|
||||
// This is the applicative apply operation.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// fab := either.Right[error](func(x int) int { return x * 2 })
|
||||
// fa := either.Right[error](21)
|
||||
// result := either.MonadAp(fab, fa) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func MonadAp[B, A any](fab Result[func(a A) B], fa Result[A]) Result[B] {
|
||||
return either.MonadAp(fab, fa)
|
||||
}
|
||||
|
||||
// Ap is the curried version of [MonadAp].
|
||||
// Returns a function that applies a wrapped function to the given wrapped value.
|
||||
//
|
||||
//go:inline
|
||||
func Ap[B, A any](fa Result[A]) Operator[func(A) B, B] {
|
||||
return either.Ap[B](fa)
|
||||
}
|
||||
|
||||
// MonadMap transforms the Right value using the provided function.
|
||||
// If the Either is Left, returns Left unchanged.
|
||||
// This is the functor map operation.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadMap(
|
||||
// either.Right[error](21),
|
||||
// func(x int) int { return x * 2 },
|
||||
// ) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func MonadMap[A, B any](fa Result[A], f func(a A) B) Result[B] {
|
||||
return either.MonadMap(fa, f)
|
||||
}
|
||||
|
||||
// MonadBiMap applies two functions: one to transform a Left value, another to transform a Right value.
|
||||
// This allows transforming both channels of the Either simultaneously.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadBiMap(
|
||||
// either.Left[int](errors.New("error")),
|
||||
// func(e error) string { return e.Error() },
|
||||
// func(n int) string { return fmt.Sprint(n) },
|
||||
// ) // Left("error")
|
||||
//
|
||||
//go:inline
|
||||
func MonadBiMap[E, A, B any](fa Result[A], f func(error) E, g func(a A) B) Either[E, B] {
|
||||
return either.MonadBiMap(fa, f, g)
|
||||
}
|
||||
|
||||
// BiMap is the curried version of [MonadBiMap].
|
||||
// Maps a pair of functions over the two type arguments of the bifunctor.
|
||||
//
|
||||
//go:inline
|
||||
func BiMap[E, A, B any](f func(error) E, g func(a A) B) func(Result[A]) Either[E, B] {
|
||||
return either.BiMap(f, g)
|
||||
}
|
||||
|
||||
// MonadMapTo replaces the Right value with a constant value.
|
||||
// If the Either is Left, returns Left unchanged.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadMapTo(either.Right[error](21), "success") // Right("success")
|
||||
//
|
||||
//go:inline
|
||||
func MonadMapTo[A, B any](fa Result[A], b B) Result[B] {
|
||||
return either.MonadMapTo(fa, b)
|
||||
}
|
||||
|
||||
// MapTo is the curried version of [MonadMapTo].
|
||||
//
|
||||
//go:inline
|
||||
func MapTo[A, B any](b B) Operator[A, B] {
|
||||
return either.MapTo[error, A](b)
|
||||
}
|
||||
|
||||
// MonadMapLeft applies a transformation function to the Left (error) value.
|
||||
// If the Either is Right, returns Right unchanged.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadMapLeft(
|
||||
// either.Left[int](errors.New("error")),
|
||||
// func(e error) string { return e.Error() },
|
||||
// ) // Left("error")
|
||||
//
|
||||
//go:inline
|
||||
func MonadMapLeft[A, E any](fa Result[A], f func(error) E) Either[E, A] {
|
||||
return either.MonadMapLeft(fa, f)
|
||||
}
|
||||
|
||||
// Map is the curried version of [MonadMap].
|
||||
// Transforms the Right value using the provided function.
|
||||
//
|
||||
//go:inline
|
||||
func Map[A, B any](f func(a A) B) Operator[A, B] {
|
||||
return either.Map[error](f)
|
||||
}
|
||||
|
||||
// MapLeft is the curried version of [MonadMapLeft].
|
||||
// Applies a mapping function to the Left (error) channel.
|
||||
//
|
||||
//go:inline
|
||||
func MapLeft[A, E any](f func(error) E) func(fa Result[A]) Either[E, A] {
|
||||
return either.MapLeft[A](f)
|
||||
}
|
||||
|
||||
// MonadChain sequences two computations, where the second depends on the result of the first.
|
||||
// If the first Either is Left, returns Left without executing the second computation.
|
||||
// This is the monadic bind operation (also known as flatMap).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadChain(
|
||||
// either.Right[error](21),
|
||||
// func(x int) either.Result[int] {
|
||||
// return either.Right[error](x * 2)
|
||||
// },
|
||||
// ) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func MonadChain[A, B any](fa Result[A], f Kleisli[A, B]) Result[B] {
|
||||
return either.MonadChain(fa, f)
|
||||
}
|
||||
|
||||
// MonadChainFirst executes a side-effect computation but returns the original value.
|
||||
// Useful for performing actions (like logging) without changing the value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadChainFirst(
|
||||
// either.Right[error](42),
|
||||
// func(x int) either.Result[string] {
|
||||
// fmt.Println(x) // side effect
|
||||
// return either.Right[error]("logged")
|
||||
// },
|
||||
// ) // Right(42) - original value preserved
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainFirst[A, B any](ma Result[A], f Kleisli[A, B]) Result[A] {
|
||||
return either.MonadChainFirst(ma, f)
|
||||
}
|
||||
|
||||
// MonadChainTo ignores the first Either and returns the second.
|
||||
// Useful for sequencing operations where you don't need the first result.
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainTo[A, B any](ma Result[A], mb Result[B]) Result[B] {
|
||||
return either.MonadChainTo(ma, mb)
|
||||
}
|
||||
|
||||
// MonadChainOptionK chains a function that returns an Option, converting None to Left.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.MonadChainOptionK(
|
||||
// func() error { return errors.New("not found") },
|
||||
// either.Right[error](42),
|
||||
// func(x int) option.Option[string] {
|
||||
// if x > 0 { return option.Some("positive") }
|
||||
// return option.None[string]()
|
||||
// },
|
||||
// ) // Right("positive")
|
||||
//
|
||||
//go:inline
|
||||
func MonadChainOptionK[A, B any](onNone func() error, ma Result[A], f option.Kleisli[A, B]) Result[B] {
|
||||
return either.MonadChainOptionK(onNone, ma, f)
|
||||
}
|
||||
|
||||
// ChainOptionK is the curried version of [MonadChainOptionK].
|
||||
//
|
||||
//go:inline
|
||||
func ChainOptionK[A, B any](onNone func() error) func(option.Kleisli[A, B]) Operator[A, B] {
|
||||
return either.ChainOptionK[A, B](onNone)
|
||||
}
|
||||
|
||||
// ChainTo is the curried version of [MonadChainTo].
|
||||
//
|
||||
//go:inline
|
||||
func ChainTo[A, B any](mb Result[B]) Operator[A, B] {
|
||||
return either.ChainTo[A](mb)
|
||||
}
|
||||
|
||||
// Chain is the curried version of [MonadChain].
|
||||
// Sequences two computations where the second depends on the first.
|
||||
//
|
||||
//go:inline
|
||||
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||
return either.Chain(f)
|
||||
}
|
||||
|
||||
// ChainFirst is the curried version of [MonadChainFirst].
|
||||
//
|
||||
//go:inline
|
||||
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
||||
return either.ChainFirst(f)
|
||||
}
|
||||
|
||||
// Flatten removes one level of nesting from a nested Either.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// nested := either.Right[error](either.Right[error](42))
|
||||
// result := either.Flatten(nested) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func Flatten[A any](mma Result[Result[A]]) Result[A] {
|
||||
return either.Flatten(mma)
|
||||
}
|
||||
|
||||
// TryCatch converts a (value, error) tuple into an Either, applying a transformation to the error.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.TryCatch(
|
||||
// 42, nil,
|
||||
// func(err error) string { return err.Error() },
|
||||
// ) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func TryCatch[FE Endomorphism[error], A any](val A, err error, onThrow FE) Result[A] {
|
||||
return either.TryCatch(val, err, onThrow)
|
||||
}
|
||||
|
||||
// TryCatchError is a specialized version of [TryCatch] for error types.
|
||||
// Converts a (value, error) tuple into Result[A].
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.TryCatchError(42, nil) // Right(42)
|
||||
// result := either.TryCatchError(0, errors.New("fail")) // Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func TryCatchError[A any](val A, err error) Result[A] {
|
||||
return either.TryCatchError(val, err)
|
||||
}
|
||||
|
||||
// Sequence2 sequences two Either values using a combining function.
|
||||
// Short-circuits on the first Left encountered.
|
||||
//
|
||||
//go:inline
|
||||
func Sequence2[T1, T2, R any](f func(T1, T2) Result[R]) func(Result[T1], Result[T2]) Result[R] {
|
||||
return either.Sequence2(f)
|
||||
}
|
||||
|
||||
// Sequence3 sequences three Either values using a combining function.
|
||||
// Short-circuits on the first Left encountered.
|
||||
//
|
||||
//go:inline
|
||||
func Sequence3[T1, T2, T3, R any](f func(T1, T2, T3) Result[R]) func(Result[T1], Result[T2], Result[T3]) Result[R] {
|
||||
return either.Sequence3(f)
|
||||
}
|
||||
|
||||
// FromOption converts an Option to an Either, using the provided function to generate a Left value for None.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// opt := option.Some(42)
|
||||
// result := either.FromOption[int](func() error { return errors.New("none") })(opt) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func FromOption[A any](onNone func() error) func(Option[A]) Result[A] {
|
||||
return either.FromOption[A](onNone)
|
||||
}
|
||||
|
||||
// ToOption converts an Either to an Option, discarding the Left value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.ToOption(either.Right[error](42)) // Some(42)
|
||||
// result := either.ToOption(either.Left[int](errors.New("err"))) // None
|
||||
//
|
||||
//go:inline
|
||||
func ToOption[A any](ma Result[A]) Option[A] {
|
||||
return either.ToOption(ma)
|
||||
}
|
||||
|
||||
// FromError creates an Either from a function that may return an error.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// validate := func(x int) error {
|
||||
// if x < 0 { return errors.New("negative") }
|
||||
// return nil
|
||||
// }
|
||||
// toEither := either.FromError(validate)
|
||||
// result := toEither(42) // Right(42)
|
||||
//
|
||||
//go:inline
|
||||
func FromError[A any](f func(a A) error) Kleisli[A, A] {
|
||||
return either.FromError(f)
|
||||
}
|
||||
|
||||
// ToError converts an Result[A] to an error, returning nil for Right values.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// err := either.ToError(either.Left[int](errors.New("fail"))) // error
|
||||
// err := either.ToError(either.Right[error](42)) // nil
|
||||
//
|
||||
//go:inline
|
||||
func ToError[A any](e Result[A]) error {
|
||||
return either.ToError(e)
|
||||
}
|
||||
|
||||
// Fold is the curried version of [MonadFold].
|
||||
// Extracts the value from an Either by providing handlers for both cases.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.Fold(
|
||||
// func(err error) string { return "Error: " + err.Error() },
|
||||
// func(n int) string { return fmt.Sprintf("Value: %d", n) },
|
||||
// )(either.Right[error](42)) // "Value: 42"
|
||||
//
|
||||
//go:inline
|
||||
func Fold[A, B any](onLeft func(error) B, onRight func(A) B) func(Result[A]) B {
|
||||
return either.Fold(onLeft, onRight)
|
||||
}
|
||||
|
||||
// UnwrapError converts an Result[A] into the idiomatic Go tuple (A, error).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// val, err := either.UnwrapError(either.Right[error](42)) // 42, nil
|
||||
// val, err := either.UnwrapError(either.Left[int](errors.New("fail"))) // zero, error
|
||||
//
|
||||
//go:inline
|
||||
func UnwrapError[A any](ma Result[A]) (A, error) {
|
||||
return either.UnwrapError(ma)
|
||||
}
|
||||
|
||||
// FromPredicate creates an Either based on a predicate.
|
||||
// If the predicate returns true, creates a Right; otherwise creates a Left using onFalse.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// isPositive := either.FromPredicate(
|
||||
// func(x int) bool { return x > 0 },
|
||||
// func(x int) error { return errors.New("not positive") },
|
||||
// )
|
||||
// result := isPositive(42) // Right(42)
|
||||
// result := isPositive(-1) // Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A] {
|
||||
return either.FromPredicate(pred, onFalse)
|
||||
}
|
||||
|
||||
// FromNillable creates an Either from a pointer, using the provided error for nil pointers.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var ptr *int = nil
|
||||
// result := either.FromNillable[int](errors.New("nil"))(ptr) // Left(error)
|
||||
// val := 42
|
||||
// result := either.FromNillable[int](errors.New("nil"))(&val) // Right(&42)
|
||||
//
|
||||
//go:inline
|
||||
func FromNillable[A any](e error) func(*A) Result[*A] {
|
||||
return either.FromNillable[A](e)
|
||||
}
|
||||
|
||||
// GetOrElse extracts the Right value or computes a default from the Left value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.GetOrElse(func(err error) int { return 0 })(either.Right[error](42)) // 42
|
||||
// result := either.GetOrElse(func(err error) int { return 0 })(either.Left[int](err)) // 0
|
||||
//
|
||||
//go:inline
|
||||
func GetOrElse[A any](onLeft func(error) A) func(Result[A]) A {
|
||||
return either.GetOrElse(onLeft)
|
||||
}
|
||||
|
||||
// Reduce folds an Either into a single value using a reducer function.
|
||||
// Returns the initial value for Left, or applies the reducer to the Right value.
|
||||
//
|
||||
//go:inline
|
||||
func Reduce[A, B any](f func(B, A) B, initial B) func(Result[A]) B {
|
||||
return either.Reduce[error](f, initial)
|
||||
}
|
||||
|
||||
// AltW provides an alternative Either if the first is Left, allowing different error types.
|
||||
// The 'W' suffix indicates "widening" of the error type.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// alternative := either.AltW[error, string](func() either.Either[string, int] {
|
||||
// return either.Right[string](99)
|
||||
// })
|
||||
// result := alternative(either.Left[int](errors.New("fail"))) // Right(99)
|
||||
//
|
||||
//go:inline
|
||||
func AltW[E1, A any](that Lazy[Either[E1, A]]) func(Result[A]) Either[E1, A] {
|
||||
return either.AltW[error](that)
|
||||
}
|
||||
|
||||
// Alt provides an alternative Either if the first is Left.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// alternative := either.Alt[error](func() either.Result[int] {
|
||||
// return either.Right[error](99)
|
||||
// })
|
||||
// result := alternative(either.Left[int](errors.New("fail"))) // Right(99)
|
||||
//
|
||||
//go:inline
|
||||
func Alt[A any](that Lazy[Result[A]]) Operator[A, A] {
|
||||
return either.Alt(that)
|
||||
}
|
||||
|
||||
// OrElse recovers from a Left by providing an alternative computation.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// recover := either.OrElse(func(err error) either.Result[int] {
|
||||
// return either.Right[error](0) // default value
|
||||
// })
|
||||
// result := recover(either.Left[int](errors.New("fail"))) // Right(0)
|
||||
//
|
||||
//go:inline
|
||||
func OrElse[A any](onLeft Kleisli[error, A]) Operator[A, A] {
|
||||
return either.OrElse(onLeft)
|
||||
}
|
||||
|
||||
// ToType attempts to convert an any value to a specific type, returning Either.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// convert := either.ToType[int](func(v any) error {
|
||||
// return fmt.Errorf("cannot convert %v to int", v)
|
||||
// })
|
||||
// result := convert(42) // Right(42)
|
||||
// result := convert("string") // Left(error)
|
||||
//
|
||||
//go:inline
|
||||
func ToType[A any](onError func(any) error) Kleisli[any, A] {
|
||||
return either.ToType[A](onError)
|
||||
}
|
||||
|
||||
// Memoize returns the Either unchanged (Either values are already memoized).
|
||||
//
|
||||
//go:inline
|
||||
func Memoize[A any](val Result[A]) Result[A] {
|
||||
return either.Memoize(val)
|
||||
}
|
||||
|
||||
// MonadSequence2 sequences two Either values using a combining function.
|
||||
// Short-circuits on the first Left encountered.
|
||||
//
|
||||
//go:inline
|
||||
func MonadSequence2[T1, T2, R any](e1 Result[T1], e2 Result[T2], f func(T1, T2) Result[R]) Result[R] {
|
||||
return either.MonadSequence2(e1, e2, f)
|
||||
}
|
||||
|
||||
// MonadSequence3 sequences three Either values using a combining function.
|
||||
// Short-circuits on the first Left encountered.
|
||||
//
|
||||
//go:inline
|
||||
func MonadSequence3[T1, T2, T3, R any](e1 Result[T1], e2 Result[T2], e3 Result[T3], f func(T1, T2, T3) Result[R]) Result[R] {
|
||||
return either.MonadSequence3(e1, e2, e3, f)
|
||||
}
|
||||
|
||||
// Swap exchanges the Left and Right type parameters.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// result := either.Swap(either.Right[error](42)) // Left(42)
|
||||
// result := either.Swap(either.Left[int](errors.New("err"))) // Right(error)
|
||||
//
|
||||
//go:inline
|
||||
func Swap[A any](val Result[A]) Either[A, error] {
|
||||
return either.Swap(val)
|
||||
}
|
||||
|
||||
// MonadFlap applies a value to a function wrapped in Either.
|
||||
// This is the reverse of [MonadAp].
|
||||
//
|
||||
//go:inline
|
||||
func MonadFlap[B, A any](fab Result[func(A) B], a A) Result[B] {
|
||||
return either.MonadFlap(fab, a)
|
||||
}
|
||||
|
||||
// Flap is the curried version of [MonadFlap].
|
||||
//
|
||||
//go:inline
|
||||
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
||||
return either.Flap[error, B](a)
|
||||
}
|
||||
|
||||
// MonadAlt provides an alternative Either if the first is Left.
|
||||
// This is the monadic version of [Alt].
|
||||
//
|
||||
//go:inline
|
||||
func MonadAlt[A any](fa Result[A], that Lazy[Result[A]]) Result[A] {
|
||||
return either.MonadAlt(fa, that)
|
||||
}
|
||||
142
v2/result/either_test.go
Normal file
142
v2/result/either_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
F "github.com/IBM/fp-go/v2/function"
|
||||
"github.com/IBM/fp-go/v2/internal/utils"
|
||||
IO "github.com/IBM/fp-go/v2/io"
|
||||
O "github.com/IBM/fp-go/v2/option"
|
||||
S "github.com/IBM/fp-go/v2/string"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsLeft(t *testing.T) {
|
||||
err := errors.New("Some error")
|
||||
withError := Left[string](err)
|
||||
|
||||
assert.True(t, IsLeft(withError))
|
||||
assert.False(t, IsRight(withError))
|
||||
}
|
||||
|
||||
func TestIsRight(t *testing.T) {
|
||||
noError := Right("Carsten")
|
||||
|
||||
assert.True(t, IsRight(noError))
|
||||
assert.False(t, IsLeft(noError))
|
||||
}
|
||||
|
||||
func TestMapEither(t *testing.T) {
|
||||
e := errors.New("s")
|
||||
assert.Equal(t, F.Pipe1(Right("abc"), Map(utils.StringLen)), Right(3))
|
||||
|
||||
val2 := F.Pipe1(Left[string](e), Map(utils.StringLen))
|
||||
exp2 := Left[int](e)
|
||||
|
||||
assert.Equal(t, val2, exp2)
|
||||
}
|
||||
|
||||
func TestUnwrapError(t *testing.T) {
|
||||
a := ""
|
||||
err := errors.New("Some error")
|
||||
withError := Left[string](err)
|
||||
|
||||
res, extracted := UnwrapError(withError)
|
||||
assert.Equal(t, a, res)
|
||||
assert.Equal(t, extracted, err)
|
||||
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
|
||||
s := S.Semigroup()
|
||||
|
||||
assert.Equal(t, "foobar", F.Pipe1(Right("bar"), Reduce(s.Concat, "foo")))
|
||||
assert.Equal(t, "foo", F.Pipe1(Left[string](errors.New("bar")), Reduce(s.Concat, "foo")))
|
||||
|
||||
}
|
||||
func TestAp(t *testing.T) {
|
||||
f := S.Size
|
||||
|
||||
maError := errors.New("maError")
|
||||
mabError := errors.New("mabError")
|
||||
|
||||
assert.Equal(t, Right(3), F.Pipe1(Right(f), Ap[int](Right("abc"))))
|
||||
assert.Equal(t, Left[int](maError), F.Pipe1(Right(f), Ap[int](Left[string](maError))))
|
||||
assert.Equal(t, Left[int](mabError), F.Pipe1(Left[func(string) int](mabError), Ap[int](Left[string](maError))))
|
||||
}
|
||||
|
||||
func TestAlt(t *testing.T) {
|
||||
|
||||
a := errors.New("a")
|
||||
b := errors.New("b")
|
||||
|
||||
assert.Equal(t, Right(1), F.Pipe1(Right(1), Alt(F.Constant(Right(2)))))
|
||||
assert.Equal(t, Right(1), F.Pipe1(Right(1), Alt(F.Constant(Left[int](a)))))
|
||||
assert.Equal(t, Right(2), F.Pipe1(Left[int](b), Alt(F.Constant(Right(2)))))
|
||||
assert.Equal(t, Left[int](b), F.Pipe1(Left[int](a), Alt(F.Constant(Left[int](b)))))
|
||||
}
|
||||
|
||||
func TestChainFirst(t *testing.T) {
|
||||
f := F.Flow2(S.Size, Right[int])
|
||||
maError := errors.New("maError")
|
||||
|
||||
assert.Equal(t, Right("abc"), F.Pipe1(Right("abc"), ChainFirst(f)))
|
||||
assert.Equal(t, Left[string](maError), F.Pipe1(Left[string](maError), ChainFirst(f)))
|
||||
}
|
||||
|
||||
func TestChainOptionK(t *testing.T) {
|
||||
a := errors.New("a")
|
||||
b := errors.New("b")
|
||||
|
||||
f := ChainOptionK[int, int](F.Constant(a))(func(n int) Option[int] {
|
||||
if n > 0 {
|
||||
return O.Some(n)
|
||||
}
|
||||
return O.None[int]()
|
||||
})
|
||||
assert.Equal(t, Right(1), f(Right(1)))
|
||||
assert.Equal(t, Left[int](a), f(Right(-1)))
|
||||
assert.Equal(t, Left[int](b), f(Left[int](b)))
|
||||
}
|
||||
|
||||
func TestFromOption(t *testing.T) {
|
||||
none := errors.New("none")
|
||||
|
||||
assert.Equal(t, Left[int](none), FromOption[int](F.Constant(none))(O.None[int]()))
|
||||
assert.Equal(t, Right(1), FromOption[int](F.Constant(none))(O.Some(1)))
|
||||
}
|
||||
|
||||
func TestStringer(t *testing.T) {
|
||||
e := Of("foo")
|
||||
exp := "Right[string](foo)"
|
||||
|
||||
assert.Equal(t, exp, e.String())
|
||||
|
||||
var s fmt.Stringer = &e
|
||||
assert.Equal(t, exp, s.String())
|
||||
}
|
||||
|
||||
func TestFromIO(t *testing.T) {
|
||||
f := IO.Of("abc")
|
||||
e := FromIO(f)
|
||||
|
||||
assert.Equal(t, Right("abc"), e)
|
||||
}
|
||||
49
v2/result/eq.go
Normal file
49
v2/result/eq.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/IBM/fp-go/v2/either"
|
||||
"github.com/IBM/fp-go/v2/eq"
|
||||
)
|
||||
|
||||
// Eq constructs an equality predicate for Either values.
|
||||
// Two Either values are equal if they are both Left with equal error values,
|
||||
// or both Right with equal success values.
|
||||
//
|
||||
// Parameters:
|
||||
// - e: Equality predicate for the Left (error) type
|
||||
// - a: Equality predicate for the Right (success) type
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// eq := either.Eq(eq.FromStrictEquals[error](), eq.FromStrictEquals[int]())
|
||||
// result := eq.Equals(either.Right[error](42), either.Right[error](42)) // true
|
||||
// result2 := eq.Equals(either.Right[error](42), either.Right[error](43)) // false
|
||||
func Eq[A any](a eq.Eq[A]) eq.Eq[Result[A]] {
|
||||
return either.Eq(eq.FromStrictEquals[error](), a)
|
||||
}
|
||||
|
||||
// FromStrictEquals constructs an equality predicate using Go's == operator.
|
||||
// Both the Left and Right types must be comparable.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// eq := either.FromStrictEquals[error, int]()
|
||||
// result := eq.Equals(either.Right[error](42), either.Right[error](42)) // true
|
||||
func FromStrictEquals[A comparable]() eq.Eq[Result[A]] {
|
||||
return either.FromStrictEquals[error, A]()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user