1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-09 23:11:40 +02:00

Compare commits

..

3 Commits

Author SHA1 Message Date
Dr. Carsten Leue
62fcd186a3 fix: introcuce readeioresult
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-10 18:44:14 +01:00
Dr. Carsten Leue
2db7e83651 fix: introduce IOResult
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-10 16:26:52 +01:00
Dr. Carsten Leue
8d92df83ad fix: introduce Result type for convenience
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-10 12:08:50 +01:00
134 changed files with 12672 additions and 1944 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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))
}

View File

@@ -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])
}

View File

@@ -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++ {

View File

@@ -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)
}

View File

@@ -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]))
}

View File

@@ -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)
})

View File

@@ -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()
})

View File

@@ -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],

View File

@@ -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)
}

View File

@@ -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
View 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]
)

View File

@@ -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])

File diff suppressed because it is too large Load Diff

View File

@@ -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,

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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))
}

View 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)
}

View 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)
}

View 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
View 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
)

View 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
View 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
View 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)
}

View 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)
}

View 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)
}

View 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
View 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
View 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)
}

View 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)
}

View 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()))
}

View 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
View 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
)

View 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)
}

View 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))
}

View 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
View 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)
}

View 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
View 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)
}

View 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
View 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
View 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)
}

View 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
View 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
View 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
View 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]()
}

View 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
View 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)
}

View 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)
}

View 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
View 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)
}

View 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
View 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]
)

View File

@@ -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.

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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),
})

View File

@@ -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)
}

View File

@@ -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]
)

View 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
View 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)
}

View 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"))
}

View 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)
}

View 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
View 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
View 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
View 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)
}

View 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)
}

View 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
View 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)
}

View 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())())
}

View 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)
}

View 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)
}

View 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)
}

View 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
View 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)
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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