mirror of
https://github.com/IBM/fp-go.git
synced 2025-12-09 23:11:40 +02:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca813b673c | ||
|
|
af271e7d10 | ||
|
|
567315a31c | ||
|
|
311ed55f06 | ||
|
|
23333ce52c | ||
|
|
eb7fc9f77b | ||
|
|
fd0550e71b | ||
|
|
13063bbd88 | ||
|
|
4f8a557072 | ||
|
|
a4e790ac3d | ||
|
|
1af6501cd8 | ||
|
|
600521b220 | ||
|
|
62fcd186a3 | ||
|
|
2db7e83651 | ||
|
|
8d92df83ad |
244
v2/README.md
244
v2/README.md
@@ -2,25 +2,152 @@
|
|||||||
|
|
||||||
[](https://pkg.go.dev/github.com/IBM/fp-go/v2)
|
[](https://pkg.go.dev/github.com/IBM/fp-go/v2)
|
||||||
[](https://coveralls.io/github/IBM/fp-go?branch=main)
|
[](https://coveralls.io/github/IBM/fp-go?branch=main)
|
||||||
|
[](https://goreportcard.com/report/github.com/IBM/fp-go/v2)
|
||||||
|
|
||||||
Version 2 of fp-go leverages [generic type aliases](https://github.com/golang/go/issues/46477) introduced in Go 1.24, providing a more ergonomic and streamlined API.
|
**fp-go** is a comprehensive functional programming library for Go, bringing type-safe functional patterns inspired by [fp-ts](https://gcanti.github.io/fp-ts/) to the Go ecosystem. Version 2 leverages [generic type aliases](https://github.com/golang/go/issues/46477) introduced in Go 1.24, providing a more ergonomic and streamlined API.
|
||||||
|
|
||||||
## 📚 Table of Contents
|
## 📚 Table of Contents
|
||||||
|
|
||||||
|
- [Overview](#-overview)
|
||||||
|
- [Features](#-features)
|
||||||
- [Requirements](#-requirements)
|
- [Requirements](#-requirements)
|
||||||
- [Breaking Changes](#-breaking-changes)
|
- [Installation](#-installation)
|
||||||
|
- [Quick Start](#-quick-start)
|
||||||
|
- [Breaking Changes](#️-breaking-changes)
|
||||||
- [Key Improvements](#-key-improvements)
|
- [Key Improvements](#-key-improvements)
|
||||||
- [Migration Guide](#-migration-guide)
|
- [Migration Guide](#-migration-guide)
|
||||||
- [Installation](#-installation)
|
|
||||||
- [What's New](#-whats-new)
|
- [What's New](#-whats-new)
|
||||||
|
- [Documentation](#-documentation)
|
||||||
|
- [Contributing](#-contributing)
|
||||||
|
- [License](#-license)
|
||||||
|
|
||||||
|
## 🎯 Overview
|
||||||
|
|
||||||
|
fp-go brings the power of functional programming to Go with:
|
||||||
|
|
||||||
|
- **Type-safe abstractions** - Monads, Functors, Applicatives, and more
|
||||||
|
- **Composable operations** - Build complex logic from simple, reusable functions
|
||||||
|
- **Error handling** - Elegant error management with `Either`, `Result`, and `IOEither`
|
||||||
|
- **Lazy evaluation** - Control when and how computations execute
|
||||||
|
- **Optics** - Powerful lens, prism, and traversal operations for immutable data manipulation
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- 🔒 **Type Safety** - Leverage Go's generics for compile-time guarantees
|
||||||
|
- 🧩 **Composability** - Chain operations naturally with functional composition
|
||||||
|
- 📦 **Rich Type System** - `Option`, `Either`, `Result`, `IO`, `Reader`, and more
|
||||||
|
- 🎯 **Practical** - Designed for real-world Go applications
|
||||||
|
- 🚀 **Performance** - Zero-cost abstractions where possible
|
||||||
|
- 📖 **Well-documented** - Comprehensive API documentation and examples
|
||||||
|
- 🧪 **Battle-tested** - Extensive test coverage
|
||||||
|
|
||||||
## 🔧 Requirements
|
## 🔧 Requirements
|
||||||
|
|
||||||
- **Go 1.24 or later** (for generic type alias support)
|
- **Go 1.24 or later** (for generic type alias support)
|
||||||
|
|
||||||
## ⚠️ Breaking Changes
|
## 📦 Installation
|
||||||
|
|
||||||
### 1. Generic Type Aliases
|
```bash
|
||||||
|
go get github.com/IBM/fp-go/v2
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Working with Option
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create an Option
|
||||||
|
some := option.Some(42)
|
||||||
|
none := option.None[int]()
|
||||||
|
|
||||||
|
// Map over values
|
||||||
|
doubled := option.Map(func(x int) int { return x * 2 })(some)
|
||||||
|
fmt.Println(option.GetOrElse(0)(doubled)) // Output: 84
|
||||||
|
|
||||||
|
// Chain operations
|
||||||
|
result := option.Chain(func(x int) option.Option[string] {
|
||||||
|
if x > 0 {
|
||||||
|
return option.Some(fmt.Sprintf("Positive: %d", x))
|
||||||
|
}
|
||||||
|
return option.None[string]()
|
||||||
|
})(some)
|
||||||
|
|
||||||
|
fmt.Println(option.GetOrElse("No value")(result)) // Output: Positive: 42
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling with Result
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
func divide(a, b int) result.Result[int] {
|
||||||
|
if b == 0 {
|
||||||
|
return result.Error[int](errors.New("division by zero"))
|
||||||
|
}
|
||||||
|
return result.Ok(a / b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
res := divide(10, 2)
|
||||||
|
|
||||||
|
// Pattern match on the result
|
||||||
|
result.Fold(
|
||||||
|
func(err error) { fmt.Println("Error:", err) },
|
||||||
|
func(val int) { fmt.Println("Result:", val) },
|
||||||
|
)(res)
|
||||||
|
// Output: Result: 5
|
||||||
|
|
||||||
|
// Or use GetOrElse for a default value
|
||||||
|
value := result.GetOrElse(0)(divide(10, 0))
|
||||||
|
fmt.Println("Value:", value) // Output: Value: 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Composing IO Operations
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/IBM/fp-go/v2/io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Define pure IO operations
|
||||||
|
readInput := io.MakeIO(func() string {
|
||||||
|
return "Hello, fp-go!"
|
||||||
|
})
|
||||||
|
|
||||||
|
// Transform the result
|
||||||
|
uppercase := io.Map(func(s string) string {
|
||||||
|
return fmt.Sprintf(">>> %s <<<", s)
|
||||||
|
})(readInput)
|
||||||
|
|
||||||
|
// Execute the IO operation
|
||||||
|
result := uppercase()
|
||||||
|
fmt.Println(result) // Output: >>> Hello, fp-go! <<<
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### From V1 to V2
|
||||||
|
|
||||||
|
#### 1. Generic Type Aliases
|
||||||
|
|
||||||
V2 uses [generic type aliases](https://github.com/golang/go/issues/46477) which require Go 1.24+. This is the most significant change and enables cleaner type definitions.
|
V2 uses [generic type aliases](https://github.com/golang/go/issues/46477) which require Go 1.24+. This is the most significant change and enables cleaner type definitions.
|
||||||
|
|
||||||
@@ -34,7 +161,7 @@ type ReaderIOEither[R, E, A any] RD.Reader[R, IOE.IOEither[E, A]]
|
|||||||
type ReaderIOEither[R, E, A any] = RD.Reader[R, IOE.IOEither[E, A]]
|
type ReaderIOEither[R, E, A any] = RD.Reader[R, IOE.IOEither[E, A]]
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Generic Type Parameter Ordering
|
#### 2. Generic Type Parameter Ordering
|
||||||
|
|
||||||
Type parameters that **cannot** be inferred from function arguments now come first, improving type inference.
|
Type parameters that **cannot** be inferred from function arguments now come first, improving type inference.
|
||||||
|
|
||||||
@@ -52,7 +179,7 @@ func Ap[B, R, E, A any](fa ReaderIOEither[R, E, A]) func(ReaderIOEither[R, E, fu
|
|||||||
|
|
||||||
This change allows the Go compiler to infer more types automatically, reducing the need for explicit type parameters.
|
This change allows the Go compiler to infer more types automatically, reducing the need for explicit type parameters.
|
||||||
|
|
||||||
### 3. Pair Monad Semantics
|
#### 3. Pair Monad Semantics
|
||||||
|
|
||||||
Monadic operations for `Pair` now operate on the **second argument** to align with the [Haskell definition](https://hackage.haskell.org/package/TypeCompose-0.9.14/docs/Data-Pair.html).
|
Monadic operations for `Pair` now operate on the **second argument** to align with the [Haskell definition](https://hackage.haskell.org/package/TypeCompose-0.9.14/docs/Data-Pair.html).
|
||||||
|
|
||||||
@@ -70,6 +197,36 @@ pair := MakePair(1, "hello")
|
|||||||
result := Map(func(s string) string { return s + "!" })(pair) // Pair(1, "hello!")
|
result := Map(func(s string) string { return s + "!" })(pair) // Pair(1, "hello!")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 4. Endomorphism Compose Semantics
|
||||||
|
|
||||||
|
The `Compose` function for endomorphisms now follows **mathematical function composition** (right-to-left execution), aligning with standard functional programming conventions.
|
||||||
|
|
||||||
|
**V1:**
|
||||||
|
```go
|
||||||
|
// Compose executed left-to-right
|
||||||
|
double := func(x int) int { return x * 2 }
|
||||||
|
increment := func(x int) int { return x + 1 }
|
||||||
|
composed := Compose(double, increment)
|
||||||
|
result := composed(5) // (5 * 2) + 1 = 11
|
||||||
|
```
|
||||||
|
|
||||||
|
**V2:**
|
||||||
|
```go
|
||||||
|
// Compose executes RIGHT-TO-LEFT (mathematical composition)
|
||||||
|
double := func(x int) int { return x * 2 }
|
||||||
|
increment := func(x int) int { return x + 1 }
|
||||||
|
composed := Compose(double, increment)
|
||||||
|
result := composed(5) // (5 + 1) * 2 = 12
|
||||||
|
|
||||||
|
// Use MonadChain for LEFT-TO-RIGHT execution
|
||||||
|
chained := MonadChain(double, increment)
|
||||||
|
result2 := chained(5) // (5 * 2) + 1 = 11
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Difference:**
|
||||||
|
- `Compose(f, g)` now means `f ∘ g`, which applies `g` first, then `f` (right-to-left)
|
||||||
|
- `MonadChain(f, g)` applies `f` first, then `g` (left-to-right)
|
||||||
|
|
||||||
## ✨ Key Improvements
|
## ✨ Key Improvements
|
||||||
|
|
||||||
### 1. Simplified Type Declarations
|
### 1. Simplified Type Declarations
|
||||||
@@ -91,16 +248,16 @@ func processData(input string) ET.Either[error, OPT.Option[int]] {
|
|||||||
**V2 Approach:**
|
**V2 Approach:**
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/either"
|
"github.com/IBM/fp-go/v2/result"
|
||||||
"github.com/IBM/fp-go/v2/option"
|
"github.com/IBM/fp-go/v2/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Define type aliases once
|
// Define type aliases once
|
||||||
type Either[A any] = either.Either[error, A]
|
type Result[A any] = result.Result[A]
|
||||||
type Option[A any] = option.Option[A]
|
type Option[A any] = option.Option[A]
|
||||||
|
|
||||||
// Use them throughout your codebase
|
// Use them throughout your codebase
|
||||||
func processData(input string) Either[Option[int]] {
|
func processData(input string) Result[Option[int]] {
|
||||||
// implementation
|
// implementation
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -230,20 +387,14 @@ Create project-wide type aliases for common patterns:
|
|||||||
package myapp
|
package myapp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/either"
|
"github.com/IBM/fp-go/v2/result"
|
||||||
"github.com/IBM/fp-go/v2/option"
|
"github.com/IBM/fp-go/v2/option"
|
||||||
"github.com/IBM/fp-go/v2/ioeither"
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Either[A any] = either.Either[error, A]
|
type Result[A any] = result.Result[A]
|
||||||
type Option[A any] = option.Option[A]
|
type Option[A any] = option.Option[A]
|
||||||
type IOEither[A any] = ioeither.IOEither[error, A]
|
type IOResult[A any] = ioresult.IOResult[A]
|
||||||
```
|
|
||||||
|
|
||||||
## 📦 Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/IBM/fp-go/v2
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🆕 What's New
|
## 🆕 What's New
|
||||||
@@ -269,7 +420,7 @@ import (
|
|||||||
|
|
||||||
func process() IOET.IOEither[error, string] {
|
func process() IOET.IOEither[error, string] {
|
||||||
return IOEG.Map[error, int, string](
|
return IOEG.Map[error, int, string](
|
||||||
func(x int) string { return fmt.Sprintf("%d", x) },
|
strconv.Itoa,
|
||||||
)(fetchData())
|
)(fetchData())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -277,25 +428,37 @@ func process() IOET.IOEither[error, string] {
|
|||||||
**V2 Simplified Example:**
|
**V2 Simplified Example:**
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/either"
|
"strconv"
|
||||||
"github.com/IBM/fp-go/v2/ioeither"
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IOEither[A any] = ioeither.IOEither[error, A]
|
type IOResult[A any] = ioresult.IOResult[A]
|
||||||
|
|
||||||
func process() IOEither[string] {
|
func process() IOResult[string] {
|
||||||
return ioeither.Map(
|
return ioresult.Map(
|
||||||
func(x int) string { return fmt.Sprintf("%d", x) },
|
strconv.Itoa,
|
||||||
)(fetchData())
|
)(fetchData())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📚 Additional Resources
|
## 📚 Documentation
|
||||||
|
|
||||||
- [Main README](../README.md) - Core concepts and design philosophy
|
- **[API Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2)** - Complete API reference
|
||||||
- [API Documentation](https://pkg.go.dev/github.com/IBM/fp-go/v2)
|
- **[Code Samples](./samples/)** - Practical examples and use cases
|
||||||
- [Code Samples](../samples/)
|
- **[Go 1.24 Release Notes](https://tip.golang.org/doc/go1.24)** - Information about generic type aliases
|
||||||
- [Go 1.24 Release Notes](https://tip.golang.org/doc/go1.24)
|
|
||||||
|
### Core Modules
|
||||||
|
|
||||||
|
- **Option** - Represent optional values without nil
|
||||||
|
- **Either** - Type-safe error handling with left/right values
|
||||||
|
- **Result** - Simplified Either with error as left type
|
||||||
|
- **IO** - Lazy evaluation and side effect management
|
||||||
|
- **IOEither** - Combine IO with error handling
|
||||||
|
- **Reader** - Dependency injection pattern
|
||||||
|
- **ReaderIOEither** - Combine Reader, IO, and Either for complex workflows
|
||||||
|
- **Array** - Functional array operations
|
||||||
|
- **Record** - Functional record/map operations
|
||||||
|
- **Optics** - Lens, Prism, Optional, and Traversal for immutable updates
|
||||||
|
|
||||||
## 🤔 Should I Migrate?
|
## 🤔 Should I Migrate?
|
||||||
|
|
||||||
@@ -310,10 +473,25 @@ func process() IOEither[string] {
|
|||||||
- ⚠️ Migration effort outweighs benefits for your project
|
- ⚠️ Migration effort outweighs benefits for your project
|
||||||
- ⚠️ You need stability in production (V2 is newer)
|
- ⚠️ You need stability in production (V2 is newer)
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Here's how you can help:
|
||||||
|
|
||||||
|
1. **Report bugs** - Open an issue with a clear description and reproduction steps
|
||||||
|
2. **Suggest features** - Share your ideas for improvements
|
||||||
|
3. **Submit PRs** - Fix bugs or add features (please discuss major changes first)
|
||||||
|
4. **Improve docs** - Help make the documentation clearer and more comprehensive
|
||||||
|
|
||||||
|
Please read our contribution guidelines before submitting pull requests.
|
||||||
|
|
||||||
## 🐛 Issues and Feedback
|
## 🐛 Issues and Feedback
|
||||||
|
|
||||||
Found a bug or have a suggestion? Please [open an issue](https://github.com/IBM/fp-go/issues) on GitHub.
|
Found a bug or have a suggestion? Please [open an issue](https://github.com/IBM/fp-go/issues) on GitHub.
|
||||||
|
|
||||||
## 📄 License
|
## 📄 License
|
||||||
|
|
||||||
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
|
This project is licensed under the Apache License 2.0. See the [LICENSE](https://github.com/IBM/fp-go/blob/main/LICENSE) file for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Made with ❤️ by IBM**
|
||||||
@@ -82,7 +82,7 @@ func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func Map[A, B any](f func(a A) B) func([]A) []B {
|
func Map[A, B any](f func(a A) B) func([]A) []B {
|
||||||
return G.Map[[]A, []B, A, B](f)
|
return G.Map[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapRef applies a function to a pointer to each element of an array, returning a new array with the results.
|
// MapRef applies a function to a pointer to each element of an array, returning a new array with the results.
|
||||||
@@ -278,7 +278,7 @@ func Of[A any](a A) []A {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
|
func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
|
||||||
return G.MonadChain[[]A, []B](fa, f)
|
return G.MonadChain(fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chain applies a function that returns an array to each element and flattens the results.
|
// Chain applies a function that returns an array to each element and flattens the results.
|
||||||
@@ -291,7 +291,7 @@ func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func Chain[A, B any](f func(A) []B) func([]A) []B {
|
func Chain[A, B any](f func(A) []B) func([]A) []B {
|
||||||
return G.Chain[[]A, []B](f)
|
return G.Chain[[]A](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadAp applies an array of functions to an array of values, producing all combinations.
|
// MonadAp applies an array of functions to an array of values, producing all combinations.
|
||||||
@@ -314,14 +314,14 @@ func Ap[B, A any](fa []A) func([]func(A) B) []B {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
||||||
return G.Match[[]A](onEmpty, onNonEmpty)
|
return G.Match(onEmpty, onNonEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchLeft performs pattern matching on an array, calling onEmpty if empty or onNonEmpty with head and tail if not.
|
// MatchLeft performs pattern matching on an array, calling onEmpty if empty or onNonEmpty with head and tail if not.
|
||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
|
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
|
||||||
return G.MatchLeft[[]A](onEmpty, onNonEmpty)
|
return G.MatchLeft(onEmpty, onNonEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tail returns all elements except the first, wrapped in an Option.
|
// Tail returns all elements except the first, wrapped in an Option.
|
||||||
@@ -390,7 +390,7 @@ func Intersperse[A any](middle A) EM.Endomorphism[[]A] {
|
|||||||
// Intercalate inserts a separator between elements and concatenates them using a Monoid.
|
// Intercalate inserts a separator between elements and concatenates them using a Monoid.
|
||||||
func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
|
func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
|
||||||
return func(middle A) func([]A) A {
|
return func(middle A) func([]A) A {
|
||||||
return Match(m.Empty, F.Flow2(Intersperse(middle), ConcatAll[A](m)))
|
return Match(m.Empty, F.Flow2(Intersperse(middle), ConcatAll(m)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,7 +519,7 @@ func Push[A any](a A) EM.Endomorphism[[]A] {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
||||||
return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a)
|
return G.MonadFlap[func(A) B, []func(A) B, []B](fab, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flap applies a value to an array of functions, producing an array of results.
|
// Flap applies a value to an array of functions, producing an array of results.
|
||||||
@@ -527,7 +527,7 @@ func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func Flap[B, A any](a A) func([]func(A) B) []B {
|
func Flap[B, A any](a A) func([]func(A) B) []B {
|
||||||
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
|
return G.Flap[func(A) B, []func(A) B, []B](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend adds an element to the beginning of an array, returning a new array.
|
// Prepend adds an element to the beginning of an array, returning a new array.
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func TestAp(t *testing.T) {
|
|||||||
utils.Double,
|
utils.Double,
|
||||||
utils.Triple,
|
utils.Triple,
|
||||||
},
|
},
|
||||||
Ap[int, int]([]int{1, 2, 3}),
|
Ap[int]([]int{1, 2, 3}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import (
|
|||||||
func Do[S any](
|
func Do[S any](
|
||||||
empty S,
|
empty S,
|
||||||
) []S {
|
) []S {
|
||||||
return G.Do[[]S, S](empty)
|
return G.Do[[]S](empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind attaches the result of a computation to a context S1 to produce a context S2.
|
// Bind attaches the result of a computation to a context S1 to produce a context S2.
|
||||||
@@ -58,7 +58,7 @@ func Bind[S1, S2, T any](
|
|||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) []T,
|
f func(S1) []T,
|
||||||
) func([]S1) []S2 {
|
) func([]S1) []S2 {
|
||||||
return G.Bind[[]S1, []S2, []T, S1, S2, T](setter, f)
|
return G.Bind[[]S1, []S2](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let attaches the result of a pure computation to a context S1 to produce a context S2.
|
// Let attaches the result of a pure computation to a context S1 to produce a context S2.
|
||||||
@@ -80,7 +80,7 @@ func Let[S1, S2, T any](
|
|||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) func([]S1) []S2 {
|
) func([]S1) []S2 {
|
||||||
return G.Let[[]S1, []S2, S1, S2, T](setter, f)
|
return G.Let[[]S1, []S2](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LetTo attaches a constant value to a context S1 to produce a context S2.
|
// LetTo attaches a constant value to a context S1 to produce a context S2.
|
||||||
@@ -102,7 +102,7 @@ func LetTo[S1, S2, T any](
|
|||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) func([]S1) []S2 {
|
) func([]S1) []S2 {
|
||||||
return G.LetTo[[]S1, []S2, S1, S2, T](setter, b)
|
return G.LetTo[[]S1, []S2](setter, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindTo initializes a new state S1 from a value T.
|
// BindTo initializes a new state S1 from a value T.
|
||||||
@@ -121,7 +121,7 @@ func LetTo[S1, S2, T any](
|
|||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func([]T) []S1 {
|
) func([]T) []S1 {
|
||||||
return G.BindTo[[]S1, []T, S1, T](setter)
|
return G.BindTo[[]S1, []T](setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApS attaches a value to a context S1 to produce a context S2 by considering
|
// ApS attaches a value to a context S1 to produce a context S2 by considering
|
||||||
@@ -144,5 +144,5 @@ func ApS[S1, S2, T any](
|
|||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa []T,
|
fa []T,
|
||||||
) func([]S1) []S2 {
|
) func([]S1) []S2 {
|
||||||
return G.ApS[[]S1, []S2, []T, S1, S2, T](setter, fa)
|
return G.ApS[[]S1, []S2](setter, fa)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,6 @@ func Example_sort() {
|
|||||||
// [abc klm zyx]
|
// [abc klm zyx]
|
||||||
// [zyx klm abc]
|
// [zyx klm abc]
|
||||||
// [None[int] Some[int](42) Some[int](1337)]
|
// [None[int] Some[int](42) Some[int](1337)]
|
||||||
// [{c {false 0}} {b {true 10}} {d {true 10}} {a {true 30}}]
|
// [{c {0 false}} {b {10 true}} {d {10 true}} {a {30 true}}]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,25 +31,25 @@ func Of[GA ~[]A, A any](value A) GA {
|
|||||||
|
|
||||||
func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B {
|
func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B {
|
||||||
return func(as GA) B {
|
return func(as GA) B {
|
||||||
return MonadReduce[GA](as, f, initial)
|
return MonadReduce(as, f, initial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReduceWithIndex[GA ~[]A, A, B any](f func(int, B, A) B, initial B) func(GA) B {
|
func ReduceWithIndex[GA ~[]A, A, B any](f func(int, B, A) B, initial B) func(GA) B {
|
||||||
return func(as GA) B {
|
return func(as GA) B {
|
||||||
return MonadReduceWithIndex[GA](as, f, initial)
|
return MonadReduceWithIndex(as, f, initial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReduceRight[GA ~[]A, A, B any](f func(A, B) B, initial B) func(GA) B {
|
func ReduceRight[GA ~[]A, A, B any](f func(A, B) B, initial B) func(GA) B {
|
||||||
return func(as GA) B {
|
return func(as GA) B {
|
||||||
return MonadReduceRight[GA](as, f, initial)
|
return MonadReduceRight(as, f, initial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReduceRightWithIndex[GA ~[]A, A, B any](f func(int, A, B) B, initial B) func(GA) B {
|
func ReduceRightWithIndex[GA ~[]A, A, B any](f func(int, A, B) B, initial B) func(GA) B {
|
||||||
return func(as GA) B {
|
return func(as GA) B {
|
||||||
return MonadReduceRightWithIndex[GA](as, f, initial)
|
return MonadReduceRightWithIndex(as, f, initial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,19 +22,19 @@ import (
|
|||||||
type arrayMonad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B] struct{}
|
type arrayMonad[A, B any, GA ~[]A, GB ~[]B, GAB ~[]func(A) B] struct{}
|
||||||
|
|
||||||
func (o *arrayMonad[A, B, GA, GB, GAB]) Of(a A) GA {
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Of(a A) GA {
|
||||||
return Of[GA, A](a)
|
return Of[GA](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *arrayMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB {
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Map(f func(A) B) func(GA) GB {
|
||||||
return Map[GA, GB, A, B](f)
|
return Map[GA, GB](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *arrayMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB {
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Chain(f func(A) GB) func(GA) GB {
|
||||||
return Chain[GA, GB, A, B](f)
|
return Chain[GA](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *arrayMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB {
|
func (o *arrayMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB {
|
||||||
return Ap[GB, GAB, GA, B, A](fa)
|
return Ap[GB, GAB](fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monad implements the monadic operations for an array
|
// Monad implements the monadic operations for an array
|
||||||
|
|||||||
@@ -97,11 +97,11 @@ func Flatten[A any](mma NonEmptyArray[NonEmptyArray[A]]) NonEmptyArray[A] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MonadChain[A, B any](fa NonEmptyArray[A], f func(a A) NonEmptyArray[B]) NonEmptyArray[B] {
|
func MonadChain[A, B any](fa NonEmptyArray[A], f func(a A) NonEmptyArray[B]) NonEmptyArray[B] {
|
||||||
return G.MonadChain[NonEmptyArray[A], NonEmptyArray[B]](fa, f)
|
return G.MonadChain(fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Chain[A, B any](f func(A) NonEmptyArray[B]) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
func Chain[A, B any](f func(A) NonEmptyArray[B]) func(NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
return G.Chain[NonEmptyArray[A], NonEmptyArray[B]](f)
|
return G.Chain[NonEmptyArray[A]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadAp[B, A any](fab NonEmptyArray[func(A) B], fa NonEmptyArray[A]) NonEmptyArray[B] {
|
func MonadAp[B, A any](fab NonEmptyArray[func(A) B], fa NonEmptyArray[A]) NonEmptyArray[B] {
|
||||||
|
|||||||
@@ -94,5 +94,5 @@ func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func SortBy[T any](ord []O.Ord[T]) func(ma []T) []T {
|
func SortBy[T any](ord []O.Ord[T]) func(ma []T) []T {
|
||||||
return G.SortBy[[]T, []O.Ord[T]](ord)
|
return G.SortBy[[]T](ord)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func StrictUniq[A comparable](as []A) []A {
|
func StrictUniq[A comparable](as []A) []A {
|
||||||
return G.StrictUniq[[]A](as)
|
return G.StrictUniq(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uniq converts an array of arbitrary items into an array of unique items
|
// Uniq converts an array of arbitrary items into an array of unique items
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import (
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func ZipWith[FCT ~func(A, B) C, A, B, C any](fa []A, fb []B, f FCT) []C {
|
func ZipWith[FCT ~func(A, B) C, A, B, C any](fa []A, fb []B, f FCT) []C {
|
||||||
return G.ZipWith[[]A, []B, []C, FCT](fa, fb, f)
|
return G.ZipWith[[]A, []B, []C](fa, fb, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zip takes two arrays and returns an array of corresponding pairs (tuples).
|
// Zip takes two arrays and returns an array of corresponding pairs (tuples).
|
||||||
@@ -79,5 +79,5 @@ func Zip[A, B any](fb []B) func([]A) []T.Tuple2[A, B] {
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func Unzip[A, B any](cs []T.Tuple2[A, B]) T.Tuple2[[]A, []B] {
|
func Unzip[A, B any](cs []T.Tuple2[A, B]) T.Tuple2[[]A, []B] {
|
||||||
return G.Unzip[[]A, []B, []T.Tuple2[A, B]](cs)
|
return G.Unzip[[]A, []B](cs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
"github.com/IBM/fp-go/v2/eq"
|
||||||
EQ "github.com/IBM/fp-go/v2/eq"
|
"github.com/IBM/fp-go/v2/result"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,82 +28,82 @@ var (
|
|||||||
errTest = fmt.Errorf("test failure")
|
errTest = fmt.Errorf("test failure")
|
||||||
|
|
||||||
// Eq is the equal predicate checking if objects are equal
|
// Eq is the equal predicate checking if objects are equal
|
||||||
Eq = EQ.FromEquals(assert.ObjectsAreEqual)
|
Eq = eq.FromEquals(assert.ObjectsAreEqual)
|
||||||
)
|
)
|
||||||
|
|
||||||
func wrap1[T any](wrapped func(t assert.TestingT, expected, actual any, msgAndArgs ...any) bool, t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
func wrap1[T any](wrapped func(t assert.TestingT, expected, actual any, msgAndArgs ...any) bool, t *testing.T, expected T) result.Kleisli[T, T] {
|
||||||
return func(actual T) E.Either[error, T] {
|
return func(actual T) Result[T] {
|
||||||
ok := wrapped(t, expected, actual)
|
ok := wrapped(t, expected, actual)
|
||||||
if ok {
|
if ok {
|
||||||
return E.Of[error](actual)
|
return result.Of(actual)
|
||||||
}
|
}
|
||||||
return E.Left[T](errTest)
|
return result.Left[T](errTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotEqual tests if the expected and the actual values are not equal
|
// NotEqual tests if the expected and the actual values are not equal
|
||||||
func NotEqual[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
func NotEqual[T any](t *testing.T, expected T) result.Kleisli[T, T] {
|
||||||
return wrap1(assert.NotEqual, t, expected)
|
return wrap1(assert.NotEqual, t, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests if the expected and the actual values are equal
|
// Equal tests if the expected and the actual values are equal
|
||||||
func Equal[T any](t *testing.T, expected T) func(actual T) E.Either[error, T] {
|
func Equal[T any](t *testing.T, expected T) result.Kleisli[T, T] {
|
||||||
return wrap1(assert.Equal, t, expected)
|
return wrap1(assert.Equal, t, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length tests if an array has the expected length
|
// Length tests if an array has the expected length
|
||||||
func Length[T any](t *testing.T, expected int) func(actual []T) E.Either[error, []T] {
|
func Length[T any](t *testing.T, expected int) result.Kleisli[[]T, []T] {
|
||||||
return func(actual []T) E.Either[error, []T] {
|
return func(actual []T) Result[[]T] {
|
||||||
ok := assert.Len(t, actual, expected)
|
ok := assert.Len(t, actual, expected)
|
||||||
if ok {
|
if ok {
|
||||||
return E.Of[error](actual)
|
return result.Of(actual)
|
||||||
}
|
}
|
||||||
return E.Left[[]T](errTest)
|
return result.Left[[]T](errTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoError validates that there is no error
|
// NoError validates that there is no error
|
||||||
func NoError[T any](t *testing.T) func(actual E.Either[error, T]) E.Either[error, T] {
|
func NoError[T any](t *testing.T) result.Operator[T, T] {
|
||||||
return func(actual E.Either[error, T]) E.Either[error, T] {
|
return func(actual Result[T]) Result[T] {
|
||||||
return E.MonadFold(actual, func(e error) E.Either[error, T] {
|
return result.MonadFold(actual, func(e error) Result[T] {
|
||||||
assert.NoError(t, e)
|
assert.NoError(t, e)
|
||||||
return E.Left[T](e)
|
return result.Left[T](e)
|
||||||
}, func(value T) E.Either[error, T] {
|
}, func(value T) Result[T] {
|
||||||
assert.NoError(t, nil)
|
assert.NoError(t, nil)
|
||||||
return E.Right[error](value)
|
return result.Of(value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArrayContains tests if a value is contained in an array
|
// ArrayContains tests if a value is contained in an array
|
||||||
func ArrayContains[T any](t *testing.T, expected T) func(actual []T) E.Either[error, []T] {
|
func ArrayContains[T any](t *testing.T, expected T) result.Kleisli[[]T, []T] {
|
||||||
return func(actual []T) E.Either[error, []T] {
|
return func(actual []T) Result[[]T] {
|
||||||
ok := assert.Contains(t, actual, expected)
|
ok := assert.Contains(t, actual, expected)
|
||||||
if ok {
|
if ok {
|
||||||
return E.Of[error](actual)
|
return result.Of(actual)
|
||||||
}
|
}
|
||||||
return E.Left[[]T](errTest)
|
return result.Left[[]T](errTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainsKey tests if a key is contained in a map
|
// ContainsKey tests if a key is contained in a map
|
||||||
func ContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
func ContainsKey[T any, K comparable](t *testing.T, expected K) result.Kleisli[map[K]T, map[K]T] {
|
||||||
return func(actual map[K]T) E.Either[error, map[K]T] {
|
return func(actual map[K]T) Result[map[K]T] {
|
||||||
ok := assert.Contains(t, actual, expected)
|
ok := assert.Contains(t, actual, expected)
|
||||||
if ok {
|
if ok {
|
||||||
return E.Of[error](actual)
|
return result.Of(actual)
|
||||||
}
|
}
|
||||||
return E.Left[map[K]T](errTest)
|
return result.Left[map[K]T](errTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotContainsKey tests if a key is not contained in a map
|
// NotContainsKey tests if a key is not contained in a map
|
||||||
func NotContainsKey[T any, K comparable](t *testing.T, expected K) func(actual map[K]T) E.Either[error, map[K]T] {
|
func NotContainsKey[T any, K comparable](t *testing.T, expected K) result.Kleisli[map[K]T, map[K]T] {
|
||||||
return func(actual map[K]T) E.Either[error, map[K]T] {
|
return func(actual map[K]T) Result[map[K]T] {
|
||||||
ok := assert.NotContains(t, actual, expected)
|
ok := assert.NotContains(t, actual, expected)
|
||||||
if ok {
|
if ok {
|
||||||
return E.Of[error](actual)
|
return result.Of(actual)
|
||||||
}
|
}
|
||||||
return E.Left[map[K]T](errTest)
|
return result.Left[map[K]T](errTest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
v2/assert/types.go
Normal file
7
v2/assert/types.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package assert
|
||||||
|
|
||||||
|
import "github.com/IBM/fp-go/v2/result"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Result[T any] = result.Result[T]
|
||||||
|
)
|
||||||
@@ -53,7 +53,7 @@ func MakeBounded[T any](o ord.Ord[T], t, b T) Bounded[T] {
|
|||||||
|
|
||||||
// Clamp returns a function that clamps against the bounds defined in the bounded type
|
// Clamp returns a function that clamps against the bounds defined in the bounded type
|
||||||
func Clamp[T any](b Bounded[T]) func(T) T {
|
func Clamp[T any](b Bounded[T]) func(T) T {
|
||||||
return ord.Clamp[T](b)(b.Bottom(), b.Top())
|
return ord.Clamp(b)(b.Bottom(), b.Top())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse reverses the ordering and swaps the bounds
|
// Reverse reverses the ordering and swaps the bounds
|
||||||
|
|||||||
7
v2/builder/builder.go
Normal file
7
v2/builder/builder.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
type (
|
||||||
|
Builder[T any] interface {
|
||||||
|
Build() Result[T]
|
||||||
|
}
|
||||||
|
)
|
||||||
12
v2/builder/prism.go
Normal file
12
v2/builder/prism.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/optics/prism"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuilderPrism createa a [Prism] that converts between a builder and its type
|
||||||
|
func BuilderPrism[T any, B Builder[T]](creator func(T) B) Prism[B, T] {
|
||||||
|
return prism.MakePrism(F.Flow2(B.Build, result.ToOption[T]), creator)
|
||||||
|
}
|
||||||
15
v2/builder/types.go
Normal file
15
v2/builder/types.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/optics/prism"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Result[T any] = result.Result[T]
|
||||||
|
|
||||||
|
Prism[S, A any] = prism.Prism[S, A]
|
||||||
|
|
||||||
|
Option[T any] = option.Option[T]
|
||||||
|
)
|
||||||
@@ -15,14 +15,163 @@
|
|||||||
|
|
||||||
package bytes
|
package bytes
|
||||||
|
|
||||||
|
// Empty returns an empty byte slice.
|
||||||
|
//
|
||||||
|
// This function returns the identity element for the byte slice Monoid,
|
||||||
|
// which is an empty byte slice. It's useful as a starting point for
|
||||||
|
// building byte slices or as a default value.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - An empty byte slice ([]byte{})
|
||||||
|
//
|
||||||
|
// Properties:
|
||||||
|
// - Empty() is the identity element for Monoid.Concat
|
||||||
|
// - Monoid.Concat(Empty(), x) == x
|
||||||
|
// - Monoid.Concat(x, Empty()) == x
|
||||||
|
//
|
||||||
|
// Example - Basic usage:
|
||||||
|
//
|
||||||
|
// empty := Empty()
|
||||||
|
// fmt.Println(len(empty)) // 0
|
||||||
|
//
|
||||||
|
// Example - As identity element:
|
||||||
|
//
|
||||||
|
// data := []byte("hello")
|
||||||
|
// result1 := Monoid.Concat(Empty(), data) // []byte("hello")
|
||||||
|
// result2 := Monoid.Concat(data, Empty()) // []byte("hello")
|
||||||
|
//
|
||||||
|
// Example - Building byte slices:
|
||||||
|
//
|
||||||
|
// // Start with empty and build up
|
||||||
|
// buffer := Empty()
|
||||||
|
// buffer = Monoid.Concat(buffer, []byte("Hello"))
|
||||||
|
// buffer = Monoid.Concat(buffer, []byte(" "))
|
||||||
|
// buffer = Monoid.Concat(buffer, []byte("World"))
|
||||||
|
// // buffer: []byte("Hello World")
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - Monoid.Empty(): Alternative way to get empty byte slice
|
||||||
|
// - ConcatAll(): For concatenating multiple byte slices
|
||||||
func Empty() []byte {
|
func Empty() []byte {
|
||||||
return Monoid.Empty()
|
return Monoid.Empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToString converts a byte slice to a string.
|
||||||
|
//
|
||||||
|
// This function performs a direct conversion from []byte to string.
|
||||||
|
// The conversion creates a new string with a copy of the byte data.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - a: The byte slice to convert
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A string containing the same data as the byte slice
|
||||||
|
//
|
||||||
|
// Performance Note:
|
||||||
|
//
|
||||||
|
// This conversion allocates a new string. For performance-critical code
|
||||||
|
// that needs to avoid allocations, consider using unsafe.String (Go 1.20+)
|
||||||
|
// or working directly with byte slices.
|
||||||
|
//
|
||||||
|
// Example - Basic conversion:
|
||||||
|
//
|
||||||
|
// bytes := []byte("hello")
|
||||||
|
// str := ToString(bytes)
|
||||||
|
// fmt.Println(str) // "hello"
|
||||||
|
//
|
||||||
|
// Example - Converting binary data:
|
||||||
|
//
|
||||||
|
// // ASCII codes for "Hello"
|
||||||
|
// data := []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f}
|
||||||
|
// str := ToString(data)
|
||||||
|
// fmt.Println(str) // "Hello"
|
||||||
|
//
|
||||||
|
// Example - Empty byte slice:
|
||||||
|
//
|
||||||
|
// empty := Empty()
|
||||||
|
// str := ToString(empty)
|
||||||
|
// fmt.Println(str == "") // true
|
||||||
|
//
|
||||||
|
// Example - UTF-8 encoded text:
|
||||||
|
//
|
||||||
|
// utf8Bytes := []byte("Hello, 世界")
|
||||||
|
// str := ToString(utf8Bytes)
|
||||||
|
// fmt.Println(str) // "Hello, 世界"
|
||||||
|
//
|
||||||
|
// Example - Round-trip conversion:
|
||||||
|
//
|
||||||
|
// original := "test string"
|
||||||
|
// bytes := []byte(original)
|
||||||
|
// result := ToString(bytes)
|
||||||
|
// fmt.Println(original == result) // true
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - []byte(string): For converting string to byte slice
|
||||||
|
// - Size(): For getting the length of a byte slice
|
||||||
func ToString(a []byte) string {
|
func ToString(a []byte) string {
|
||||||
return string(a)
|
return string(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the number of bytes in a byte slice.
|
||||||
|
//
|
||||||
|
// This function returns the length of the byte slice, which is the number
|
||||||
|
// of bytes it contains. This is equivalent to len(as) but provided as a
|
||||||
|
// named function for use in functional composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - as: The byte slice to measure
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - The number of bytes in the slice
|
||||||
|
//
|
||||||
|
// Example - Basic usage:
|
||||||
|
//
|
||||||
|
// data := []byte("hello")
|
||||||
|
// size := Size(data)
|
||||||
|
// fmt.Println(size) // 5
|
||||||
|
//
|
||||||
|
// Example - Empty slice:
|
||||||
|
//
|
||||||
|
// empty := Empty()
|
||||||
|
// size := Size(empty)
|
||||||
|
// fmt.Println(size) // 0
|
||||||
|
//
|
||||||
|
// Example - Binary data:
|
||||||
|
//
|
||||||
|
// binary := []byte{0x01, 0x02, 0x03, 0x04}
|
||||||
|
// size := Size(binary)
|
||||||
|
// fmt.Println(size) // 4
|
||||||
|
//
|
||||||
|
// Example - UTF-8 encoded text:
|
||||||
|
//
|
||||||
|
// // Note: Size returns byte count, not character count
|
||||||
|
// utf8 := []byte("Hello, 世界")
|
||||||
|
// byteCount := Size(utf8)
|
||||||
|
// fmt.Println(byteCount) // 13 (not 9 characters)
|
||||||
|
//
|
||||||
|
// Example - Using in functional composition:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/array"
|
||||||
|
//
|
||||||
|
// slices := [][]byte{
|
||||||
|
// []byte("a"),
|
||||||
|
// []byte("bb"),
|
||||||
|
// []byte("ccc"),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Map to get sizes
|
||||||
|
// sizes := array.Map(Size)(slices)
|
||||||
|
// // sizes: []int{1, 2, 3}
|
||||||
|
//
|
||||||
|
// Example - Checking if slice is empty:
|
||||||
|
//
|
||||||
|
// data := []byte("test")
|
||||||
|
// isEmpty := Size(data) == 0
|
||||||
|
// fmt.Println(isEmpty) // false
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - len(): Built-in function for getting slice length
|
||||||
|
// - ToString(): For converting byte slice to string
|
||||||
func Size(as []byte) int {
|
func Size(as []byte) int {
|
||||||
return len(as)
|
return len(as)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,6 +187,299 @@ func TestOrd(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestOrdProperties tests mathematical properties of Ord
|
||||||
|
func TestOrdProperties(t *testing.T) {
|
||||||
|
t.Run("reflexivity: x == x", func(t *testing.T) {
|
||||||
|
testCases := [][]byte{
|
||||||
|
[]byte{},
|
||||||
|
[]byte("a"),
|
||||||
|
[]byte("test"),
|
||||||
|
[]byte{0x01, 0x02, 0x03},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
assert.Equal(t, 0, Ord.Compare(tc, tc),
|
||||||
|
"Compare(%v, %v) should be 0", tc, tc)
|
||||||
|
assert.True(t, Ord.Equals(tc, tc),
|
||||||
|
"Equals(%v, %v) should be true", tc, tc)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("antisymmetry: if x <= y and y <= x then x == y", func(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
a, b []byte
|
||||||
|
}{
|
||||||
|
{[]byte("abc"), []byte("abc")},
|
||||||
|
{[]byte{}, []byte{}},
|
||||||
|
{[]byte{0x01}, []byte{0x01}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
cmp1 := Ord.Compare(tc.a, tc.b)
|
||||||
|
cmp2 := Ord.Compare(tc.b, tc.a)
|
||||||
|
|
||||||
|
if cmp1 <= 0 && cmp2 <= 0 {
|
||||||
|
assert.True(t, Ord.Equals(tc.a, tc.b),
|
||||||
|
"If %v <= %v and %v <= %v, they should be equal", tc.a, tc.b, tc.b, tc.a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("transitivity: if x <= y and y <= z then x <= z", func(t *testing.T) {
|
||||||
|
x := []byte("a")
|
||||||
|
y := []byte("b")
|
||||||
|
z := []byte("c")
|
||||||
|
|
||||||
|
cmpXY := Ord.Compare(x, y)
|
||||||
|
cmpYZ := Ord.Compare(y, z)
|
||||||
|
cmpXZ := Ord.Compare(x, z)
|
||||||
|
|
||||||
|
if cmpXY <= 0 && cmpYZ <= 0 {
|
||||||
|
assert.True(t, cmpXZ <= 0,
|
||||||
|
"If %v <= %v and %v <= %v, then %v <= %v", x, y, y, z, x, z)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("totality: either x <= y or y <= x", func(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
a, b []byte
|
||||||
|
}{
|
||||||
|
{[]byte("abc"), []byte("abd")},
|
||||||
|
{[]byte("xyz"), []byte("abc")},
|
||||||
|
{[]byte{}, []byte("a")},
|
||||||
|
{[]byte{0x01}, []byte{0x02}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
cmp1 := Ord.Compare(tc.a, tc.b)
|
||||||
|
cmp2 := Ord.Compare(tc.b, tc.a)
|
||||||
|
|
||||||
|
assert.True(t, cmp1 <= 0 || cmp2 <= 0,
|
||||||
|
"Either %v <= %v or %v <= %v must be true", tc.a, tc.b, tc.b, tc.a)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEdgeCases tests edge cases and boundary conditions
|
||||||
|
func TestEdgeCases(t *testing.T) {
|
||||||
|
t.Run("very large byte slices", func(t *testing.T) {
|
||||||
|
large := make([]byte, 1000000)
|
||||||
|
for i := range large {
|
||||||
|
large[i] = byte(i % 256)
|
||||||
|
}
|
||||||
|
|
||||||
|
size := Size(large)
|
||||||
|
assert.Equal(t, 1000000, size)
|
||||||
|
|
||||||
|
str := ToString(large)
|
||||||
|
assert.Equal(t, 1000000, len(str))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("concatenating many slices", func(t *testing.T) {
|
||||||
|
slices := make([][]byte, 100)
|
||||||
|
for i := range slices {
|
||||||
|
slices[i] = []byte{byte(i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := ConcatAll(slices...)
|
||||||
|
assert.Equal(t, 100, Size(result))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("null bytes in slice", func(t *testing.T) {
|
||||||
|
data := []byte{0x00, 0x01, 0x00, 0x02}
|
||||||
|
size := Size(data)
|
||||||
|
assert.Equal(t, 4, size)
|
||||||
|
|
||||||
|
str := ToString(data)
|
||||||
|
assert.Equal(t, 4, len(str))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("comparing slices with null bytes", func(t *testing.T) {
|
||||||
|
a := []byte{0x00, 0x01}
|
||||||
|
b := []byte{0x00, 0x02}
|
||||||
|
assert.Equal(t, -1, Ord.Compare(a, b))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMonoidConcatPerformance tests concatenation performance characteristics
|
||||||
|
func TestMonoidConcatPerformance(t *testing.T) {
|
||||||
|
t.Run("ConcatAll vs repeated Concat", func(t *testing.T) {
|
||||||
|
slices := [][]byte{
|
||||||
|
[]byte("a"),
|
||||||
|
[]byte("b"),
|
||||||
|
[]byte("c"),
|
||||||
|
[]byte("d"),
|
||||||
|
[]byte("e"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using ConcatAll
|
||||||
|
result1 := ConcatAll(slices...)
|
||||||
|
|
||||||
|
// Using repeated Concat
|
||||||
|
result2 := Monoid.Empty()
|
||||||
|
for _, s := range slices {
|
||||||
|
result2 = Monoid.Concat(result2, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, result1, result2)
|
||||||
|
assert.Equal(t, []byte("abcde"), result1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRoundTrip tests round-trip conversions
|
||||||
|
func TestRoundTrip(t *testing.T) {
|
||||||
|
t.Run("string to bytes to string", func(t *testing.T) {
|
||||||
|
original := "Hello, World! 世界"
|
||||||
|
bytes := []byte(original)
|
||||||
|
result := ToString(bytes)
|
||||||
|
assert.Equal(t, original, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bytes to string to bytes", func(t *testing.T) {
|
||||||
|
original := []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f}
|
||||||
|
str := ToString(original)
|
||||||
|
result := []byte(str)
|
||||||
|
assert.Equal(t, original, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConcatAllVariadic tests ConcatAll with various argument counts
|
||||||
|
func TestConcatAllVariadic(t *testing.T) {
|
||||||
|
t.Run("zero arguments", func(t *testing.T) {
|
||||||
|
result := ConcatAll()
|
||||||
|
assert.Equal(t, []byte{}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("one argument", func(t *testing.T) {
|
||||||
|
result := ConcatAll([]byte("test"))
|
||||||
|
assert.Equal(t, []byte("test"), result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("two arguments", func(t *testing.T) {
|
||||||
|
result := ConcatAll([]byte("hello"), []byte("world"))
|
||||||
|
assert.Equal(t, []byte("helloworld"), result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("many arguments", func(t *testing.T) {
|
||||||
|
result := ConcatAll(
|
||||||
|
[]byte("a"),
|
||||||
|
[]byte("b"),
|
||||||
|
[]byte("c"),
|
||||||
|
[]byte("d"),
|
||||||
|
[]byte("e"),
|
||||||
|
[]byte("f"),
|
||||||
|
[]byte("g"),
|
||||||
|
[]byte("h"),
|
||||||
|
[]byte("i"),
|
||||||
|
[]byte("j"),
|
||||||
|
)
|
||||||
|
assert.Equal(t, []byte("abcdefghij"), result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark tests
|
||||||
|
func BenchmarkToString(b *testing.B) {
|
||||||
|
data := []byte("Hello, World!")
|
||||||
|
|
||||||
|
b.Run("small", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = ToString(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("large", func(b *testing.B) {
|
||||||
|
large := make([]byte, 10000)
|
||||||
|
for i := range large {
|
||||||
|
large[i] = byte(i % 256)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = ToString(large)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSize(b *testing.B) {
|
||||||
|
data := []byte("Hello, World!")
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = Size(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMonoidConcat(b *testing.B) {
|
||||||
|
a := []byte("Hello")
|
||||||
|
c := []byte(" World")
|
||||||
|
|
||||||
|
b.Run("small slices", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = Monoid.Concat(a, c)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("large slices", func(b *testing.B) {
|
||||||
|
large1 := make([]byte, 10000)
|
||||||
|
large2 := make([]byte, 10000)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = Monoid.Concat(large1, large2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkConcatAll(b *testing.B) {
|
||||||
|
slices := [][]byte{
|
||||||
|
[]byte("Hello"),
|
||||||
|
[]byte(" "),
|
||||||
|
[]byte("World"),
|
||||||
|
[]byte("!"),
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Run("few slices", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = ConcatAll(slices...)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("many slices", func(b *testing.B) {
|
||||||
|
many := make([][]byte, 100)
|
||||||
|
for i := range many {
|
||||||
|
many[i] = []byte{byte(i)}
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = ConcatAll(many...)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkOrdCompare(b *testing.B) {
|
||||||
|
a := []byte("abc")
|
||||||
|
c := []byte("abd")
|
||||||
|
|
||||||
|
b.Run("equal", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = Ord.Compare(a, a)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("different", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = Ord.Compare(a, c)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("large slices", func(b *testing.B) {
|
||||||
|
large1 := make([]byte, 10000)
|
||||||
|
large2 := make([]byte, 10000)
|
||||||
|
large2[9999] = 1
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = Ord.Compare(large1, large2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Example tests
|
// Example tests
|
||||||
func ExampleEmpty() {
|
func ExampleEmpty() {
|
||||||
empty := Empty()
|
empty := Empty()
|
||||||
@@ -219,3 +512,17 @@ func ExampleConcatAll() {
|
|||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleMonoid_concat() {
|
||||||
|
result := Monoid.Concat([]byte("Hello"), []byte(" World"))
|
||||||
|
println(string(result)) // Hello World
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleOrd_compare() {
|
||||||
|
cmp := Ord.Compare([]byte("abc"), []byte("abd"))
|
||||||
|
println(cmp) // -1 (abc < abd)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
}
|
||||||
|
|||||||
4
v2/bytes/coverage.out
Normal file
4
v2/bytes/coverage.out
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
mode: set
|
||||||
|
github.com/IBM/fp-go/v2/bytes/bytes.go:55.21,57.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/bytes/bytes.go:111.32,113.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/bytes/bytes.go:175.26,177.2 1 1
|
||||||
@@ -23,12 +23,219 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// monoid for byte arrays
|
// Monoid is the Monoid instance for byte slices.
|
||||||
|
//
|
||||||
|
// This Monoid combines byte slices through concatenation, with an empty
|
||||||
|
// byte slice as the identity element. It satisfies the monoid laws:
|
||||||
|
//
|
||||||
|
// Identity laws:
|
||||||
|
// - Monoid.Concat(Monoid.Empty(), x) == x (left identity)
|
||||||
|
// - Monoid.Concat(x, Monoid.Empty()) == x (right identity)
|
||||||
|
//
|
||||||
|
// Associativity law:
|
||||||
|
// - Monoid.Concat(Monoid.Concat(a, b), c) == Monoid.Concat(a, Monoid.Concat(b, c))
|
||||||
|
//
|
||||||
|
// Operations:
|
||||||
|
// - Empty(): Returns an empty byte slice []byte{}
|
||||||
|
// - Concat(a, b []byte): Concatenates two byte slices
|
||||||
|
//
|
||||||
|
// Example - Basic concatenation:
|
||||||
|
//
|
||||||
|
// result := Monoid.Concat([]byte("Hello"), []byte(" World"))
|
||||||
|
// // result: []byte("Hello World")
|
||||||
|
//
|
||||||
|
// Example - Identity element:
|
||||||
|
//
|
||||||
|
// empty := Monoid.Empty()
|
||||||
|
// data := []byte("test")
|
||||||
|
// result1 := Monoid.Concat(empty, data) // []byte("test")
|
||||||
|
// result2 := Monoid.Concat(data, empty) // []byte("test")
|
||||||
|
//
|
||||||
|
// Example - Building byte buffers:
|
||||||
|
//
|
||||||
|
// buffer := Monoid.Empty()
|
||||||
|
// buffer = Monoid.Concat(buffer, []byte("Line 1\n"))
|
||||||
|
// buffer = Monoid.Concat(buffer, []byte("Line 2\n"))
|
||||||
|
// buffer = Monoid.Concat(buffer, []byte("Line 3\n"))
|
||||||
|
//
|
||||||
|
// Example - Associativity:
|
||||||
|
//
|
||||||
|
// a := []byte("a")
|
||||||
|
// b := []byte("b")
|
||||||
|
// c := []byte("c")
|
||||||
|
// left := Monoid.Concat(Monoid.Concat(a, b), c) // []byte("abc")
|
||||||
|
// right := Monoid.Concat(a, Monoid.Concat(b, c)) // []byte("abc")
|
||||||
|
// // left == right
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - ConcatAll: For concatenating multiple byte slices at once
|
||||||
|
// - Empty(): Convenience function for getting empty byte slice
|
||||||
Monoid = A.Monoid[byte]()
|
Monoid = A.Monoid[byte]()
|
||||||
|
|
||||||
// ConcatAll concatenates all bytes
|
// ConcatAll efficiently concatenates multiple byte slices into a single slice.
|
||||||
|
//
|
||||||
|
// This function takes a variadic number of byte slices and combines them
|
||||||
|
// into a single byte slice. It pre-allocates the exact amount of memory
|
||||||
|
// needed, making it more efficient than repeated concatenation.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - slices: Zero or more byte slices to concatenate
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A new byte slice containing all input slices concatenated in order
|
||||||
|
//
|
||||||
|
// Performance:
|
||||||
|
//
|
||||||
|
// ConcatAll is more efficient than using Monoid.Concat repeatedly because
|
||||||
|
// it calculates the total size upfront and allocates memory once, avoiding
|
||||||
|
// multiple allocations and copies.
|
||||||
|
//
|
||||||
|
// Example - Basic usage:
|
||||||
|
//
|
||||||
|
// result := ConcatAll(
|
||||||
|
// []byte("Hello"),
|
||||||
|
// []byte(" "),
|
||||||
|
// []byte("World"),
|
||||||
|
// )
|
||||||
|
// // result: []byte("Hello World")
|
||||||
|
//
|
||||||
|
// Example - Empty input:
|
||||||
|
//
|
||||||
|
// result := ConcatAll()
|
||||||
|
// // result: []byte{}
|
||||||
|
//
|
||||||
|
// Example - Single slice:
|
||||||
|
//
|
||||||
|
// result := ConcatAll([]byte("test"))
|
||||||
|
// // result: []byte("test")
|
||||||
|
//
|
||||||
|
// Example - Building protocol messages:
|
||||||
|
//
|
||||||
|
// import "encoding/binary"
|
||||||
|
//
|
||||||
|
// header := []byte{0x01, 0x02}
|
||||||
|
// length := make([]byte, 4)
|
||||||
|
// binary.BigEndian.PutUint32(length, 100)
|
||||||
|
// payload := []byte("data")
|
||||||
|
// footer := []byte{0xFF}
|
||||||
|
//
|
||||||
|
// message := ConcatAll(header, length, payload, footer)
|
||||||
|
//
|
||||||
|
// Example - With empty slices:
|
||||||
|
//
|
||||||
|
// result := ConcatAll(
|
||||||
|
// []byte("a"),
|
||||||
|
// []byte{},
|
||||||
|
// []byte("b"),
|
||||||
|
// []byte{},
|
||||||
|
// []byte("c"),
|
||||||
|
// )
|
||||||
|
// // result: []byte("abc")
|
||||||
|
//
|
||||||
|
// Example - Building CSV line:
|
||||||
|
//
|
||||||
|
// fields := [][]byte{
|
||||||
|
// []byte("John"),
|
||||||
|
// []byte("Doe"),
|
||||||
|
// []byte("30"),
|
||||||
|
// }
|
||||||
|
// separator := []byte(",")
|
||||||
|
//
|
||||||
|
// // Interleave fields with separators
|
||||||
|
// parts := [][]byte{
|
||||||
|
// fields[0], separator,
|
||||||
|
// fields[1], separator,
|
||||||
|
// fields[2],
|
||||||
|
// }
|
||||||
|
// line := ConcatAll(parts...)
|
||||||
|
// // line: []byte("John,Doe,30")
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - Monoid.Concat: For concatenating exactly two byte slices
|
||||||
|
// - bytes.Join: Standard library function for joining with separator
|
||||||
ConcatAll = A.ArrayConcatAll[byte]
|
ConcatAll = A.ArrayConcatAll[byte]
|
||||||
|
|
||||||
// Ord implements the default ordering on bytes
|
// Ord is the Ord instance for byte slices providing lexicographic ordering.
|
||||||
|
//
|
||||||
|
// This Ord instance compares byte slices lexicographically (dictionary order),
|
||||||
|
// comparing bytes from left to right until a difference is found or one slice
|
||||||
|
// ends. It uses the standard library's bytes.Compare and bytes.Equal functions.
|
||||||
|
//
|
||||||
|
// Comparison rules:
|
||||||
|
// - Compares byte-by-byte from left to right
|
||||||
|
// - First differing byte determines the order
|
||||||
|
// - Shorter slice is less than longer slice if all bytes match
|
||||||
|
// - Empty slice is less than any non-empty slice
|
||||||
|
//
|
||||||
|
// Operations:
|
||||||
|
// - Compare(a, b []byte) int: Returns -1 if a < b, 0 if a == b, 1 if a > b
|
||||||
|
// - Equals(a, b []byte) bool: Returns true if slices are equal
|
||||||
|
//
|
||||||
|
// Example - Basic comparison:
|
||||||
|
//
|
||||||
|
// cmp := Ord.Compare([]byte("abc"), []byte("abd"))
|
||||||
|
// // cmp: -1 (abc < abd)
|
||||||
|
//
|
||||||
|
// cmp = Ord.Compare([]byte("xyz"), []byte("abc"))
|
||||||
|
// // cmp: 1 (xyz > abc)
|
||||||
|
//
|
||||||
|
// cmp = Ord.Compare([]byte("test"), []byte("test"))
|
||||||
|
// // cmp: 0 (equal)
|
||||||
|
//
|
||||||
|
// Example - Length differences:
|
||||||
|
//
|
||||||
|
// cmp := Ord.Compare([]byte("ab"), []byte("abc"))
|
||||||
|
// // cmp: -1 (shorter is less)
|
||||||
|
//
|
||||||
|
// cmp = Ord.Compare([]byte("abc"), []byte("ab"))
|
||||||
|
// // cmp: 1 (longer is greater)
|
||||||
|
//
|
||||||
|
// Example - Empty slices:
|
||||||
|
//
|
||||||
|
// cmp := Ord.Compare([]byte{}, []byte("a"))
|
||||||
|
// // cmp: -1 (empty is less)
|
||||||
|
//
|
||||||
|
// cmp = Ord.Compare([]byte{}, []byte{})
|
||||||
|
// // cmp: 0 (both empty)
|
||||||
|
//
|
||||||
|
// Example - Equality check:
|
||||||
|
//
|
||||||
|
// equal := Ord.Equals([]byte("test"), []byte("test"))
|
||||||
|
// // equal: true
|
||||||
|
//
|
||||||
|
// equal = Ord.Equals([]byte("test"), []byte("Test"))
|
||||||
|
// // equal: false (case-sensitive)
|
||||||
|
//
|
||||||
|
// Example - Sorting byte slices:
|
||||||
|
//
|
||||||
|
// import "github.com/IBM/fp-go/v2/array"
|
||||||
|
//
|
||||||
|
// data := [][]byte{
|
||||||
|
// []byte("zebra"),
|
||||||
|
// []byte("apple"),
|
||||||
|
// []byte("mango"),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// sorted := array.Sort(Ord)(data)
|
||||||
|
// // sorted: [[]byte("apple"), []byte("mango"), []byte("zebra")]
|
||||||
|
//
|
||||||
|
// Example - Binary data comparison:
|
||||||
|
//
|
||||||
|
// cmp := Ord.Compare([]byte{0x01, 0x02}, []byte{0x01, 0x03})
|
||||||
|
// // cmp: -1 (0x02 < 0x03)
|
||||||
|
//
|
||||||
|
// Example - Finding minimum:
|
||||||
|
//
|
||||||
|
// import O "github.com/IBM/fp-go/v2/ord"
|
||||||
|
//
|
||||||
|
// a := []byte("xyz")
|
||||||
|
// b := []byte("abc")
|
||||||
|
// min := O.Min(Ord)(a, b)
|
||||||
|
// // min: []byte("abc")
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// - bytes.Compare: Standard library comparison function
|
||||||
|
// - bytes.Equal: Standard library equality function
|
||||||
|
// - array.Sort: For sorting slices using an Ord instance
|
||||||
Ord = O.MakeOrd(bytes.Compare, bytes.Equal)
|
Ord = O.MakeOrd(bytes.Compare, bytes.Equal)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
func TestMap(t *testing.T) {
|
func TestMap(t *testing.T) {
|
||||||
fa := Make[string, int]("foo")
|
fa := Make[string, int]("foo")
|
||||||
assert.Equal(t, fa, F.Pipe1(fa, Map[string, int](utils.Double)))
|
assert.Equal(t, fa, F.Pipe1(fa, Map[string](utils.Double)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOf(t *testing.T) {
|
func TestOf(t *testing.T) {
|
||||||
|
|||||||
@@ -13,20 +13,19 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package ioeither
|
package ioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/IBM/fp-go/v2/either"
|
"github.com/IBM/fp-go/v2/result"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// withContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
// withContext wraps an existing IOEither and performs a context check for cancellation before delegating
|
||||||
func WithContext[A any](ctx context.Context, ma IOE.IOEither[error, A]) IOE.IOEither[error, A] {
|
func WithContext[A any](ctx context.Context, ma IOResult[A]) IOResult[A] {
|
||||||
return func() either.Either[error, A] {
|
return func() Result[A] {
|
||||||
if err := context.Cause(ctx); err != nil {
|
if err := context.Cause(ctx); err != nil {
|
||||||
return either.Left[A](err)
|
return result.Left[A](err)
|
||||||
}
|
}
|
||||||
return ma()
|
return ma()
|
||||||
}
|
}
|
||||||
11
v2/context/ioresult/types.go
Normal file
11
v2/context/ioresult/types.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package ioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
IOResult[T any] = ioresult.IOResult[T]
|
||||||
|
Result[T any] = result.Result[T]
|
||||||
|
)
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
mode: set
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/bind.go:27.21,29.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/bind.go:35.47,42.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/bind.go:48.47,54.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/bind.go:60.47,66.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/bind.go:71.46,76.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/bind.go:82.47,89.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/bracket.go:33.21,44.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/cancel.go:35.65,36.47 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/cancel.go:36.47,37.44 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/cancel.go:37.44,39.4 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/cancel.go:40.3,40.40 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/eq.go:42.84,44.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:18.91,20.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:24.93,26.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:30.101,32.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:36.103,38.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:43.36,48.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:53.36,58.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:63.36,68.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:71.98,76.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:79.101,84.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:87.101,92.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:95.129,96.68 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:96.68,102.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:106.132,107.68 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:107.68,113.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:117.132,118.68 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:118.68,124.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:129.113,131.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:135.115,137.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:143.40,150.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:156.40,163.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:169.40,176.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:179.126,185.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:188.129,194.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:197.129,203.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:206.185,207.76 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:207.76,215.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:219.188,220.76 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:220.76,228.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:232.188,233.76 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:233.76,241.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:246.125,248.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:252.127,254.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:261.44,270.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:277.44,286.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:293.44,302.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:305.154,312.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:315.157,322.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:325.157,332.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:335.241,336.84 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:336.84,346.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:350.244,351.84 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:351.84,361.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:365.244,366.84 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:366.84,376.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:381.137,383.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:387.139,389.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:397.48,408.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:416.48,427.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:435.48,446.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:449.182,457.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:460.185,468.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:471.185,479.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:482.297,483.92 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:483.92,495.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:499.300,500.92 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:500.92,512.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:516.300,517.92 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:517.92,529.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:534.149,536.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:540.151,542.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:551.52,564.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:573.52,586.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:595.52,608.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:611.210,620.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:623.213,632.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:635.213,644.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:647.353,648.100 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:648.100,662.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:666.356,667.100 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:667.100,681.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:685.356,686.100 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:686.100,700.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:705.161,707.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:711.163,713.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:723.56,738.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:748.56,763.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:773.56,788.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:791.238,801.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:804.241,814.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:817.241,827.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:830.409,831.108 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:831.108,847.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:851.412,852.108 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:852.108,868.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:872.412,873.108 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:873.108,889.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:894.173,896.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:900.175,902.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:913.60,930.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:941.60,958.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:969.60,986.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:989.266,1000.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1003.269,1014.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1017.269,1028.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1031.465,1032.116 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1032.116,1050.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1054.468,1055.116 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1055.116,1073.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1077.468,1078.116 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1078.116,1096.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1101.185,1103.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1107.187,1109.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1121.64,1140.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1152.64,1171.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1183.64,1202.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1205.294,1217.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1220.297,1232.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1235.297,1247.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1250.521,1251.124 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1251.124,1271.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1275.524,1276.124 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1276.124,1296.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1300.524,1301.124 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1301.124,1321.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1326.197,1328.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1332.199,1334.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1347.68,1368.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1381.68,1402.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1415.68,1436.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1439.322,1452.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1455.325,1468.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1471.325,1484.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1487.577,1488.132 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1488.132,1510.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1514.580,1515.132 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1515.132,1537.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1541.580,1542.132 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1542.132,1564.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1569.210,1571.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1575.212,1577.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1591.74,1614.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1628.74,1651.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1665.74,1688.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1691.356,1705.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1708.359,1722.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1725.359,1739.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1742.645,1743.144 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1743.144,1767.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1771.648,1772.144 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1772.144,1796.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1800.648,1801.144 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/gen.go:1801.144,1825.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/monoid.go:36.61,43.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/monoid.go:52.64,59.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/monoid.go:68.64,75.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/monoid.go:85.61,93.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/monoid.go:103.63,108.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:42.55,44.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:52.45,54.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:62.42,64.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:74.78,76.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:85.75,87.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:97.72,99.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:108.69,110.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:120.96,122.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:131.93,133.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:143.101,145.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:154.71,156.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:165.39,167.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:169.93,173.56 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:173.56,174.32 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:174.32,174.47 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:189.98,194.47 3 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:194.47,196.44 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:196.44,198.4 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:200.3,200.27 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:200.27,202.45 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:202.45,204.5 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:207.4,213.47 5 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:227.95,229.17 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:229.17,231.3 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:232.2,232.28 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:243.98,245.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:254.91,256.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:265.94,267.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:276.94,278.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:288.95,290.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:299.73,301.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:307.44,309.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:319.95,321.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:330.95,332.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:342.100,344.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:353.100,355.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:364.116,366.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:375.75,377.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:386.47,388.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:398.51,400.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:406.39,407.47 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:407.47,408.27 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:408.27,411.4 2 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:423.87,425.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:434.87,436.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:446.92,448.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:457.92,459.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:468.115,470.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:479.85,480.54 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:480.54,481.48 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:481.48,482.28 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:482.28,487.12 3 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:488.30,489.22 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:490.23,491.47 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:505.59,511.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:520.66,522.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:531.83,533.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:543.97,545.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:554.64,556.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:566.62,568.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:577.78,579.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:589.80,591.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:600.76,602.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:612.136,614.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:623.91,625.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/reader.go:634.71,636.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/resource.go:58.151,63.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/semigroup.go:39.41,43.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/sync.go:46.78,54.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:31.89,39.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:48.103,56.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:65.71,67.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:75.112,83.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:92.124,100.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:108.94,110.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:120.95,128.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:137.92,145.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:148.106,156.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:165.74,167.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:170.118,178.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:181.115,189.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:192.127,200.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:203.97,205.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:215.95,223.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:232.92,240.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:243.106,251.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:260.74,262.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:265.115,273.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:276.127,284.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:287.118,295.2 1 0
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/traverse.go:304.97,306.2 1 0
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
mode: set
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:117.52,119.103 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:119.103,120.80 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:120.80,121.41 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:121.41,123.19 2 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:123.19,126.6 2 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:127.5,127.20 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:132.2,132.93 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:132.93,133.80 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:133.80,134.41 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:134.41,136.19 2 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:136.19,138.6 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:139.5,139.20 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:144.2,150.50 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/builder/builder.go:150.50,153.4 2 1
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
mode: set
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:111.76,116.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:134.49,136.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:161.90,162.65 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:162.65,166.76 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:166.76,176.5 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:198.73,203.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:222.74,227.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:234.76,236.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:245.74,254.2 1 1
|
|
||||||
github.com/IBM/fp-go/v2/context/readerioeither/http/request.go:281.76,286.2 1 1
|
|
||||||
@@ -1,720 +0,0 @@
|
|||||||
// 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 readerioeither
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/IBM/fp-go/v2/either"
|
|
||||||
"github.com/IBM/fp-go/v2/errors"
|
|
||||||
"github.com/IBM/fp-go/v2/function"
|
|
||||||
"github.com/IBM/fp-go/v2/io"
|
|
||||||
"github.com/IBM/fp-go/v2/ioeither"
|
|
||||||
"github.com/IBM/fp-go/v2/readerioeither"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// useParallel is the feature flag to control if we use the parallel or the sequential implementation of ap
|
|
||||||
useParallel = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromEither converts an [Either] into a [ReaderIOEither].
|
|
||||||
// The resulting computation ignores the context and immediately returns the Either value.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - e: The Either value to lift into ReaderIOEither
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that produces the given Either value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func FromEither[A any](e Either[A]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.FromEither[context.Context](e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Left creates a [ReaderIOEither] that represents a failed computation with the given error.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - l: The error value
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that always fails with the given error.
|
|
||||||
func Left[A any](l error) ReaderIOEither[A] {
|
|
||||||
return readerioeither.Left[context.Context, A](l)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right creates a [ReaderIOEither] that represents a successful computation with the given value.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - r: The success value
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that always succeeds with the given value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Right[A any](r A) ReaderIOEither[A] {
|
|
||||||
return readerioeither.Right[context.Context, error](r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadMap transforms the success value of a [ReaderIOEither] using the provided function.
|
|
||||||
// If the computation fails, the error is propagated unchanged.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fa: The ReaderIOEither to transform
|
|
||||||
// - f: The transformation function
|
|
||||||
//
|
|
||||||
// Returns a new ReaderIOEither with the transformed value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
|
||||||
return readerioeither.MonadMap(fa, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map transforms the success value of a [ReaderIOEither] using the provided function.
|
|
||||||
// This is the curried version of [MonadMap], useful for composition.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: The transformation function
|
|
||||||
//
|
|
||||||
// Returns a function that transforms a ReaderIOEither.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Map[A, B any](f func(A) B) Operator[A, B] {
|
|
||||||
return readerioeither.Map[context.Context, error](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadMapTo replaces the success value of a [ReaderIOEither] with a constant value.
|
|
||||||
// If the computation fails, the error is propagated unchanged.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fa: The ReaderIOEither to transform
|
|
||||||
// - b: The constant value to use
|
|
||||||
//
|
|
||||||
// Returns a new ReaderIOEither with the constant value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] {
|
|
||||||
return readerioeither.MonadMapTo(fa, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MapTo replaces the success value of a [ReaderIOEither] with a constant value.
|
|
||||||
// This is the curried version of [MonadMapTo].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - b: The constant value to use
|
|
||||||
//
|
|
||||||
// Returns a function that transforms a ReaderIOEither.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MapTo[A, B any](b B) Operator[A, B] {
|
|
||||||
return readerioeither.MapTo[context.Context, error, A](b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadChain sequences two [ReaderIOEither] computations, where the second depends on the result of the first.
|
|
||||||
// If the first computation fails, the second is not executed.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - ma: The first ReaderIOEither
|
|
||||||
// - f: Function that produces the second ReaderIOEither based on the first's result
|
|
||||||
//
|
|
||||||
// Returns a new ReaderIOEither representing the sequenced computation.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadChain[A, B any](ma ReaderIOEither[A], f Kleisli[A, B]) ReaderIOEither[B] {
|
|
||||||
return readerioeither.MonadChain(ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chain sequences two [ReaderIOEither] computations, where the second depends on the result of the first.
|
|
||||||
// This is the curried version of [MonadChain], useful for composition.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that produces the second ReaderIOEither based on the first's result
|
|
||||||
//
|
|
||||||
// Returns a function that sequences ReaderIOEither computations.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
|
||||||
return readerioeither.Chain(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadChainFirst sequences two [ReaderIOEither] computations but returns the result of the first.
|
|
||||||
// The second computation is executed for its side effects only.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - ma: The first ReaderIOEither
|
|
||||||
// - f: Function that produces the second ReaderIOEither
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither with the result of the first computation.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadChainFirst[A, B any](ma ReaderIOEither[A], f Kleisli[A, B]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.MonadChainFirst(ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainFirst sequences two [ReaderIOEither] computations but returns the result of the first.
|
|
||||||
// This is the curried version of [MonadChainFirst].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that produces the second ReaderIOEither
|
|
||||||
//
|
|
||||||
// Returns a function that sequences ReaderIOEither computations.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
|
||||||
return readerioeither.ChainFirst(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of creates a [ReaderIOEither] that always succeeds with the given value.
|
|
||||||
// This is the same as [Right] and represents the monadic return operation.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - a: The value to wrap
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that always succeeds with the given value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Of[A any](a A) ReaderIOEither[A] {
|
|
||||||
return readerioeither.Of[context.Context, error](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withCancelCauseFunc[A any](cancel context.CancelCauseFunc, ma IOEither[A]) IOEither[A] {
|
|
||||||
return function.Pipe3(
|
|
||||||
ma,
|
|
||||||
ioeither.Swap[error, A],
|
|
||||||
ioeither.ChainFirstIOK[A](func(err error) func() any {
|
|
||||||
return io.FromImpure(func() { cancel(err) })
|
|
||||||
}),
|
|
||||||
ioeither.Swap[A, error],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadApPar implements parallel applicative application for [ReaderIOEither].
|
|
||||||
// It executes both computations in parallel and creates a sub-context that will be canceled
|
|
||||||
// if either operation fails. This provides automatic cancellation propagation.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fab: ReaderIOEither containing a function
|
|
||||||
// - fa: ReaderIOEither containing a value
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither with the function applied to the value.
|
|
||||||
func MonadApPar[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) ReaderIOEither[B] {
|
|
||||||
// context sensitive input
|
|
||||||
cfab := WithContext(fab)
|
|
||||||
cfa := WithContext(fa)
|
|
||||||
|
|
||||||
return func(ctx context.Context) IOEither[B] {
|
|
||||||
// quick check for cancellation
|
|
||||||
if err := context.Cause(ctx); err != nil {
|
|
||||||
return ioeither.Left[B](err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return func() Either[B] {
|
|
||||||
// quick check for cancellation
|
|
||||||
if err := context.Cause(ctx); err != nil {
|
|
||||||
return either.Left[B](err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create sub-contexts for fa and fab, so they can cancel one other
|
|
||||||
ctxSub, cancelSub := context.WithCancelCause(ctx)
|
|
||||||
defer cancelSub(nil) // cancel has to be called in all paths
|
|
||||||
|
|
||||||
fabIOE := withCancelCauseFunc(cancelSub, cfab(ctxSub))
|
|
||||||
faIOE := withCancelCauseFunc(cancelSub, cfa(ctxSub))
|
|
||||||
|
|
||||||
return ioeither.MonadApPar(fabIOE, faIOE)()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadAp implements applicative application for [ReaderIOEither].
|
|
||||||
// By default, it uses parallel execution ([MonadApPar]) but can be configured to use
|
|
||||||
// sequential execution ([MonadApSeq]) via the useParallel constant.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fab: ReaderIOEither containing a function
|
|
||||||
// - fa: ReaderIOEither containing a value
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither with the function applied to the value.
|
|
||||||
func MonadAp[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) ReaderIOEither[B] {
|
|
||||||
// dispatch to the configured version
|
|
||||||
if useParallel {
|
|
||||||
return MonadApPar(fab, fa)
|
|
||||||
}
|
|
||||||
return MonadApSeq(fab, fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadApSeq implements sequential applicative application for [ReaderIOEither].
|
|
||||||
// It executes the function computation first, then the value computation.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fab: ReaderIOEither containing a function
|
|
||||||
// - fa: ReaderIOEither containing a value
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither with the function applied to the value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadApSeq[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) ReaderIOEither[B] {
|
|
||||||
return readerioeither.MonadApSeq(fab, fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ap applies a function wrapped in a [ReaderIOEither] to a value wrapped in a ReaderIOEither.
|
|
||||||
// This is the curried version of [MonadAp], using the default execution mode.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fa: ReaderIOEither containing a value
|
|
||||||
//
|
|
||||||
// Returns a function that applies a ReaderIOEither function to the value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Ap[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
|
||||||
return function.Bind2nd(MonadAp[B, A], fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApSeq applies a function wrapped in a [ReaderIOEither] to a value sequentially.
|
|
||||||
// This is the curried version of [MonadApSeq].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fa: ReaderIOEither containing a value
|
|
||||||
//
|
|
||||||
// Returns a function that applies a ReaderIOEither function to the value sequentially.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ApSeq[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
|
||||||
return function.Bind2nd(MonadApSeq[B, A], fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApPar applies a function wrapped in a [ReaderIOEither] to a value in parallel.
|
|
||||||
// This is the curried version of [MonadApPar].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fa: ReaderIOEither containing a value
|
|
||||||
//
|
|
||||||
// Returns a function that applies a ReaderIOEither function to the value in parallel.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ApPar[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
|
||||||
return function.Bind2nd(MonadApPar[B, A], fa)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromPredicate creates a [ReaderIOEither] from a predicate function.
|
|
||||||
// If the predicate returns true, the value is wrapped in Right; otherwise, Left with the error from onFalse.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - pred: Predicate function to test the value
|
|
||||||
// - onFalse: Function to generate an error when predicate fails
|
|
||||||
//
|
|
||||||
// Returns a function that converts a value to ReaderIOEither based on the predicate.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A] {
|
|
||||||
return readerioeither.FromPredicate[context.Context](pred, onFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OrElse provides an alternative [ReaderIOEither] computation if the first one fails.
|
|
||||||
// The alternative is only executed if the first computation results in a Left (error).
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - onLeft: Function that produces an alternative ReaderIOEither from the error
|
|
||||||
//
|
|
||||||
// Returns a function that provides fallback behavior for failed computations.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func OrElse[A any](onLeft Kleisli[error, A]) Operator[A, A] {
|
|
||||||
return readerioeither.OrElse[context.Context](onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask returns a [ReaderIOEither] that provides access to the context.
|
|
||||||
// This is useful for accessing the [context.Context] within a computation.
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that produces the context.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Ask() ReaderIOEither[context.Context] {
|
|
||||||
return readerioeither.Ask[context.Context, error]()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadChainEitherK chains a function that returns an [Either] into a [ReaderIOEither] computation.
|
|
||||||
// This is useful for integrating pure Either-returning functions into ReaderIOEither workflows.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - ma: The ReaderIOEither to chain from
|
|
||||||
// - f: Function that produces an Either
|
|
||||||
//
|
|
||||||
// Returns a new ReaderIOEither with the chained computation.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) ReaderIOEither[B] {
|
|
||||||
return readerioeither.MonadChainEitherK[context.Context](ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainEitherK chains a function that returns an [Either] into a [ReaderIOEither] computation.
|
|
||||||
// This is the curried version of [MonadChainEitherK].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that produces an Either
|
|
||||||
//
|
|
||||||
// Returns a function that chains the Either-returning function.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ChainEitherK[A, B any](f func(A) Either[B]) Operator[A, B] {
|
|
||||||
return readerioeither.ChainEitherK[context.Context](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadChainFirstEitherK chains a function that returns an [Either] but keeps the original value.
|
|
||||||
// The Either-returning function is executed for its validation/side effects only.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - ma: The ReaderIOEither to chain from
|
|
||||||
// - f: Function that produces an Either
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither with the original value if both computations succeed.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.MonadChainFirstEitherK[context.Context](ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainFirstEitherK chains a function that returns an [Either] but keeps the original value.
|
|
||||||
// This is the curried version of [MonadChainFirstEitherK].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that produces an Either
|
|
||||||
//
|
|
||||||
// Returns a function that chains the Either-returning function.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ChainFirstEitherK[A, B any](f func(A) Either[B]) Operator[A, A] {
|
|
||||||
return readerioeither.ChainFirstEitherK[context.Context](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainOptionK chains a function that returns an [Option] into a [ReaderIOEither] computation.
|
|
||||||
// If the Option is None, the provided error function is called.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - onNone: Function to generate an error when Option is None
|
|
||||||
//
|
|
||||||
// Returns a function that chains Option-returning functions into ReaderIOEither.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operator[A, B] {
|
|
||||||
return readerioeither.ChainOptionK[context.Context, A, B](onNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromIOEither converts an [IOEither] into a [ReaderIOEither].
|
|
||||||
// The resulting computation ignores the context.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - t: The IOEither to convert
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that executes the IOEither.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func FromIOEither[A any](t ioeither.IOEither[error, A]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.FromIOEither[context.Context](t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromIO converts an [IO] into a [ReaderIOEither].
|
|
||||||
// The IO computation always succeeds, so it's wrapped in Right.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - t: The IO to convert
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that executes the IO and wraps the result in Right.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func FromIO[A any](t IO[A]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.FromIO[context.Context, error](t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromLazy converts a [Lazy] computation into a [ReaderIOEither].
|
|
||||||
// The Lazy computation always succeeds, so it's wrapped in Right.
|
|
||||||
// This is an alias for [FromIO] since Lazy and IO have the same structure.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - t: The Lazy computation to convert
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that executes the Lazy computation and wraps the result in Right.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func FromLazy[A any](t Lazy[A]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.FromIO[context.Context, error](t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Never returns a [ReaderIOEither] that blocks indefinitely until the context is canceled.
|
|
||||||
// This is useful for creating computations that wait for external cancellation signals.
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that waits for context cancellation and returns the cancellation error.
|
|
||||||
func Never[A any]() ReaderIOEither[A] {
|
|
||||||
return func(ctx context.Context) IOEither[A] {
|
|
||||||
return func() Either[A] {
|
|
||||||
<-ctx.Done()
|
|
||||||
return either.Left[A](context.Cause(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadChainIOK chains a function that returns an [IO] into a [ReaderIOEither] computation.
|
|
||||||
// The IO computation always succeeds, so it's wrapped in Right.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - ma: The ReaderIOEither to chain from
|
|
||||||
// - f: Function that produces an IO
|
|
||||||
//
|
|
||||||
// Returns a new ReaderIOEither with the chained IO computation.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEither[B] {
|
|
||||||
return readerioeither.MonadChainIOK(ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainIOK chains a function that returns an [IO] into a [ReaderIOEither] computation.
|
|
||||||
// This is the curried version of [MonadChainIOK].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that produces an IO
|
|
||||||
//
|
|
||||||
// Returns a function that chains the IO-returning function.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ChainIOK[A, B any](f func(A) IO[B]) Operator[A, B] {
|
|
||||||
return readerioeither.ChainIOK[context.Context, error](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadChainFirstIOK chains a function that returns an [IO] but keeps the original value.
|
|
||||||
// The IO computation is executed for its side effects only.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - ma: The ReaderIOEither to chain from
|
|
||||||
// - f: Function that produces an IO
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither with the original value after executing the IO.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.MonadChainFirstIOK(ma, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainFirstIOK chains a function that returns an [IO] but keeps the original value.
|
|
||||||
// This is the curried version of [MonadChainFirstIOK].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that produces an IO
|
|
||||||
//
|
|
||||||
// Returns a function that chains the IO-returning function.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ChainFirstIOK[A, B any](f func(A) IO[B]) Operator[A, A] {
|
|
||||||
return readerioeither.ChainFirstIOK[context.Context, error](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainIOEitherK chains a function that returns an [IOEither] into a [ReaderIOEither] computation.
|
|
||||||
// This is useful for integrating IOEither-returning functions into ReaderIOEither workflows.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that produces an IOEither
|
|
||||||
//
|
|
||||||
// Returns a function that chains the IOEither-returning function.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func ChainIOEitherK[A, B any](f func(A) ioeither.IOEither[error, B]) Operator[A, B] {
|
|
||||||
return readerioeither.ChainIOEitherK[context.Context](f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay creates an operation that delays execution by the specified duration.
|
|
||||||
// The computation waits for either the delay to expire or the context to be canceled.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - delay: The duration to wait before executing the computation
|
|
||||||
//
|
|
||||||
// Returns a function that delays a ReaderIOEither computation.
|
|
||||||
func Delay[A any](delay time.Duration) Operator[A, A] {
|
|
||||||
return func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
|
||||||
return func(ctx context.Context) IOEither[A] {
|
|
||||||
return func() Either[A] {
|
|
||||||
// manage the timeout
|
|
||||||
timeoutCtx, cancelTimeout := context.WithTimeout(ctx, delay)
|
|
||||||
defer cancelTimeout()
|
|
||||||
// whatever comes first
|
|
||||||
select {
|
|
||||||
case <-timeoutCtx.Done():
|
|
||||||
return ma(ctx)()
|
|
||||||
case <-ctx.Done():
|
|
||||||
return either.Left[A](context.Cause(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timer returns the current time after waiting for the specified delay.
|
|
||||||
// This is useful for creating time-based computations.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - delay: The duration to wait before returning the time
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that produces the current time after the delay.
|
|
||||||
func Timer(delay time.Duration) ReaderIOEither[time.Time] {
|
|
||||||
return function.Pipe2(
|
|
||||||
io.Now,
|
|
||||||
FromIO[time.Time],
|
|
||||||
Delay[time.Time](delay),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defer creates a [ReaderIOEither] by lazily generating a new computation each time it's executed.
|
|
||||||
// This is useful for creating computations that should be re-evaluated on each execution.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - gen: Lazy generator function that produces a ReaderIOEither
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that generates a fresh computation on each execution.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Defer[A any](gen Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.Defer(gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TryCatch wraps a function that returns a tuple (value, error) into a [ReaderIOEither].
|
|
||||||
// This is the standard way to convert Go error-returning functions into ReaderIOEither.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - f: Function that takes a context and returns a function producing (value, error)
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that wraps the error-returning function.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] {
|
|
||||||
return readerioeither.TryCatch(f, errors.IdentityError)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadAlt provides an alternative [ReaderIOEither] if the first one fails.
|
|
||||||
// The alternative is lazily evaluated only if needed.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - first: The primary ReaderIOEither to try
|
|
||||||
// - second: Lazy alternative ReaderIOEither to use if first fails
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that tries the first, then the second if first fails.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadAlt[A any](first ReaderIOEither[A], second Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.MonadAlt(first, second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alt provides an alternative [ReaderIOEither] if the first one fails.
|
|
||||||
// This is the curried version of [MonadAlt].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - second: Lazy alternative ReaderIOEither to use if first fails
|
|
||||||
//
|
|
||||||
// Returns a function that provides fallback behavior.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Alt[A any](second Lazy[ReaderIOEither[A]]) Operator[A, A] {
|
|
||||||
return readerioeither.Alt(second)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once.
|
|
||||||
// The context used to compute the value is the context of the first call, so do not use this
|
|
||||||
// method if the value has a functional dependency on the content of the context.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - rdr: The ReaderIOEither to memoize
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither that caches its result after the first execution.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.Memoize(rdr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flatten converts a nested [ReaderIOEither] into a flat [ReaderIOEither].
|
|
||||||
// This is equivalent to [MonadChain] with the identity function.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - rdr: The nested ReaderIOEither to flatten
|
|
||||||
//
|
|
||||||
// Returns a flattened ReaderIOEither.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Flatten[A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
|
|
||||||
return readerioeither.Flatten(rdr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MonadFlap applies a value to a function wrapped in a [ReaderIOEither].
|
|
||||||
// This is the reverse of [MonadAp], useful in certain composition scenarios.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - fab: ReaderIOEither containing a function
|
|
||||||
// - a: The value to apply to the function
|
|
||||||
//
|
|
||||||
// Returns a ReaderIOEither with the function applied to the value.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] {
|
|
||||||
return readerioeither.MonadFlap(fab, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flap applies a value to a function wrapped in a [ReaderIOEither].
|
|
||||||
// This is the curried version of [MonadFlap].
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - a: The value to apply to the function
|
|
||||||
//
|
|
||||||
// Returns a function that applies the value to a ReaderIOEither function.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
|
||||||
return readerioeither.Flap[context.Context, error, B](a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fold handles both success and error cases of a [ReaderIOEither] by providing handlers for each.
|
|
||||||
// Both handlers return ReaderIOEither, allowing for further composition.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - onLeft: Handler for error case
|
|
||||||
// - onRight: Handler for success case
|
|
||||||
//
|
|
||||||
// Returns a function that folds a ReaderIOEither into a new ReaderIOEither.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func Fold[A, B any](onLeft Kleisli[error, B], onRight Kleisli[A, B]) Operator[A, B] {
|
|
||||||
return readerioeither.Fold(onLeft, onRight)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOrElse extracts the value from a [ReaderIOEither], providing a default via a function if it fails.
|
|
||||||
// The result is a [ReaderIO] that always succeeds.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - onLeft: Function to provide a default value from the error
|
|
||||||
//
|
|
||||||
// Returns a function that converts a ReaderIOEither to a ReaderIO.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func GetOrElse[A any](onLeft func(error) ReaderIO[A]) func(ReaderIOEither[A]) ReaderIO[A] {
|
|
||||||
return readerioeither.GetOrElse(onLeft)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OrLeft transforms the error of a [ReaderIOEither] using the provided function.
|
|
||||||
// The success value is left unchanged.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
// - onLeft: Function to transform the error
|
|
||||||
//
|
|
||||||
// Returns a function that transforms the error of a ReaderIOEither.
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func OrLeft[A any](onLeft func(error) ReaderIO[error]) Operator[A, A] {
|
|
||||||
return readerioeither.OrLeft[A](onLeft)
|
|
||||||
}
|
|
||||||
@@ -1,532 +0,0 @@
|
|||||||
// 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 readerioeither
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFromEither(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
rightVal := E.Right[error](42)
|
|
||||||
result := FromEither(rightVal)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with Left
|
|
||||||
err := errors.New("test error")
|
|
||||||
leftVal := E.Left[int](err)
|
|
||||||
result = FromEither(leftVal)(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLeftRight(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test Left
|
|
||||||
err := errors.New("test error")
|
|
||||||
result := Left[int](err)(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
|
|
||||||
// Test Right
|
|
||||||
result = Right(42)(ctx)()
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
val, _ := E.Unwrap(result)
|
|
||||||
assert.Equal(t, 42, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOf(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
result := Of(42)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadMap(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadMap(Right(42), func(x int) int { return x * 2 })(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
|
|
||||||
// Test with Left
|
|
||||||
err := errors.New("test error")
|
|
||||||
result = MonadMap(Left[int](err), func(x int) int { return x * 2 })(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadMapTo(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadMapTo(Right(42), "hello")(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error]("hello"), result)
|
|
||||||
|
|
||||||
// Test with Left
|
|
||||||
err := errors.New("test error")
|
|
||||||
result = MonadMapTo(Left[int](err), "hello")(ctx)()
|
|
||||||
assert.Equal(t, E.Left[string](err), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadChain(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadChain(Right(42), func(x int) ReaderIOEither[int] {
|
|
||||||
return Right(x * 2)
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
|
|
||||||
// Test with Left
|
|
||||||
err := errors.New("test error")
|
|
||||||
result = MonadChain(Left[int](err), func(x int) ReaderIOEither[int] {
|
|
||||||
return Right(x * 2)
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
|
|
||||||
// Test where function returns Left
|
|
||||||
result = MonadChain(Right(42), func(x int) ReaderIOEither[int] {
|
|
||||||
return Left[int](errors.New("chain error"))
|
|
||||||
})(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadChainFirst(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadChainFirst(Right(42), func(x int) ReaderIOEither[string] {
|
|
||||||
return Right("ignored")
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with Left in first
|
|
||||||
err := errors.New("test error")
|
|
||||||
result = MonadChainFirst(Left[int](err), func(x int) ReaderIOEither[string] {
|
|
||||||
return Right("ignored")
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
|
|
||||||
// Test with Left in second
|
|
||||||
result = MonadChainFirst(Right(42), func(x int) ReaderIOEither[string] {
|
|
||||||
return Left[string](errors.New("chain error"))
|
|
||||||
})(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadApSeq(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with both Right
|
|
||||||
fct := Right(func(x int) int { return x * 2 })
|
|
||||||
val := Right(42)
|
|
||||||
result := MonadApSeq(fct, val)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
|
|
||||||
// Test with Left function
|
|
||||||
err := errors.New("function error")
|
|
||||||
fct = Left[func(int) int](err)
|
|
||||||
result = MonadApSeq(fct, val)(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
|
|
||||||
// Test with Left value
|
|
||||||
fct = Right(func(x int) int { return x * 2 })
|
|
||||||
err = errors.New("value error")
|
|
||||||
val = Left[int](err)
|
|
||||||
result = MonadApSeq(fct, val)(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadApPar(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with both Right
|
|
||||||
fct := Right(func(x int) int { return x * 2 })
|
|
||||||
val := Right(42)
|
|
||||||
result := MonadApPar(fct, val)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromPredicate(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
pred := func(x int) bool { return x > 0 }
|
|
||||||
onFalse := func(x int) error { return fmt.Errorf("value %d is not positive", x) }
|
|
||||||
|
|
||||||
// Test with predicate true
|
|
||||||
result := FromPredicate(pred, onFalse)(42)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with predicate false
|
|
||||||
result = FromPredicate(pred, onFalse)(-1)(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAsk(t *testing.T) {
|
|
||||||
ctx := context.WithValue(t.Context(), "key", "value")
|
|
||||||
result := Ask()(ctx)()
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
retrievedCtx, _ := E.Unwrap(result)
|
|
||||||
assert.Equal(t, "value", retrievedCtx.Value("key"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadChainEitherK(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadChainEitherK(Right(42), func(x int) E.Either[error, int] {
|
|
||||||
return E.Right[error](x * 2)
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
|
|
||||||
// Test with Left in Either
|
|
||||||
result = MonadChainEitherK(Right(42), func(x int) E.Either[error, int] {
|
|
||||||
return E.Left[int](errors.New("either error"))
|
|
||||||
})(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadChainFirstEitherK(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadChainFirstEitherK(Right(42), func(x int) E.Either[error, string] {
|
|
||||||
return E.Right[error]("ignored")
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with Left in Either
|
|
||||||
result = MonadChainFirstEitherK(Right(42), func(x int) E.Either[error, string] {
|
|
||||||
return E.Left[string](errors.New("either error"))
|
|
||||||
})(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainOptionKFunc(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
onNone := func() error { return errors.New("none error") }
|
|
||||||
|
|
||||||
// Test with Some
|
|
||||||
chainFunc := ChainOptionK[int, int](onNone)
|
|
||||||
result := chainFunc(func(x int) O.Option[int] {
|
|
||||||
return O.Some(x * 2)
|
|
||||||
})(Right(42))(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
|
|
||||||
// Test with None
|
|
||||||
result = chainFunc(func(x int) O.Option[int] {
|
|
||||||
return O.None[int]()
|
|
||||||
})(Right(42))(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromIOEither(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
ioe := func() E.Either[error, int] {
|
|
||||||
return E.Right[error](42)
|
|
||||||
}
|
|
||||||
result := FromIOEither(ioe)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with Left
|
|
||||||
err := errors.New("test error")
|
|
||||||
ioe = func() E.Either[error, int] {
|
|
||||||
return E.Left[int](err)
|
|
||||||
}
|
|
||||||
result = FromIOEither(ioe)(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromIO(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
io := func() int { return 42 }
|
|
||||||
result := FromIO(io)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromLazy(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
lazy := func() int { return 42 }
|
|
||||||
result := FromLazy(lazy)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNeverWithCancel(t *testing.T) {
|
|
||||||
ctx, cancel := context.WithCancel(t.Context())
|
|
||||||
|
|
||||||
// Start Never in a goroutine
|
|
||||||
done := make(chan E.Either[error, int])
|
|
||||||
go func() {
|
|
||||||
done <- Never[int]()(ctx)()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Cancel the context
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
// Should receive cancellation error
|
|
||||||
result := <-done
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadChainIOK(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadChainIOK(Right(42), func(x int) func() int {
|
|
||||||
return func() int { return x * 2 }
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadChainFirstIOK(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right
|
|
||||||
result := MonadChainFirstIOK(Right(42), func(x int) func() string {
|
|
||||||
return func() string { return "ignored" }
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelayFunc(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
delay := 100 * time.Millisecond
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
delayFunc := Delay[int](delay)
|
|
||||||
result := delayFunc(Right(42))(ctx)()
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
assert.GreaterOrEqual(t, elapsed, delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefer(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
count := 0
|
|
||||||
|
|
||||||
gen := func() ReaderIOEither[int] {
|
|
||||||
count++
|
|
||||||
return Right(count)
|
|
||||||
}
|
|
||||||
|
|
||||||
deferred := Defer(gen)
|
|
||||||
|
|
||||||
// First call
|
|
||||||
result1 := deferred(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](1), result1)
|
|
||||||
|
|
||||||
// Second call should generate new value
|
|
||||||
result2 := deferred(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](2), result2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTryCatch(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test success
|
|
||||||
result := TryCatch(func(ctx context.Context) func() (int, error) {
|
|
||||||
return func() (int, error) {
|
|
||||||
return 42, nil
|
|
||||||
}
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test error
|
|
||||||
err := errors.New("test error")
|
|
||||||
result = TryCatch(func(ctx context.Context) func() (int, error) {
|
|
||||||
return func() (int, error) {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Left[int](err), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadAlt(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with Right (alternative not called)
|
|
||||||
result := MonadAlt(Right(42), func() ReaderIOEither[int] {
|
|
||||||
return Right(99)
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with Left (alternative called)
|
|
||||||
err := errors.New("test error")
|
|
||||||
result = MonadAlt(Left[int](err), func() ReaderIOEither[int] {
|
|
||||||
return Right(99)
|
|
||||||
})(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](99), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMemoize(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
count := 0
|
|
||||||
|
|
||||||
rdr := Memoize(FromLazy(func() int {
|
|
||||||
count++
|
|
||||||
return count
|
|
||||||
}))
|
|
||||||
|
|
||||||
// First call
|
|
||||||
result1 := rdr(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](1), result1)
|
|
||||||
|
|
||||||
// Second call should return memoized value
|
|
||||||
result2 := rdr(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](1), result2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlatten(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
nested := Right(Right(42))
|
|
||||||
result := Flatten(nested)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadFlap(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
fab := Right(func(x int) int { return x * 2 })
|
|
||||||
result := MonadFlap(fab, 42)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithContext(t *testing.T) {
|
|
||||||
// Test with non-canceled context
|
|
||||||
ctx := t.Context()
|
|
||||||
result := WithContext(Right(42))(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with canceled context
|
|
||||||
ctx, cancel := context.WithCancel(t.Context())
|
|
||||||
cancel()
|
|
||||||
result = WithContext(Right(42))(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMonadAp(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with both Right
|
|
||||||
fct := Right(func(x int) int { return x * 2 })
|
|
||||||
val := Right(42)
|
|
||||||
result := MonadAp(fct, val)(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test traverse functions
|
|
||||||
func TestSequenceArray(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with all Right
|
|
||||||
arr := []ReaderIOEither[int]{Right(1), Right(2), Right(3)}
|
|
||||||
result := SequenceArray(arr)(ctx)()
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
vals, _ := E.Unwrap(result)
|
|
||||||
assert.Equal(t, []int{1, 2, 3}, vals)
|
|
||||||
|
|
||||||
// Test with one Left
|
|
||||||
err := errors.New("test error")
|
|
||||||
arr = []ReaderIOEither[int]{Right(1), Left[int](err), Right(3)}
|
|
||||||
result = SequenceArray(arr)(ctx)()
|
|
||||||
assert.True(t, E.IsLeft(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTraverseArray(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test transformation
|
|
||||||
arr := []int{1, 2, 3}
|
|
||||||
result := TraverseArray(func(x int) ReaderIOEither[int] {
|
|
||||||
return Right(x * 2)
|
|
||||||
})(arr)(ctx)()
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
vals, _ := E.Unwrap(result)
|
|
||||||
assert.Equal(t, []int{2, 4, 6}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSequenceRecord(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test with all Right
|
|
||||||
rec := map[string]ReaderIOEither[int]{
|
|
||||||
"a": Right(1),
|
|
||||||
"b": Right(2),
|
|
||||||
}
|
|
||||||
result := SequenceRecord(rec)(ctx)()
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
vals, _ := E.Unwrap(result)
|
|
||||||
assert.Equal(t, 1, vals["a"])
|
|
||||||
assert.Equal(t, 2, vals["b"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTraverseRecord(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
// Test transformation
|
|
||||||
rec := map[string]int{"a": 1, "b": 2}
|
|
||||||
result := TraverseRecord[string](func(x int) ReaderIOEither[int] {
|
|
||||||
return Right(x * 2)
|
|
||||||
})(rec)(ctx)()
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
vals, _ := E.Unwrap(result)
|
|
||||||
assert.Equal(t, 2, vals["a"])
|
|
||||||
assert.Equal(t, 4, vals["b"])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test monoid functions
|
|
||||||
func TestAltSemigroup(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
sg := AltSemigroup[int]()
|
|
||||||
|
|
||||||
// Test with Right (first succeeds)
|
|
||||||
result := sg.Concat(Right(42), Right(99))(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
|
||||||
|
|
||||||
// Test with Left then Right (fallback)
|
|
||||||
err := errors.New("test error")
|
|
||||||
result = sg.Concat(Left[int](err), Right(99))(ctx)()
|
|
||||||
assert.Equal(t, E.Right[error](99), result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Do notation
|
|
||||||
func TestDo(t *testing.T) {
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
type State struct {
|
|
||||||
Value int
|
|
||||||
}
|
|
||||||
|
|
||||||
result := Do(State{Value: 42})(ctx)()
|
|
||||||
assert.True(t, E.IsRight(result))
|
|
||||||
state, _ := E.Unwrap(result)
|
|
||||||
assert.Equal(t, 42, state.Value)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# ReaderIOEither Benchmarks
|
# ReaderIOResult Benchmarks
|
||||||
|
|
||||||
This document describes the benchmark suite for the `context/readerioeither` package and how to interpret the results to identify performance bottlenecks.
|
This document describes the benchmark suite for the `context/readerioeither` package and how to interpret the results to identify performance bottlenecks.
|
||||||
|
|
||||||
@@ -35,8 +35,8 @@ go test -bench=. -benchmem -benchtime=100000x
|
|||||||
- Construction is very fast, suitable for hot paths
|
- Construction is very fast, suitable for hot paths
|
||||||
|
|
||||||
### 2. Conversion Operations
|
### 2. Conversion Operations
|
||||||
- `BenchmarkFromEither_Right/Left` - Converting Either to ReaderIOEither (~70ns, 2 allocs)
|
- `BenchmarkFromEither_Right/Left` - Converting Either to ReaderIOResult (~70ns, 2 allocs)
|
||||||
- `BenchmarkFromIO` - Converting IO to ReaderIOEither (~78ns, 3 allocs)
|
- `BenchmarkFromIO` - Converting IO to ReaderIOResult (~78ns, 3 allocs)
|
||||||
- `BenchmarkFromIOEither_Right/Left` - Converting IOEither (~23ns, 1 alloc)
|
- `BenchmarkFromIOEither_Right/Left` - Converting IOEither (~23ns, 1 alloc)
|
||||||
|
|
||||||
**Key Insights:**
|
**Key Insights:**
|
||||||
724
v2/context/readerioresult/bind.go
Normal file
724
v2/context/readerioresult/bind.go
Normal file
@@ -0,0 +1,724 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
|
"github.com/IBM/fp-go/v2/io"
|
||||||
|
"github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
|
"github.com/IBM/fp-go/v2/readerio"
|
||||||
|
RIOR "github.com/IBM/fp-go/v2/readerioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
// result := readerioeither.Do(State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Do[S any](
|
||||||
|
empty S,
|
||||||
|
) ReaderIOResult[S] {
|
||||||
|
return RIOR.Of[context.Context](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 context.Context from the 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
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do(State{}),
|
||||||
|
// readerioeither.Bind(
|
||||||
|
// func(user User) func(State) State {
|
||||||
|
// return func(s State) State { s.User = user; return s }
|
||||||
|
// },
|
||||||
|
// func(s State) readerioeither.ReaderIOResult[User] {
|
||||||
|
// return func(ctx context.Context) ioeither.IOEither[error, User] {
|
||||||
|
// return ioeither.TryCatch(func() (User, error) {
|
||||||
|
// return fetchUser(ctx)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// readerioeither.Bind(
|
||||||
|
// func(cfg Config) func(State) State {
|
||||||
|
// return func(s State) State { s.Config = cfg; return s }
|
||||||
|
// },
|
||||||
|
// func(s State) readerioeither.ReaderIOResult[Config] {
|
||||||
|
// // This can access s.User from the previous step
|
||||||
|
// return func(ctx context.Context) ioeither.IOEither[error, Config] {
|
||||||
|
// return ioeither.TryCatch(func() (Config, error) {
|
||||||
|
// return fetchConfigForUser(ctx, 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 RIOR.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 RIOR.Let[context.Context](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 RIOR.LetTo[context.Context](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 RIOR.BindTo[context.Context](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
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // These operations are independent and can be combined with ApS
|
||||||
|
// getUser := func(ctx context.Context) ioeither.IOEither[error, User] {
|
||||||
|
// return ioeither.TryCatch(func() (User, error) {
|
||||||
|
// return fetchUser(ctx)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// getConfig := func(ctx context.Context) ioeither.IOEither[error, Config] {
|
||||||
|
// return ioeither.TryCatch(func() (Config, error) {
|
||||||
|
// return fetchConfig(ctx)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do(State{}),
|
||||||
|
// readerioeither.ApS(
|
||||||
|
// func(user User) func(State) State {
|
||||||
|
// return func(s State) State { s.User = user; return s }
|
||||||
|
// },
|
||||||
|
// getUser,
|
||||||
|
// ),
|
||||||
|
// readerioeither.ApS(
|
||||||
|
// func(cfg Config) func(State) State {
|
||||||
|
// return func(s State) State { s.Config = cfg; return s }
|
||||||
|
// },
|
||||||
|
// getConfig,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ReaderIOResult[T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return apply.ApS(
|
||||||
|
Ap,
|
||||||
|
Map,
|
||||||
|
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
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// getUser := func(ctx context.Context) ioeither.IOEither[error, User] {
|
||||||
|
// return ioeither.TryCatch(func() (User, error) {
|
||||||
|
// return fetchUser(ctx)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Of(State{}),
|
||||||
|
// readerioeither.ApSL(userLens, getUser),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderIOResult[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApS(lens.Set, 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
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// 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(State{}),
|
||||||
|
// readerioeither.BindL(userLens, func(user User) readerioeither.ReaderIOResult[User] {
|
||||||
|
// return func(ctx context.Context) ioeither.IOEither[error, User] {
|
||||||
|
// return ioeither.TryCatch(func() (User, error) {
|
||||||
|
// return fetchUser(ctx)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return RIOR.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
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// 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(State{User: User{Name: "Alice"}}),
|
||||||
|
// readerioeither.LetL(userLens, func(user User) User {
|
||||||
|
// user.Name = "Bob"
|
||||||
|
// return user
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Operator[S, S] {
|
||||||
|
return RIOR.LetL[context.Context](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
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// 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(State{}),
|
||||||
|
// readerioeither.LetToL(userLens, newUser),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetToL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Operator[S, S] {
|
||||||
|
return RIOR.LetToL[context.Context](lens, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindIOEitherK is a variant of Bind that works with IOEither computations.
|
||||||
|
// It lifts an IOEither Kleisli arrow into the ReaderIOResult context (with context.Context as environment).
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - f: An IOEither Kleisli arrow (S1 -> IOEither[error, T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindIOEitherK[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f ioresult.Kleisli[S1, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return Bind(setter, F.Flow2(f, FromIOEither[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindIOResultK is a variant of Bind that works with IOResult computations.
|
||||||
|
// This is an alias for BindIOEitherK for consistency with the Result naming convention.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - f: An IOResult Kleisli arrow (S1 -> IOResult[T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindIOResultK[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f ioresult.Kleisli[S1, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return Bind(setter, F.Flow2(f, FromIOResult[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindIOK is a variant of Bind that works with IO computations.
|
||||||
|
// It lifts an IO Kleisli arrow into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - f: An IO Kleisli arrow (S1 -> IO[T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindIOK[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f io.Kleisli[S1, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return Bind(setter, F.Flow2(f, FromIO[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindReaderK is a variant of Bind that works with Reader computations.
|
||||||
|
// It lifts a Reader Kleisli arrow (with context.Context) into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - f: A Reader Kleisli arrow (S1 -> Reader[context.Context, T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindReaderK[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f reader.Kleisli[context.Context, S1, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return Bind(setter, F.Flow2(f, FromReader[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindReaderIOK is a variant of Bind that works with ReaderIO computations.
|
||||||
|
// It lifts a ReaderIO Kleisli arrow (with context.Context) into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - f: A ReaderIO Kleisli arrow (S1 -> ReaderIO[context.Context, T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindReaderIOK[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f readerio.Kleisli[context.Context, S1, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return Bind(setter, F.Flow2(f, FromReaderIO[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindEitherK is a variant of Bind that works with Either (Result) computations.
|
||||||
|
// It lifts an Either Kleisli arrow into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - f: An Either Kleisli arrow (S1 -> Either[error, T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindEitherK[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f result.Kleisli[S1, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return Bind(setter, F.Flow2(f, FromEither[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindResultK is a variant of Bind that works with Result computations.
|
||||||
|
// This is an alias for BindEitherK for consistency with the Result naming convention.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - f: A Result Kleisli arrow (S1 -> Result[T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindResultK[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f result.Kleisli[S1, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return Bind(setter, F.Flow2(f, FromResult[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindIOEitherKL is a lens-based variant of BindIOEitherK.
|
||||||
|
// It combines a lens with an IOEither Kleisli arrow, focusing on a specific field
|
||||||
|
// within the state structure.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - f: An IOEither Kleisli arrow (T -> IOEither[error, T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindIOEitherKL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f ioresult.Kleisli[T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return BindL(lens, F.Flow2(f, FromIOEither[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindIOResultKL is a lens-based variant of BindIOResultK.
|
||||||
|
// This is an alias for BindIOEitherKL for consistency with the Result naming convention.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - f: An IOResult Kleisli arrow (T -> IOResult[T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindIOResultKL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f ioresult.Kleisli[T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return BindL(lens, F.Flow2(f, FromIOEither[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindIOKL is a lens-based variant of BindIOK.
|
||||||
|
// It combines a lens with an IO Kleisli arrow, focusing on a specific field
|
||||||
|
// within the state structure.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - f: An IO Kleisli arrow (T -> IO[T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindIOKL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f io.Kleisli[T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return BindL(lens, F.Flow2(f, FromIO[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindReaderKL is a lens-based variant of BindReaderK.
|
||||||
|
// It combines a lens with a Reader Kleisli arrow (with context.Context), focusing on a specific field
|
||||||
|
// within the state structure.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - f: A Reader Kleisli arrow (T -> Reader[context.Context, T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindReaderKL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f reader.Kleisli[context.Context, T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return BindL(lens, F.Flow2(f, FromReader[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindReaderIOKL is a lens-based variant of BindReaderIOK.
|
||||||
|
// It combines a lens with a ReaderIO Kleisli arrow (with context.Context), focusing on a specific field
|
||||||
|
// within the state structure.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - f: A ReaderIO Kleisli arrow (T -> ReaderIO[context.Context, T])
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindReaderIOKL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f readerio.Kleisli[context.Context, T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return BindL(lens, F.Flow2(f, FromReaderIO[T]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApIOEitherS is an applicative variant that works with IOEither values.
|
||||||
|
// Unlike BindIOEitherK, this uses applicative composition (ApS) instead of monadic
|
||||||
|
// composition (Bind), allowing independent computations to be combined.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - fa: An IOEither value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApIOEitherS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa IOResult[T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return F.Bind2nd(F.Flow2[ReaderIOResult[S1], ioresult.Operator[S1, S2]], ioeither.ApS(setter, fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApIOResultS is an applicative variant that works with IOResult values.
|
||||||
|
// This is an alias for ApIOEitherS for consistency with the Result naming convention.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - fa: An IOResult value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApIOResultS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa IOResult[T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return F.Bind2nd(F.Flow2[ReaderIOResult[S1], ioresult.Operator[S1, S2]], ioeither.ApS(setter, fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApIOS is an applicative variant that works with IO values.
|
||||||
|
// It lifts an IO value into the ReaderIOResult context using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - fa: An IO value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApIOS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa IO[T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return ApS(setter, FromIO(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApReaderS is an applicative variant that works with Reader values.
|
||||||
|
// It lifts a Reader value (with context.Context) into the ReaderIOResult context using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - fa: A Reader value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApReaderS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Reader[context.Context, T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return ApS(setter, FromReader(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApReaderIOS is an applicative variant that works with ReaderIO values.
|
||||||
|
// It lifts a ReaderIO value (with context.Context) into the ReaderIOResult context using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - fa: A ReaderIO value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApReaderIOS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ReaderIO[T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return ApS(setter, FromReaderIO(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApEitherS is an applicative variant that works with Either (Result) values.
|
||||||
|
// It lifts an Either value into the ReaderIOResult context using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - fa: An Either value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApEitherS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Result[T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return ApS(setter, FromEither(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApResultS is an applicative variant that works with Result values.
|
||||||
|
// This is an alias for ApEitherS for consistency with the Result naming convention.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - setter: Updates state from S1 to S2 using result T
|
||||||
|
// - fa: A Result value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApResultS[S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Result[T],
|
||||||
|
) Operator[S1, S2] {
|
||||||
|
return ApS(setter, FromResult(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApIOEitherSL is a lens-based variant of ApIOEitherS.
|
||||||
|
// It combines a lens with an IOEither value using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - fa: An IOEither value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApIOEitherSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa IOResult[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return F.Bind2nd(F.Flow2[ReaderIOResult[S], ioresult.Operator[S, S]], ioresult.ApSL(lens, fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApIOResultSL is a lens-based variant of ApIOResultS.
|
||||||
|
// This is an alias for ApIOEitherSL for consistency with the Result naming convention.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - fa: An IOResult value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApIOResultSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa IOResult[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return F.Bind2nd(F.Flow2[ReaderIOResult[S], ioresult.Operator[S, S]], ioresult.ApSL(lens, fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApIOSL is a lens-based variant of ApIOS.
|
||||||
|
// It combines a lens with an IO value using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - fa: An IO value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApIOSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa IO[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApSL(lens, FromIO(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApReaderSL is a lens-based variant of ApReaderS.
|
||||||
|
// It combines a lens with a Reader value (with context.Context) using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - fa: A Reader value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApReaderSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa Reader[context.Context, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApSL(lens, FromReader(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApReaderIOSL is a lens-based variant of ApReaderIOS.
|
||||||
|
// It combines a lens with a ReaderIO value (with context.Context) using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - fa: A ReaderIO value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApReaderIOSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderIO[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApSL(lens, FromReaderIO(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApEitherSL is a lens-based variant of ApEitherS.
|
||||||
|
// It combines a lens with an Either value using applicative composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - fa: An Either value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApEitherSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa Result[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApSL(lens, FromEither(fa))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApResultSL is a lens-based variant of ApResultS.
|
||||||
|
// This is an alias for ApEitherSL for consistency with the Result naming convention.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A lens focusing on field T within state S
|
||||||
|
// - fa: A Result value
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApResultSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa Result[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApSL(lens, FromResult(fa))
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -26,11 +26,11 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getLastName(s utils.Initial) ReaderIOEither[string] {
|
func getLastName(s utils.Initial) ReaderIOResult[string] {
|
||||||
return Of("Doe")
|
return Of("Doe")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGivenName(s utils.WithLastName) ReaderIOEither[string] {
|
func getGivenName(s utils.WithLastName) ReaderIOResult[string] {
|
||||||
return Of("John")
|
return Of("John")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ func TestApS_ChainedWithBind(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDependentValue := func(s State) ReaderIOEither[string] {
|
getDependentValue := func(s State) ReaderIOResult[string] {
|
||||||
// This depends on the Independent field
|
// This depends on the Independent field
|
||||||
return Of(s.Independent + "-dependent")
|
return Of(s.Independent + "-dependent")
|
||||||
}
|
}
|
||||||
@@ -13,13 +13,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
RIOR "github.com/IBM/fp-go/v2/readerioresult"
|
||||||
|
|
||||||
"github.com/IBM/fp-go/v2/internal/bracket"
|
|
||||||
"github.com/IBM/fp-go/v2/readerio"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||||
@@ -27,18 +24,9 @@ import (
|
|||||||
func Bracket[
|
func Bracket[
|
||||||
A, B, ANY any](
|
A, B, ANY any](
|
||||||
|
|
||||||
acquire ReaderIOEither[A],
|
acquire ReaderIOResult[A],
|
||||||
use Kleisli[A, B],
|
use Kleisli[A, B],
|
||||||
release func(A, Either[B]) ReaderIOEither[ANY],
|
release func(A, Either[B]) ReaderIOResult[ANY],
|
||||||
) ReaderIOEither[B] {
|
) ReaderIOResult[B] {
|
||||||
return bracket.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY], Either[B], A, B](
|
return RIOR.Bracket(acquire, use, release)
|
||||||
readerio.Of[context.Context, Either[B]],
|
|
||||||
MonadChain[A, B],
|
|
||||||
readerio.MonadChain[context.Context, Either[B], Either[B]],
|
|
||||||
MonadChain[ANY, B],
|
|
||||||
|
|
||||||
acquire,
|
|
||||||
use,
|
|
||||||
release,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@@ -13,26 +13,26 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
CIOE "github.com/IBM/fp-go/v2/context/ioeither"
|
CIOE "github.com/IBM/fp-go/v2/context/ioresult"
|
||||||
"github.com/IBM/fp-go/v2/ioeither"
|
"github.com/IBM/fp-go/v2/ioeither"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithContext wraps an existing [ReaderIOEither] and performs a context check for cancellation before delegating.
|
// WithContext wraps an existing [ReaderIOResult] and performs a context check for cancellation before delegating.
|
||||||
// This ensures that if the context is already canceled, the computation short-circuits immediately
|
// This ensures that if the context is already canceled, the computation short-circuits immediately
|
||||||
// without executing the wrapped computation.
|
// without executing the wrapped computation.
|
||||||
//
|
//
|
||||||
// This is useful for adding cancellation awareness to computations that might not check the context themselves.
|
// This is useful for adding cancellation awareness to computations that might not check the context themselves.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - ma: The ReaderIOEither to wrap with context checking
|
// - ma: The ReaderIOResult to wrap with context checking
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that checks for cancellation before executing.
|
// Returns a ReaderIOResult that checks for cancellation before executing.
|
||||||
func WithContext[A any](ma ReaderIOEither[A]) ReaderIOEither[A] {
|
func WithContext[A any](ma ReaderIOResult[A]) ReaderIOResult[A] {
|
||||||
return func(ctx context.Context) IOEither[A] {
|
return func(ctx context.Context) IOEither[A] {
|
||||||
if err := context.Cause(ctx); err != nil {
|
if err := context.Cause(ctx); err != nil {
|
||||||
return ioeither.Left[A](err)
|
return ioeither.Left[A](err)
|
||||||
251
v2/context/readerioresult/coverage.out
Normal file
251
v2/context/readerioresult/coverage.out
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
mode: set
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/bind.go:27.21,29.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/bind.go:35.47,42.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/bind.go:48.47,54.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/bind.go:60.47,66.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/bind.go:71.46,76.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/bind.go:82.47,89.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/bracket.go:33.21,44.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/cancel.go:35.65,36.47 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/cancel.go:36.47,37.44 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/cancel.go:37.44,39.4 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/cancel.go:40.3,40.40 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/eq.go:42.84,44.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:18.91,20.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:24.93,26.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:30.101,32.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:36.103,38.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:43.36,48.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:53.36,58.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:63.36,68.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:71.98,76.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:79.101,84.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:87.101,92.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:95.129,96.68 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:96.68,102.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:106.132,107.68 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:107.68,113.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:117.132,118.68 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:118.68,124.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:129.113,131.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:135.115,137.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:143.40,150.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:156.40,163.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:169.40,176.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:179.126,185.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:188.129,194.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:197.129,203.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:206.185,207.76 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:207.76,215.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:219.188,220.76 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:220.76,228.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:232.188,233.76 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:233.76,241.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:246.125,248.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:252.127,254.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:261.44,270.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:277.44,286.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:293.44,302.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:305.154,312.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:315.157,322.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:325.157,332.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:335.241,336.84 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:336.84,346.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:350.244,351.84 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:351.84,361.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:365.244,366.84 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:366.84,376.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:381.137,383.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:387.139,389.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:397.48,408.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:416.48,427.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:435.48,446.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:449.182,457.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:460.185,468.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:471.185,479.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:482.297,483.92 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:483.92,495.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:499.300,500.92 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:500.92,512.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:516.300,517.92 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:517.92,529.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:534.149,536.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:540.151,542.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:551.52,564.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:573.52,586.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:595.52,608.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:611.210,620.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:623.213,632.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:635.213,644.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:647.353,648.100 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:648.100,662.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:666.356,667.100 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:667.100,681.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:685.356,686.100 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:686.100,700.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:705.161,707.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:711.163,713.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:723.56,738.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:748.56,763.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:773.56,788.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:791.238,801.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:804.241,814.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:817.241,827.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:830.409,831.108 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:831.108,847.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:851.412,852.108 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:852.108,868.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:872.412,873.108 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:873.108,889.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:894.173,896.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:900.175,902.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:913.60,930.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:941.60,958.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:969.60,986.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:989.266,1000.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1003.269,1014.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1017.269,1028.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1031.465,1032.116 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1032.116,1050.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1054.468,1055.116 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1055.116,1073.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1077.468,1078.116 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1078.116,1096.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1101.185,1103.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1107.187,1109.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1121.64,1140.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1152.64,1171.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1183.64,1202.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1205.294,1217.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1220.297,1232.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1235.297,1247.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1250.521,1251.124 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1251.124,1271.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1275.524,1276.124 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1276.124,1296.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1300.524,1301.124 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1301.124,1321.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1326.197,1328.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1332.199,1334.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1347.68,1368.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1381.68,1402.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1415.68,1436.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1439.322,1452.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1455.325,1468.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1471.325,1484.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1487.577,1488.132 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1488.132,1510.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1514.580,1515.132 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1515.132,1537.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1541.580,1542.132 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1542.132,1564.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1569.210,1571.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1575.212,1577.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1591.74,1614.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1628.74,1651.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1665.74,1688.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1691.356,1705.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1708.359,1722.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1725.359,1739.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1742.645,1743.144 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1743.144,1767.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1771.648,1772.144 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1772.144,1796.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1800.648,1801.144 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/gen.go:1801.144,1825.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/monoid.go:36.61,43.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/monoid.go:52.64,59.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/monoid.go:68.64,75.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/monoid.go:85.61,93.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/monoid.go:103.63,108.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:42.55,44.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:52.45,54.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:62.42,64.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:74.78,76.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:85.75,87.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:97.72,99.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:108.69,110.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:120.96,122.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:131.93,133.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:143.101,145.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:154.71,156.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:165.39,167.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:169.93,173.56 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:173.56,174.32 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:174.32,174.47 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:189.98,194.47 3 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:194.47,196.44 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:196.44,198.4 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:200.3,200.27 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:200.27,202.45 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:202.45,204.5 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:207.4,213.47 5 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:227.95,229.17 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:229.17,231.3 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:232.2,232.28 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:243.98,245.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:254.91,256.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:265.94,267.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:276.94,278.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:288.95,290.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:299.73,301.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:307.44,309.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:319.95,321.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:330.95,332.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:342.100,344.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:353.100,355.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:364.116,366.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:375.75,377.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:386.47,388.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:398.51,400.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:406.39,407.47 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:407.47,408.27 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:408.27,411.4 2 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:423.87,425.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:434.87,436.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:446.92,448.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:457.92,459.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:468.115,470.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:479.85,480.54 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:480.54,481.48 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:481.48,482.28 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:482.28,487.12 3 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:488.30,489.22 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:490.23,491.47 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:505.59,511.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:520.66,522.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:531.83,533.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:543.97,545.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:554.64,556.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:566.62,568.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:577.78,579.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:589.80,591.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:600.76,602.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:612.136,614.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:623.91,625.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/reader.go:634.71,636.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/resource.go:58.151,63.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/semigroup.go:39.41,43.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/sync.go:46.78,54.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:31.89,39.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:48.103,56.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:65.71,67.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:75.112,83.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:92.124,100.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:108.94,110.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:120.95,128.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:137.92,145.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:148.106,156.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:165.74,167.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:170.118,178.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:181.115,189.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:192.127,200.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:203.97,205.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:215.95,223.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:232.92,240.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:243.106,251.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:260.74,262.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:265.115,273.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:276.127,284.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:287.118,295.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/traverse.go:304.97,306.2 1 0
|
||||||
@@ -13,13 +13,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package readerioeither provides a specialized version of [readerioeither.ReaderIOEither] that uses
|
// package readerioresult provides a specialized version of [readerioeither.ReaderIOResult] that uses
|
||||||
// [context.Context] as the context type and [error] as the left (error) type. This package is designed
|
// [context.Context] as the context type and [error] as the left (error) type. This package is designed
|
||||||
// for typical Go applications where context-aware, effectful computations with error handling are needed.
|
// for typical Go applications where context-aware, effectful computations with error handling are needed.
|
||||||
//
|
//
|
||||||
// # Core Concept
|
// # Core Concept
|
||||||
//
|
//
|
||||||
// ReaderIOEither[A] represents a computation that:
|
// ReaderIOResult[A] represents a computation that:
|
||||||
// - Depends on a [context.Context] (Reader aspect)
|
// - Depends on a [context.Context] (Reader aspect)
|
||||||
// - Performs side effects (IO aspect)
|
// - Performs side effects (IO aspect)
|
||||||
// - Can fail with an [error] (Either aspect)
|
// - Can fail with an [error] (Either aspect)
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
//
|
//
|
||||||
// The type is defined as:
|
// The type is defined as:
|
||||||
//
|
//
|
||||||
// ReaderIOEither[A] = func(context.Context) func() Either[error, A]
|
// ReaderIOResult[A] = func(context.Context) func() Either[error, A]
|
||||||
//
|
//
|
||||||
// This combines three powerful functional programming concepts:
|
// This combines three powerful functional programming concepts:
|
||||||
// - Reader: Dependency injection via context
|
// - Reader: Dependency injection via context
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
// - [Left]: Create failed computations
|
// - [Left]: Create failed computations
|
||||||
// - [FromEither], [FromIO], [FromIOEither]: Convert from other types
|
// - [FromEither], [FromIO], [FromIOEither]: Convert from other types
|
||||||
// - [TryCatch]: Wrap error-returning functions
|
// - [TryCatch]: Wrap error-returning functions
|
||||||
// - [Eitherize0-10]: Convert standard Go functions to ReaderIOEither
|
// - [Eitherize0-10]: Convert standard Go functions to ReaderIOResult
|
||||||
//
|
//
|
||||||
// Transformation:
|
// Transformation:
|
||||||
// - [Map]: Transform success values
|
// - [Map]: Transform success values
|
||||||
@@ -90,15 +90,15 @@
|
|||||||
// import (
|
// import (
|
||||||
// "context"
|
// "context"
|
||||||
// "fmt"
|
// "fmt"
|
||||||
// RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
// RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
// F "github.com/IBM/fp-go/v2/function"
|
// F "github.com/IBM/fp-go/v2/function"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// // Define a computation that reads from context and may fail
|
// // Define a computation that reads from context and may fail
|
||||||
// func fetchUser(id string) RIOE.ReaderIOEither[User] {
|
// func fetchUser(id string) RIOE.ReaderIOResult[User] {
|
||||||
// return F.Pipe2(
|
// return F.Pipe2(
|
||||||
// RIOE.Ask(),
|
// RIOE.Ask(),
|
||||||
// RIOE.Chain(func(ctx context.Context) RIOE.ReaderIOEither[User] {
|
// RIOE.Chain(func(ctx context.Context) RIOE.ReaderIOResult[User] {
|
||||||
// return RIOE.TryCatch(func(ctx context.Context) func() (User, error) {
|
// return RIOE.TryCatch(func(ctx context.Context) func() (User, error) {
|
||||||
// return func() (User, error) {
|
// return func() (User, error) {
|
||||||
// return userService.Get(ctx, id)
|
// return userService.Get(ctx, id)
|
||||||
@@ -138,8 +138,8 @@
|
|||||||
// openFile("data.txt"),
|
// openFile("data.txt"),
|
||||||
// closeFile,
|
// closeFile,
|
||||||
// ),
|
// ),
|
||||||
// func(use func(func(*os.File) RIOE.ReaderIOEither[string]) RIOE.ReaderIOEither[string]) RIOE.ReaderIOEither[string] {
|
// func(use func(func(*os.File) RIOE.ReaderIOResult[string]) RIOE.ReaderIOResult[string]) RIOE.ReaderIOResult[string] {
|
||||||
// return use(func(file *os.File) RIOE.ReaderIOEither[string] {
|
// return use(func(file *os.File) RIOE.ReaderIOResult[string] {
|
||||||
// return readContent(file)
|
// return readContent(file)
|
||||||
// })
|
// })
|
||||||
// },
|
// },
|
||||||
@@ -166,4 +166,4 @@
|
|||||||
// result := computation(ctx)() // Returns Left with cancellation error
|
// result := computation(ctx)() // Returns Left with cancellation error
|
||||||
//
|
//
|
||||||
//go:generate go run ../.. contextreaderioeither --count 10 --filename gen.go
|
//go:generate go run ../.. contextreaderioeither --count 10 --filename gen.go
|
||||||
package readerioeither
|
package readerioresult
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -22,14 +22,14 @@ import (
|
|||||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Eq implements the equals predicate for values contained in the [ReaderIOEither] monad.
|
// Eq implements the equals predicate for values contained in the [ReaderIOResult] monad.
|
||||||
// It creates an equality checker that can compare two ReaderIOEither values by executing them
|
// It creates an equality checker that can compare two ReaderIOResult values by executing them
|
||||||
// with a given context and comparing their results using the provided Either equality checker.
|
// with a given context and comparing their results using the provided Either equality checker.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - eq: Equality checker for Either[A] values
|
// - eq: Equality checker for Either[A] values
|
||||||
//
|
//
|
||||||
// Returns a function that takes a context and returns an equality checker for ReaderIOEither[A].
|
// Returns a function that takes a context and returns an equality checker for ReaderIOResult[A].
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -41,6 +41,6 @@ import (
|
|||||||
// equal := eqRIE(ctx).Equals(Right[int](42), Right[int](42)) // true
|
// equal := eqRIE(ctx).Equals(Right[int](42), Right[int](42)) // true
|
||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func Eq[A any](eq eq.Eq[Either[A]]) func(context.Context) eq.Eq[ReaderIOEither[A]] {
|
func Eq[A any](eq eq.Eq[Either[A]]) func(context.Context) eq.Eq[ReaderIOResult[A]] {
|
||||||
return RIOE.Eq[context.Context](eq)
|
return RIOE.Eq[context.Context](eq)
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ package exec
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
"github.com/IBM/fp-go/v2/exec"
|
"github.com/IBM/fp-go/v2/exec"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
GE "github.com/IBM/fp-go/v2/internal/exec"
|
GE "github.com/IBM/fp-go/v2/internal/exec"
|
||||||
@@ -30,7 +30,7 @@ var (
|
|||||||
Command = F.Curry3(command)
|
Command = F.Curry3(command)
|
||||||
)
|
)
|
||||||
|
|
||||||
func command(name string, args []string, in []byte) RIOE.ReaderIOEither[exec.CommandOutput] {
|
func command(name string, args []string, in []byte) RIOE.ReaderIOResult[exec.CommandOutput] {
|
||||||
return func(ctx context.Context) IOE.IOEither[error, exec.CommandOutput] {
|
return func(ctx context.Context) IOE.IOEither[error, exec.CommandOutput] {
|
||||||
return IOE.TryCatchError(func() (exec.CommandOutput, error) {
|
return IOE.TryCatchError(func() (exec.CommandOutput, error) {
|
||||||
return GE.Exec(ctx, name, args, in)
|
return GE.Exec(ctx, name, args, in)
|
||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
ET "github.com/IBM/fp-go/v2/either"
|
ET "github.com/IBM/fp-go/v2/either"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/IBM/fp-go/v2/internal/file"
|
"github.com/IBM/fp-go/v2/internal/file"
|
||||||
@@ -44,7 +44,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Close closes an object
|
// Close closes an object
|
||||||
func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
func Close[C io.Closer](c C) RIOE.ReaderIOResult[any] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
c,
|
c,
|
||||||
IOEF.Close[C],
|
IOEF.Close[C],
|
||||||
@@ -53,8 +53,8 @@ func Close[C io.Closer](c C) RIOE.ReaderIOEither[any] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile reads a file in the scope of a context
|
// ReadFile reads a file in the scope of a context
|
||||||
func ReadFile(path string) RIOE.ReaderIOEither[[]byte] {
|
func ReadFile(path string) RIOE.ReaderIOResult[[]byte] {
|
||||||
return RIOE.WithResource[[]byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOEither[[]byte] {
|
return RIOE.WithResource[[]byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOResult[[]byte] {
|
||||||
return func(ctx context.Context) IOE.IOEither[error, []byte] {
|
return func(ctx context.Context) IOE.IOEither[error, []byte] {
|
||||||
return func() ET.Either[error, []byte] {
|
return func() ET.Either[error, []byte] {
|
||||||
return file.ReadAll(ctx, r)
|
return file.ReadAll(ctx, r)
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/v2/context/readerioeither"
|
R "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/IBM/fp-go/v2/io"
|
"github.com/IBM/fp-go/v2/io"
|
||||||
J "github.com/IBM/fp-go/v2/json"
|
J "github.com/IBM/fp-go/v2/json"
|
||||||
@@ -18,7 +18,7 @@ package file
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
IO "github.com/IBM/fp-go/v2/io"
|
IO "github.com/IBM/fp-go/v2/io"
|
||||||
IOF "github.com/IBM/fp-go/v2/io/file"
|
IOF "github.com/IBM/fp-go/v2/io/file"
|
||||||
@@ -38,7 +38,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CreateTemp created a temp file with proper parametrization
|
// CreateTemp created a temp file with proper parametrization
|
||||||
func CreateTemp(dir, pattern string) RIOE.ReaderIOEither[*os.File] {
|
func CreateTemp(dir, pattern string) RIOE.ReaderIOResult[*os.File] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
IOEF.CreateTemp(dir, pattern),
|
IOEF.CreateTemp(dir, pattern),
|
||||||
RIOE.FromIOEither[*os.File],
|
RIOE.FromIOEither[*os.File],
|
||||||
@@ -47,6 +47,6 @@ func CreateTemp(dir, pattern string) RIOE.ReaderIOEither[*os.File] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithTempFile creates a temporary file, then invokes a callback to create a resource based on the file, then close and remove the temp file
|
// 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) RIOE.ReaderIOEither[A]) RIOE.ReaderIOEither[A] {
|
func WithTempFile[A any](f func(*os.File) RIOE.ReaderIOResult[A]) RIOE.ReaderIOResult[A] {
|
||||||
return RIOE.WithResource[A](onCreateTempFile, onReleaseTempFile)(f)
|
return RIOE.WithResource[A](onCreateTempFile, onReleaseTempFile)(f)
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -35,7 +35,7 @@ func TestWithTempFile(t *testing.T) {
|
|||||||
|
|
||||||
func TestWithTempFileOnClosedFile(t *testing.T) {
|
func TestWithTempFileOnClosedFile(t *testing.T) {
|
||||||
|
|
||||||
res := WithTempFile(func(f *os.File) RIOE.ReaderIOEither[[]byte] {
|
res := WithTempFile(func(f *os.File) RIOE.ReaderIOResult[[]byte] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
f,
|
f,
|
||||||
onWriteAll[*os.File]([]byte("Carsten")),
|
onWriteAll[*os.File]([]byte("Carsten")),
|
||||||
@@ -19,12 +19,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
)
|
)
|
||||||
|
|
||||||
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
|
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOResult[[]byte] {
|
||||||
return func(w W) RIOE.ReaderIOEither[[]byte] {
|
return func(w W) RIOE.ReaderIOResult[[]byte] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
|
RIOE.TryCatch(func(_ context.Context) func() ([]byte, error) {
|
||||||
return func() ([]byte, error) {
|
return func() ([]byte, error) {
|
||||||
@@ -38,9 +38,9 @@ func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte]
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteAll uses a generator function to create a stream, writes data to it and closes it
|
// 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 RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
func WriteAll[W io.WriteCloser](data []byte) func(acquire RIOE.ReaderIOResult[W]) RIOE.ReaderIOResult[[]byte] {
|
||||||
onWrite := onWriteAll[W](data)
|
onWrite := onWriteAll[W](data)
|
||||||
return func(onCreate RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
|
return func(onCreate RIOE.ReaderIOResult[W]) RIOE.ReaderIOResult[[]byte] {
|
||||||
return RIOE.WithResource[[]byte](
|
return RIOE.WithResource[[]byte](
|
||||||
onCreate,
|
onCreate,
|
||||||
Close[W])(
|
Close[W])(
|
||||||
@@ -50,7 +50,7 @@ func WriteAll[W io.WriteCloser](data []byte) func(acquire RIOE.ReaderIOEither[W]
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write uses a generator function to create a stream, writes data to it and closes it
|
// Write uses a generator function to create a stream, writes data to it and closes it
|
||||||
func Write[R any, W io.WriteCloser](acquire RIOE.ReaderIOEither[W]) func(use func(W) RIOE.ReaderIOEither[R]) RIOE.ReaderIOEither[R] {
|
func Write[R any, W io.WriteCloser](acquire RIOE.ReaderIOResult[W]) func(use func(W) RIOE.ReaderIOResult[R]) RIOE.ReaderIOResult[R] {
|
||||||
return RIOE.WithResource[R](
|
return RIOE.WithResource[R](
|
||||||
acquire,
|
acquire,
|
||||||
Close[W])
|
Close[W])
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -14,12 +14,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package builder provides utilities for building HTTP requests in a functional way
|
// Package builder provides utilities for building HTTP requests in a functional way
|
||||||
// using the ReaderIOEither monad. It integrates with the http/builder package to
|
// using the ReaderIOResult monad. It integrates with the http/builder package to
|
||||||
// create composable, type-safe HTTP request builders with proper error handling
|
// create composable, type-safe HTTP request builders with proper error handling
|
||||||
// and context support.
|
// and context support.
|
||||||
//
|
//
|
||||||
// The main function, Requester, converts a Builder from the http/builder package
|
// The main function, Requester, converts a Builder from the http/builder package
|
||||||
// into a ReaderIOEither that produces HTTP requests. This allows for:
|
// into a ReaderIOResult that produces HTTP requests. This allows for:
|
||||||
// - Immutable request building with method chaining
|
// - Immutable request building with method chaining
|
||||||
// - Automatic header management including Content-Length
|
// - Automatic header management including Content-Length
|
||||||
// - Support for requests with and without bodies
|
// - Support for requests with and without bodies
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
// import (
|
// import (
|
||||||
// "context"
|
// "context"
|
||||||
// B "github.com/IBM/fp-go/v2/http/builder"
|
// B "github.com/IBM/fp-go/v2/http/builder"
|
||||||
// RB "github.com/IBM/fp-go/v2/context/readerioeither/http/builder"
|
// RB "github.com/IBM/fp-go/v2/context/readerioresult/http/builder"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// builder := F.Pipe3(
|
// builder := F.Pipe3(
|
||||||
@@ -51,8 +51,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
RIOEH "github.com/IBM/fp-go/v2/context/readerioeither/http"
|
RIOEH "github.com/IBM/fp-go/v2/context/readerioresult/http"
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
R "github.com/IBM/fp-go/v2/http/builder"
|
R "github.com/IBM/fp-go/v2/http/builder"
|
||||||
@@ -61,7 +61,7 @@ import (
|
|||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Requester converts an http/builder.Builder into a ReaderIOEither that produces HTTP requests.
|
// Requester converts an http/builder.Builder into a ReaderIOResult that produces HTTP requests.
|
||||||
// It handles both requests with and without bodies, automatically managing headers including
|
// It handles both requests with and without bodies, automatically managing headers including
|
||||||
// Content-Length for requests with bodies.
|
// Content-Length for requests with bodies.
|
||||||
//
|
//
|
||||||
@@ -86,14 +86,14 @@ import (
|
|||||||
// - builder: A pointer to an http/builder.Builder containing request configuration
|
// - builder: A pointer to an http/builder.Builder containing request configuration
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A Requester (ReaderIOEither[*http.Request]) that, when executed with a context,
|
// - A Requester (ReaderIOResult[*http.Request]) that, when executed with a context,
|
||||||
// produces either an error or a configured *http.Request
|
// produces either an error or a configured *http.Request
|
||||||
//
|
//
|
||||||
// Example with body:
|
// Example with body:
|
||||||
//
|
//
|
||||||
// import (
|
// import (
|
||||||
// B "github.com/IBM/fp-go/v2/http/builder"
|
// B "github.com/IBM/fp-go/v2/http/builder"
|
||||||
// RB "github.com/IBM/fp-go/v2/context/readerioeither/http/builder"
|
// RB "github.com/IBM/fp-go/v2/context/readerioresult/http/builder"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// builder := F.Pipe3(
|
// builder := F.Pipe3(
|
||||||
@@ -116,7 +116,7 @@ import (
|
|||||||
// result := requester(context.Background())()
|
// result := requester(context.Background())()
|
||||||
func Requester(builder *R.Builder) RIOEH.Requester {
|
func Requester(builder *R.Builder) RIOEH.Requester {
|
||||||
|
|
||||||
withBody := F.Curry3(func(data []byte, url string, method string) RIOE.ReaderIOEither[*http.Request] {
|
withBody := F.Curry3(func(data []byte, url string, method string) RIOE.ReaderIOResult[*http.Request] {
|
||||||
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
|
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
|
||||||
return func() (*http.Request, error) {
|
return func() (*http.Request, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(data))
|
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(data))
|
||||||
@@ -129,7 +129,7 @@ func Requester(builder *R.Builder) RIOEH.Requester {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
withoutBody := F.Curry2(func(url string, method string) RIOE.ReaderIOEither[*http.Request] {
|
withoutBody := F.Curry2(func(url string, method string) RIOE.ReaderIOResult[*http.Request] {
|
||||||
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
|
return RIOE.TryCatch(func(ctx context.Context) func() (*http.Request, error) {
|
||||||
return func() (*http.Request, error) {
|
return func() (*http.Request, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, method, url, nil)
|
req, err := http.NewRequestWithContext(ctx, method, url, nil)
|
||||||
@@ -144,8 +144,8 @@ func Requester(builder *R.Builder) RIOEH.Requester {
|
|||||||
return F.Pipe5(
|
return F.Pipe5(
|
||||||
builder.GetBody(),
|
builder.GetBody(),
|
||||||
O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)),
|
O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)),
|
||||||
E.Ap[func(string) RIOE.ReaderIOEither[*http.Request]](builder.GetTargetURL()),
|
E.Ap[func(string) RIOE.ReaderIOResult[*http.Request]](builder.GetTargetURL()),
|
||||||
E.Flap[error, RIOE.ReaderIOEither[*http.Request]](builder.GetMethod()),
|
E.Flap[error, RIOE.ReaderIOResult[*http.Request]](builder.GetMethod()),
|
||||||
E.GetOrElse(RIOE.Left[*http.Request]),
|
E.GetOrElse(RIOE.Left[*http.Request]),
|
||||||
RIOE.Map(func(req *http.Request) *http.Request {
|
RIOE.Map(func(req *http.Request) *http.Request {
|
||||||
req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders())
|
req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders())
|
||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
R "github.com/IBM/fp-go/v2/http/builder"
|
R "github.com/IBM/fp-go/v2/http/builder"
|
||||||
15
v2/context/readerioresult/http/builder/coverage.out
Normal file
15
v2/context/readerioresult/http/builder/coverage.out
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
mode: set
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:117.52,119.103 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:119.103,120.80 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:120.80,121.41 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:121.41,123.19 2 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:123.19,126.6 2 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:127.5,127.20 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:132.2,132.93 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:132.93,133.80 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:133.80,134.41 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:134.41,136.19 2 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:136.19,138.6 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:139.5,139.20 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:144.2,150.50 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/builder/builder.go:150.50,153.4 2 1
|
||||||
11
v2/context/readerioresult/http/coverage.out
Normal file
11
v2/context/readerioresult/http/coverage.out
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
mode: set
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:111.76,116.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:134.49,136.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:161.90,162.65 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:162.65,166.76 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:166.76,176.5 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:198.73,203.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:222.74,227.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:234.76,236.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:245.74,254.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/context/readerioresult/http/request.go:281.76,286.2 1 1
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package http provides functional HTTP client utilities built on top of ReaderIOEither monad.
|
// Package http provides functional HTTP client utilities built on top of ReaderIOResult monad.
|
||||||
// It offers a composable way to make HTTP requests with context support, error handling,
|
// It offers a composable way to make HTTP requests with context support, error handling,
|
||||||
// and response parsing capabilities. The package follows functional programming principles
|
// and response parsing capabilities. The package follows functional programming principles
|
||||||
// to ensure type-safe, testable, and maintainable HTTP operations.
|
// to ensure type-safe, testable, and maintainable HTTP operations.
|
||||||
@@ -36,7 +36,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
B "github.com/IBM/fp-go/v2/bytes"
|
B "github.com/IBM/fp-go/v2/bytes"
|
||||||
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
|
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
H "github.com/IBM/fp-go/v2/http"
|
H "github.com/IBM/fp-go/v2/http"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
@@ -50,13 +50,13 @@ type (
|
|||||||
// It represents a computation that, given a context, produces either an error
|
// It represents a computation that, given a context, produces either an error
|
||||||
// or an HTTP request. This allows for composable request building with proper
|
// or an HTTP request. This allows for composable request building with proper
|
||||||
// error handling and context propagation.
|
// error handling and context propagation.
|
||||||
Requester = RIOE.ReaderIOEither[*http.Request]
|
Requester = RIOE.ReaderIOResult[*http.Request]
|
||||||
|
|
||||||
// Client is an interface for executing HTTP requests in a functional way.
|
// Client is an interface for executing HTTP requests in a functional way.
|
||||||
// It wraps the standard http.Client and provides a Do method that works
|
// It wraps the standard http.Client and provides a Do method that works
|
||||||
// with the ReaderIOEither monad for composable, type-safe HTTP operations.
|
// with the ReaderIOResult monad for composable, type-safe HTTP operations.
|
||||||
Client interface {
|
Client interface {
|
||||||
// Do executes an HTTP request and returns the response wrapped in a ReaderIOEither.
|
// Do executes an HTTP request and returns the response wrapped in a ReaderIOResult.
|
||||||
// It takes a Requester (which builds the request) and returns a computation that,
|
// It takes a Requester (which builds the request) and returns a computation that,
|
||||||
// when executed with a context, performs the HTTP request and returns either
|
// when executed with a context, performs the HTTP request and returns either
|
||||||
// an error or the HTTP response.
|
// an error or the HTTP response.
|
||||||
@@ -65,8 +65,8 @@ type (
|
|||||||
// - req: A Requester that builds the HTTP request
|
// - req: A Requester that builds the HTTP request
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A ReaderIOEither that produces either an error or an *http.Response
|
// - A ReaderIOResult that produces either an error or an *http.Response
|
||||||
Do(Requester) RIOE.ReaderIOEither[*http.Response]
|
Do(Requester) RIOE.ReaderIOResult[*http.Response]
|
||||||
}
|
}
|
||||||
|
|
||||||
// client is the internal implementation of the Client interface.
|
// client is the internal implementation of the Client interface.
|
||||||
@@ -108,7 +108,7 @@ var (
|
|||||||
MakeGetRequest = makeRequest("GET", nil)
|
MakeGetRequest = makeRequest("GET", nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (client client) Do(req Requester) RIOE.ReaderIOEither[*http.Response] {
|
func (client client) Do(req Requester) RIOE.ReaderIOResult[*http.Response] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
req,
|
req,
|
||||||
RIOE.ChainIOEitherK(client.doIOE),
|
RIOE.ChainIOEitherK(client.doIOE),
|
||||||
@@ -117,7 +117,7 @@ func (client client) Do(req Requester) RIOE.ReaderIOEither[*http.Response] {
|
|||||||
|
|
||||||
// MakeClient creates a functional HTTP client wrapper around a standard http.Client.
|
// MakeClient creates a functional HTTP client wrapper around a standard http.Client.
|
||||||
// The returned Client provides methods for executing HTTP requests in a functional,
|
// The returned Client provides methods for executing HTTP requests in a functional,
|
||||||
// composable way using the ReaderIOEither monad.
|
// composable way using the ReaderIOResult monad.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - httpClient: A standard *http.Client to wrap (e.g., http.DefaultClient)
|
// - httpClient: A standard *http.Client to wrap (e.g., http.DefaultClient)
|
||||||
@@ -149,7 +149,7 @@ func MakeClient(httpClient *http.Client) Client {
|
|||||||
// - client: The HTTP client to use for executing the request
|
// - client: The HTTP client to use for executing the request
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that takes a Requester and returns a ReaderIOEither[FullResponse]
|
// - A function that takes a Requester and returns a ReaderIOResult[FullResponse]
|
||||||
// where FullResponse is a tuple of (*http.Response, []byte)
|
// where FullResponse is a tuple of (*http.Response, []byte)
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
@@ -158,8 +158,8 @@ func MakeClient(httpClient *http.Client) Client {
|
|||||||
// request := MakeGetRequest("https://api.example.com/data")
|
// request := MakeGetRequest("https://api.example.com/data")
|
||||||
// fullResp := ReadFullResponse(client)(request)
|
// fullResp := ReadFullResponse(client)(request)
|
||||||
// result := fullResp(context.Background())()
|
// result := fullResp(context.Background())()
|
||||||
func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullResponse] {
|
func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOResult[H.FullResponse] {
|
||||||
return func(req Requester) RIOE.ReaderIOEither[H.FullResponse] {
|
return func(req Requester) RIOE.ReaderIOResult[H.FullResponse] {
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
client.Do(req),
|
client.Do(req),
|
||||||
IOE.ChainEitherK(H.ValidateResponse),
|
IOE.ChainEitherK(H.ValidateResponse),
|
||||||
@@ -186,7 +186,7 @@ func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullR
|
|||||||
// - client: The HTTP client to use for executing the request
|
// - client: The HTTP client to use for executing the request
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that takes a Requester and returns a ReaderIOEither[[]byte]
|
// - A function that takes a Requester and returns a ReaderIOResult[[]byte]
|
||||||
// containing the response body as bytes
|
// containing the response body as bytes
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
@@ -195,7 +195,7 @@ func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullR
|
|||||||
// request := MakeGetRequest("https://api.example.com/data")
|
// request := MakeGetRequest("https://api.example.com/data")
|
||||||
// readBytes := ReadAll(client)
|
// readBytes := ReadAll(client)
|
||||||
// result := readBytes(request)(context.Background())()
|
// result := readBytes(request)(context.Background())()
|
||||||
func ReadAll(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
func ReadAll(client Client) func(Requester) RIOE.ReaderIOResult[[]byte] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
ReadFullResponse(client),
|
ReadFullResponse(client),
|
||||||
RIOE.Map(H.Body),
|
RIOE.Map(H.Body),
|
||||||
@@ -210,7 +210,7 @@ func ReadAll(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
|||||||
// - client: The HTTP client to use for executing the request
|
// - client: The HTTP client to use for executing the request
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that takes a Requester and returns a ReaderIOEither[string]
|
// - A function that takes a Requester and returns a ReaderIOResult[string]
|
||||||
// containing the response body as a string
|
// containing the response body as a string
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
@@ -219,7 +219,7 @@ func ReadAll(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
|||||||
// request := MakeGetRequest("https://api.example.com/text")
|
// request := MakeGetRequest("https://api.example.com/text")
|
||||||
// readText := ReadText(client)
|
// readText := ReadText(client)
|
||||||
// result := readText(request)(context.Background())()
|
// result := readText(request)(context.Background())()
|
||||||
func ReadText(client Client) func(Requester) RIOE.ReaderIOEither[string] {
|
func ReadText(client Client) func(Requester) RIOE.ReaderIOResult[string] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
ReadAll(client),
|
ReadAll(client),
|
||||||
RIOE.Map(B.ToString),
|
RIOE.Map(B.ToString),
|
||||||
@@ -231,7 +231,7 @@ func ReadText(client Client) func(Requester) RIOE.ReaderIOEither[string] {
|
|||||||
// Deprecated: Use [ReadJSON] instead. This function is kept for backward compatibility
|
// Deprecated: Use [ReadJSON] instead. This function is kept for backward compatibility
|
||||||
// but will be removed in a future version. The capitalized version follows Go naming
|
// but will be removed in a future version. The capitalized version follows Go naming
|
||||||
// conventions for acronyms.
|
// conventions for acronyms.
|
||||||
func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOResult[A] {
|
||||||
return ReadJSON[A](client)
|
return ReadJSON[A](client)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
|||||||
// 3. Reads the response body as bytes
|
// 3. Reads the response body as bytes
|
||||||
//
|
//
|
||||||
// This function is used internally by ReadJSON to ensure proper JSON response handling.
|
// This function is used internally by ReadJSON to ensure proper JSON response handling.
|
||||||
func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
func readJSON(client Client) func(Requester) RIOE.ReaderIOResult[[]byte] {
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
ReadFullResponse(client),
|
ReadFullResponse(client),
|
||||||
RIOE.ChainFirstEitherK(F.Flow2(
|
RIOE.ChainFirstEitherK(F.Flow2(
|
||||||
@@ -264,7 +264,7 @@ func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
|||||||
// - client: The HTTP client to use for executing the request
|
// - client: The HTTP client to use for executing the request
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that takes a Requester and returns a ReaderIOEither[A]
|
// - A function that takes a Requester and returns a ReaderIOResult[A]
|
||||||
// containing the parsed JSON data
|
// containing the parsed JSON data
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
@@ -278,7 +278,7 @@ func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
|
|||||||
// request := MakeGetRequest("https://api.example.com/user/1")
|
// request := MakeGetRequest("https://api.example.com/user/1")
|
||||||
// readUser := ReadJSON[User](client)
|
// readUser := ReadJSON[User](client)
|
||||||
// result := readUser(request)(context.Background())()
|
// result := readUser(request)(context.Background())()
|
||||||
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
|
func ReadJSON[A any](client Client) func(Requester) RIOE.ReaderIOResult[A] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
readJSON(client),
|
readJSON(client),
|
||||||
RIOE.ChainEitherK(J.Unmarshal[A]),
|
RIOE.ChainEitherK(J.Unmarshal[A]),
|
||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
H "net/http"
|
H "net/http"
|
||||||
|
|
||||||
R "github.com/IBM/fp-go/v2/context/readerioeither"
|
R "github.com/IBM/fp-go/v2/context/readerioresult"
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
"github.com/IBM/fp-go/v2/errors"
|
"github.com/IBM/fp-go/v2/errors"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
@@ -66,7 +66,7 @@ func (b simpleRequestBuilder) WithHeader(key, value string) simpleRequestBuilder
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b simpleRequestBuilder) Build() R.ReaderIOEither[*H.Request] {
|
func (b simpleRequestBuilder) Build() R.ReaderIOResult[*H.Request] {
|
||||||
return func(ctx context.Context) IOE.IOEither[error, *H.Request] {
|
return func(ctx context.Context) IOE.IOEither[error, *H.Request] {
|
||||||
return IOE.TryCatchError(func() (*H.Request, error) {
|
return IOE.TryCatchError(func() (*H.Request, error) {
|
||||||
req, err := H.NewRequestWithContext(ctx, b.method, b.url, nil)
|
req, err := H.NewRequestWithContext(ctx, b.method, b.url, nil)
|
||||||
@@ -13,96 +13,75 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/IBM/fp-go/v2/monoid"
|
"github.com/IBM/fp-go/v2/monoid"
|
||||||
|
RIOR "github.com/IBM/fp-go/v2/readerioresult"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Monoid[A any] = monoid.Monoid[ReaderIOEither[A]]
|
Monoid[A any] = monoid.Monoid[ReaderIOResult[A]]
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApplicativeMonoid returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative.
|
// ApplicativeMonoid returns a [Monoid] that concatenates [ReaderIOResult] instances via their applicative.
|
||||||
// This uses the default applicative behavior (parallel or sequential based on useParallel flag).
|
// This uses the default applicative behavior (parallel or sequential based on useParallel flag).
|
||||||
//
|
//
|
||||||
// The monoid combines two ReaderIOEither values by applying the underlying monoid's combine operation
|
// The monoid combines two ReaderIOResult values by applying the underlying monoid's combine operation
|
||||||
// to their success values using applicative application.
|
// to their success values using applicative application.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - m: The underlying monoid for type A
|
// - m: The underlying monoid for type A
|
||||||
//
|
//
|
||||||
// Returns a Monoid for ReaderIOEither[A].
|
// Returns a Monoid for ReaderIOResult[A].
|
||||||
func ApplicativeMonoid[A any](m monoid.Monoid[A]) Monoid[A] {
|
func ApplicativeMonoid[A any](m monoid.Monoid[A]) Monoid[A] {
|
||||||
return monoid.ApplicativeMonoid(
|
return RIOR.ApplicativeMonoid[context.Context](m)
|
||||||
Of[A],
|
|
||||||
MonadMap[A, func(A) A],
|
|
||||||
MonadAp[A, A],
|
|
||||||
m,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplicativeMonoidSeq returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative.
|
// ApplicativeMonoidSeq returns a [Monoid] that concatenates [ReaderIOResult] instances via their applicative.
|
||||||
// This explicitly uses sequential execution for combining values.
|
// This explicitly uses sequential execution for combining values.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - m: The underlying monoid for type A
|
// - m: The underlying monoid for type A
|
||||||
//
|
//
|
||||||
// Returns a Monoid for ReaderIOEither[A] with sequential execution.
|
// Returns a Monoid for ReaderIOResult[A] with sequential execution.
|
||||||
func ApplicativeMonoidSeq[A any](m monoid.Monoid[A]) Monoid[A] {
|
func ApplicativeMonoidSeq[A any](m monoid.Monoid[A]) Monoid[A] {
|
||||||
return monoid.ApplicativeMonoid(
|
return RIOR.ApplicativeMonoidSeq[context.Context](m)
|
||||||
Of[A],
|
|
||||||
MonadMap[A, func(A) A],
|
|
||||||
MonadApSeq[A, A],
|
|
||||||
m,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplicativeMonoidPar returns a [Monoid] that concatenates [ReaderIOEither] instances via their applicative.
|
// ApplicativeMonoidPar returns a [Monoid] that concatenates [ReaderIOResult] instances via their applicative.
|
||||||
// This explicitly uses parallel execution for combining values.
|
// This explicitly uses parallel execution for combining values.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - m: The underlying monoid for type A
|
// - m: The underlying monoid for type A
|
||||||
//
|
//
|
||||||
// Returns a Monoid for ReaderIOEither[A] with parallel execution.
|
// Returns a Monoid for ReaderIOResult[A] with parallel execution.
|
||||||
func ApplicativeMonoidPar[A any](m monoid.Monoid[A]) Monoid[A] {
|
func ApplicativeMonoidPar[A any](m monoid.Monoid[A]) Monoid[A] {
|
||||||
return monoid.ApplicativeMonoid(
|
return RIOR.ApplicativeMonoidPar[context.Context](m)
|
||||||
Of[A],
|
|
||||||
MonadMap[A, func(A) A],
|
|
||||||
MonadApPar[A, A],
|
|
||||||
m,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlternativeMonoid is the alternative [Monoid] for [ReaderIOEither].
|
// AlternativeMonoid is the alternative [Monoid] for [ReaderIOResult].
|
||||||
// This combines ReaderIOEither values using the alternative semantics,
|
// This combines ReaderIOResult values using the alternative semantics,
|
||||||
// where the second value is only evaluated if the first fails.
|
// where the second value is only evaluated if the first fails.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - m: The underlying monoid for type A
|
// - m: The underlying monoid for type A
|
||||||
//
|
//
|
||||||
// Returns a Monoid for ReaderIOEither[A] with alternative semantics.
|
// Returns a Monoid for ReaderIOResult[A] with alternative semantics.
|
||||||
func AlternativeMonoid[A any](m monoid.Monoid[A]) Monoid[A] {
|
func AlternativeMonoid[A any](m monoid.Monoid[A]) Monoid[A] {
|
||||||
return monoid.AlternativeMonoid(
|
return RIOR.AlternativeMonoid[context.Context](m)
|
||||||
Of[A],
|
|
||||||
MonadMap[A, func(A) A],
|
|
||||||
MonadAp[A, A],
|
|
||||||
MonadAlt[A],
|
|
||||||
m,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AltMonoid is the alternative [Monoid] for a [ReaderIOEither].
|
// AltMonoid is the alternative [Monoid] for a [ReaderIOResult].
|
||||||
// This creates a monoid where the empty value is provided lazily,
|
// This creates a monoid where the empty value is provided lazily,
|
||||||
// and combination uses the Alt operation (try first, fallback to second on failure).
|
// and combination uses the Alt operation (try first, fallback to second on failure).
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - zero: Lazy computation that provides the empty/identity value
|
// - zero: Lazy computation that provides the empty/identity value
|
||||||
//
|
//
|
||||||
// Returns a Monoid for ReaderIOEither[A] with Alt-based combination.
|
// Returns a Monoid for ReaderIOResult[A] with Alt-based combination.
|
||||||
func AltMonoid[A any](zero Lazy[ReaderIOEither[A]]) Monoid[A] {
|
func AltMonoid[A any](zero Lazy[ReaderIOResult[A]]) Monoid[A] {
|
||||||
return monoid.AltMonoid(
|
return RIOR.AltMonoid(zero)
|
||||||
zero,
|
|
||||||
MonadAlt[A],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
843
v2/context/readerioresult/reader.go
Normal file
843
v2/context/readerioresult/reader.go
Normal file
@@ -0,0 +1,843 @@
|
|||||||
|
// 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"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/context/readerresult"
|
||||||
|
"github.com/IBM/fp-go/v2/either"
|
||||||
|
"github.com/IBM/fp-go/v2/errors"
|
||||||
|
"github.com/IBM/fp-go/v2/function"
|
||||||
|
"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/reader"
|
||||||
|
"github.com/IBM/fp-go/v2/readerio"
|
||||||
|
RIOR "github.com/IBM/fp-go/v2/readerioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/readeroption"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// useParallel is the feature flag to control if we use the parallel or the sequential implementation of ap
|
||||||
|
useParallel = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// FromEither converts an [Either] into a [ReaderIOResult].
|
||||||
|
// The resulting computation ignores the context and immediately returns the Either value.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - e: The Either value to lift into ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that produces the given Either value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromEither[A any](e Either[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromEither[context.Context](e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromEither converts an [Either] into a [ReaderIOResult].
|
||||||
|
// The resulting computation ignores the context and immediately returns the Either value.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - e: The Either value to lift into ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that produces the given Either value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromResult[A any](e Result[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromEither[context.Context](e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left creates a [ReaderIOResult] that represents a failed computation with the given error.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - l: The error value
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that always fails with the given error.
|
||||||
|
func Left[A any](l error) ReaderIOResult[A] {
|
||||||
|
return RIOR.Left[context.Context, A](l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right creates a [ReaderIOResult] that represents a successful computation with the given value.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - r: The success value
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that always succeeds with the given value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Right[A any](r A) ReaderIOResult[A] {
|
||||||
|
return RIOR.Right[context.Context](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMap transforms the success value of a [ReaderIOResult] using the provided function.
|
||||||
|
// If the computation fails, the error is propagated unchanged.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fa: The ReaderIOResult to transform
|
||||||
|
// - f: The transformation function
|
||||||
|
//
|
||||||
|
// Returns a new ReaderIOResult with the transformed value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadMap[A, B any](fa ReaderIOResult[A], f func(A) B) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadMap(fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map transforms the success value of a [ReaderIOResult] using the provided function.
|
||||||
|
// This is the curried version of [MonadMap], useful for composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: The transformation function
|
||||||
|
//
|
||||||
|
// Returns a function that transforms a ReaderIOResult.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Map[A, B any](f func(A) B) Operator[A, B] {
|
||||||
|
return RIOR.Map[context.Context](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMapTo replaces the success value of a [ReaderIOResult] with a constant value.
|
||||||
|
// If the computation fails, the error is propagated unchanged.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fa: The ReaderIOResult to transform
|
||||||
|
// - b: The constant value to use
|
||||||
|
//
|
||||||
|
// Returns a new ReaderIOResult with the constant value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadMapTo[A, B any](fa ReaderIOResult[A], b B) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadMapTo(fa, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapTo replaces the success value of a [ReaderIOResult] with a constant value.
|
||||||
|
// This is the curried version of [MonadMapTo].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - b: The constant value to use
|
||||||
|
//
|
||||||
|
// Returns a function that transforms a ReaderIOResult.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MapTo[A, B any](b B) Operator[A, B] {
|
||||||
|
return RIOR.MapTo[context.Context, A](b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChain sequences two [ReaderIOResult] computations, where the second depends on the result of the first.
|
||||||
|
// If the first computation fails, the second is not executed.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: The first ReaderIOResult
|
||||||
|
// - f: Function that produces the second ReaderIOResult based on the first's result
|
||||||
|
//
|
||||||
|
// Returns a new ReaderIOResult representing the sequenced computation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChain[A, B any](ma ReaderIOResult[A], f Kleisli[A, B]) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadChain(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain sequences two [ReaderIOResult] computations, where the second depends on the result of the first.
|
||||||
|
// This is the curried version of [MonadChain], useful for composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that produces the second ReaderIOResult based on the first's result
|
||||||
|
//
|
||||||
|
// Returns a function that sequences ReaderIOResult computations.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||||
|
return RIOR.Chain(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainFirst sequences two [ReaderIOResult] computations but returns the result of the first.
|
||||||
|
// The second computation is executed for its side effects only.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: The first ReaderIOResult
|
||||||
|
// - f: Function that produces the second ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult with the result of the first computation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirst[A, B any](ma ReaderIOResult[A], f Kleisli[A, B]) ReaderIOResult[A] {
|
||||||
|
return RIOR.MonadChainFirst(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainFirst sequences two [ReaderIOResult] computations but returns the result of the first.
|
||||||
|
// This is the curried version of [MonadChainFirst].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that produces the second ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns a function that sequences ReaderIOResult computations.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
||||||
|
return RIOR.ChainFirst(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a [ReaderIOResult] that always succeeds with the given value.
|
||||||
|
// This is the same as [Right] and represents the monadic return operation.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - a: The value to wrap
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that always succeeds with the given value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Of[A any](a A) ReaderIOResult[A] {
|
||||||
|
return RIOR.Of[context.Context](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withCancelCauseFunc[A any](cancel context.CancelCauseFunc, ma IOResult[A]) IOResult[A] {
|
||||||
|
return function.Pipe3(
|
||||||
|
ma,
|
||||||
|
ioresult.Swap[A],
|
||||||
|
ioeither.ChainFirstIOK[A](func(err error) func() any {
|
||||||
|
return io.FromImpure(func() { cancel(err) })
|
||||||
|
}),
|
||||||
|
ioeither.Swap[A],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadApPar implements parallel applicative application for [ReaderIOResult].
|
||||||
|
// It executes both computations in parallel and creates a sub-context that will be canceled
|
||||||
|
// if either operation fails. This provides automatic cancellation propagation.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fab: ReaderIOResult containing a function
|
||||||
|
// - fa: ReaderIOResult containing a value
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult with the function applied to the value.
|
||||||
|
func MonadApPar[B, A any](fab ReaderIOResult[func(A) B], fa ReaderIOResult[A]) ReaderIOResult[B] {
|
||||||
|
// context sensitive input
|
||||||
|
cfab := WithContext(fab)
|
||||||
|
cfa := WithContext(fa)
|
||||||
|
|
||||||
|
return func(ctx context.Context) IOResult[B] {
|
||||||
|
// quick check for cancellation
|
||||||
|
if err := context.Cause(ctx); err != nil {
|
||||||
|
return ioeither.Left[B](err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() Result[B] {
|
||||||
|
// quick check for cancellation
|
||||||
|
if err := context.Cause(ctx); err != nil {
|
||||||
|
return either.Left[B](err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create sub-contexts for fa and fab, so they can cancel one other
|
||||||
|
ctxSub, cancelSub := context.WithCancelCause(ctx)
|
||||||
|
defer cancelSub(nil) // cancel has to be called in all paths
|
||||||
|
|
||||||
|
fabIOE := withCancelCauseFunc(cancelSub, cfab(ctxSub))
|
||||||
|
faIOE := withCancelCauseFunc(cancelSub, cfa(ctxSub))
|
||||||
|
|
||||||
|
return ioresult.MonadApPar(fabIOE, faIOE)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadAp implements applicative application for [ReaderIOResult].
|
||||||
|
// By default, it uses parallel execution ([MonadApPar]) but can be configured to use
|
||||||
|
// sequential execution ([MonadApSeq]) via the useParallel constant.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fab: ReaderIOResult containing a function
|
||||||
|
// - fa: ReaderIOResult containing a value
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult with the function applied to the value.
|
||||||
|
func MonadAp[B, A any](fab ReaderIOResult[func(A) B], fa ReaderIOResult[A]) ReaderIOResult[B] {
|
||||||
|
// dispatch to the configured version
|
||||||
|
if useParallel {
|
||||||
|
return MonadApPar(fab, fa)
|
||||||
|
}
|
||||||
|
return MonadApSeq(fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadApSeq implements sequential applicative application for [ReaderIOResult].
|
||||||
|
// It executes the function computation first, then the value computation.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fab: ReaderIOResult containing a function
|
||||||
|
// - fa: ReaderIOResult containing a value
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult with the function applied to the value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadApSeq[B, A any](fab ReaderIOResult[func(A) B], fa ReaderIOResult[A]) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadApSeq(fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ap applies a function wrapped in a [ReaderIOResult] to a value wrapped in a ReaderIOResult.
|
||||||
|
// This is the curried version of [MonadAp], using the default execution mode.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fa: ReaderIOResult containing a value
|
||||||
|
//
|
||||||
|
// Returns a function that applies a ReaderIOResult function to the value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Ap[B, A any](fa ReaderIOResult[A]) Operator[func(A) B, B] {
|
||||||
|
return function.Bind2nd(MonadAp[B, A], fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApSeq applies a function wrapped in a [ReaderIOResult] to a value sequentially.
|
||||||
|
// This is the curried version of [MonadApSeq].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fa: ReaderIOResult containing a value
|
||||||
|
//
|
||||||
|
// Returns a function that applies a ReaderIOResult function to the value sequentially.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApSeq[B, A any](fa ReaderIOResult[A]) Operator[func(A) B, B] {
|
||||||
|
return function.Bind2nd(MonadApSeq[B, A], fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApPar applies a function wrapped in a [ReaderIOResult] to a value in parallel.
|
||||||
|
// This is the curried version of [MonadApPar].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fa: ReaderIOResult containing a value
|
||||||
|
//
|
||||||
|
// Returns a function that applies a ReaderIOResult function to the value in parallel.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApPar[B, A any](fa ReaderIOResult[A]) Operator[func(A) B, B] {
|
||||||
|
return function.Bind2nd(MonadApPar[B, A], fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPredicate creates a [ReaderIOResult] from a predicate function.
|
||||||
|
// If the predicate returns true, the value is wrapped in Right; otherwise, Left with the error from onFalse.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - pred: Predicate function to test the value
|
||||||
|
// - onFalse: Function to generate an error when predicate fails
|
||||||
|
//
|
||||||
|
// Returns a function that converts a value to ReaderIOResult based on the predicate.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A] {
|
||||||
|
return RIOR.FromPredicate[context.Context](pred, onFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrElse provides an alternative [ReaderIOResult] computation if the first one fails.
|
||||||
|
// The alternative is only executed if the first computation results in a Left (error).
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - onLeft: Function that produces an alternative ReaderIOResult from the error
|
||||||
|
//
|
||||||
|
// Returns a function that provides fallback behavior for failed computations.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func OrElse[A any](onLeft Kleisli[error, A]) Operator[A, A] {
|
||||||
|
return RIOR.OrElse(onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask returns a [ReaderIOResult] that provides access to the context.
|
||||||
|
// This is useful for accessing the [context.Context] within a computation.
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that produces the context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Ask() ReaderIOResult[context.Context] {
|
||||||
|
return RIOR.Ask[context.Context]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainEitherK chains a function that returns an [Either] into a [ReaderIOResult] computation.
|
||||||
|
// This is useful for integrating pure Either-returning functions into ReaderIOResult workflows.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: The ReaderIOResult to chain from
|
||||||
|
// - f: Function that produces an Either
|
||||||
|
//
|
||||||
|
// Returns a new ReaderIOResult with the chained computation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainEitherK[A, B any](ma ReaderIOResult[A], f func(A) Either[B]) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadChainEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainEitherK chains a function that returns an [Either] into a [ReaderIOResult] computation.
|
||||||
|
// This is the curried version of [MonadChainEitherK].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that produces an Either
|
||||||
|
//
|
||||||
|
// Returns a function that chains the Either-returning function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainEitherK[A, B any](f func(A) Either[B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainEitherK[context.Context](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainFirstEitherK chains a function that returns an [Either] but keeps the original value.
|
||||||
|
// The Either-returning function is executed for its validation/side effects only.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: The ReaderIOResult to chain from
|
||||||
|
// - f: Function that produces an Either
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult with the original value if both computations succeed.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstEitherK[A, B any](ma ReaderIOResult[A], f func(A) Either[B]) ReaderIOResult[A] {
|
||||||
|
return RIOR.MonadChainFirstEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainFirstEitherK chains a function that returns an [Either] but keeps the original value.
|
||||||
|
// This is the curried version of [MonadChainFirstEitherK].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that produces an Either
|
||||||
|
//
|
||||||
|
// Returns a function that chains the Either-returning function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstEitherK[A, B any](f func(A) Either[B]) Operator[A, A] {
|
||||||
|
return RIOR.ChainFirstEitherK[context.Context](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainOptionK chains a function that returns an [Option] into a [ReaderIOResult] computation.
|
||||||
|
// If the Option is None, the provided error function is called.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - onNone: Function to generate an error when Option is None
|
||||||
|
//
|
||||||
|
// Returns a function that chains Option-returning functions into ReaderIOResult.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainOptionK[context.Context, A, B](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIOEither converts an [IOResult] into a [ReaderIOResult].
|
||||||
|
// The resulting computation ignores the context.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - t: The IOResult to convert
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that executes the IOResult.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromIOEither[A any](t IOResult[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromIOEither[context.Context](t)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func FromIOResult[A any](t IOResult[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromIOResult[context.Context](t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIO converts an [IO] into a [ReaderIOResult].
|
||||||
|
// The IO computation always succeeds, so it's wrapped in Right.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - t: The IO to convert
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that executes the IO and wraps the result in Right.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromIO[A any](t IO[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromIO[context.Context](t)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func FromReader[A any](t Reader[context.Context, A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromReader(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func FromReaderIO[A any](t ReaderIO[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromReaderIO(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromLazy converts a [Lazy] computation into a [ReaderIOResult].
|
||||||
|
// The Lazy computation always succeeds, so it's wrapped in Right.
|
||||||
|
// This is an alias for [FromIO] since Lazy and IO have the same structure.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - t: The Lazy computation to convert
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that executes the Lazy computation and wraps the result in Right.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromLazy[A any](t Lazy[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromIO[context.Context](t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Never returns a [ReaderIOResult] that blocks indefinitely until the context is canceled.
|
||||||
|
// This is useful for creating computations that wait for external cancellation signals.
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that waits for context cancellation and returns the cancellation error.
|
||||||
|
func Never[A any]() ReaderIOResult[A] {
|
||||||
|
return func(ctx context.Context) IOResult[A] {
|
||||||
|
return func() Either[A] {
|
||||||
|
<-ctx.Done()
|
||||||
|
return either.Left[A](context.Cause(ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainIOK chains a function that returns an [IO] into a [ReaderIOResult] computation.
|
||||||
|
// The IO computation always succeeds, so it's wrapped in Right.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: The ReaderIOResult to chain from
|
||||||
|
// - f: Function that produces an IO
|
||||||
|
//
|
||||||
|
// Returns a new ReaderIOResult with the chained IO computation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainIOK[A, B any](ma ReaderIOResult[A], f func(A) IO[B]) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadChainIOK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainIOK chains a function that returns an [IO] into a [ReaderIOResult] computation.
|
||||||
|
// This is the curried version of [MonadChainIOK].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that produces an IO
|
||||||
|
//
|
||||||
|
// Returns a function that chains the IO-returning function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainIOK[A, B any](f func(A) IO[B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainIOK[context.Context](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainFirstIOK chains a function that returns an [IO] but keeps the original value.
|
||||||
|
// The IO computation is executed for its side effects only.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: The ReaderIOResult to chain from
|
||||||
|
// - f: Function that produces an IO
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult with the original value after executing the IO.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstIOK[A, B any](ma ReaderIOResult[A], f func(A) IO[B]) ReaderIOResult[A] {
|
||||||
|
return RIOR.MonadChainFirstIOK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainFirstIOK chains a function that returns an [IO] but keeps the original value.
|
||||||
|
// This is the curried version of [MonadChainFirstIOK].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that produces an IO
|
||||||
|
//
|
||||||
|
// Returns a function that chains the IO-returning function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstIOK[A, B any](f func(A) IO[B]) Operator[A, A] {
|
||||||
|
return RIOR.ChainFirstIOK[context.Context](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainIOEitherK chains a function that returns an [IOResult] into a [ReaderIOResult] computation.
|
||||||
|
// This is useful for integrating IOResult-returning functions into ReaderIOResult workflows.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that produces an IOResult
|
||||||
|
//
|
||||||
|
// Returns a function that chains the IOResult-returning function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainIOEitherK[A, B any](f func(A) IOResult[B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainIOEitherK[context.Context](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay creates an operation that delays execution by the specified duration.
|
||||||
|
// The computation waits for either the delay to expire or the context to be canceled.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - delay: The duration to wait before executing the computation
|
||||||
|
//
|
||||||
|
// Returns a function that delays a ReaderIOResult computation.
|
||||||
|
func Delay[A any](delay time.Duration) Operator[A, A] {
|
||||||
|
return func(ma ReaderIOResult[A]) ReaderIOResult[A] {
|
||||||
|
return func(ctx context.Context) IOResult[A] {
|
||||||
|
return func() Either[A] {
|
||||||
|
// manage the timeout
|
||||||
|
timeoutCtx, cancelTimeout := context.WithTimeout(ctx, delay)
|
||||||
|
defer cancelTimeout()
|
||||||
|
// whatever comes first
|
||||||
|
select {
|
||||||
|
case <-timeoutCtx.Done():
|
||||||
|
return ma(ctx)()
|
||||||
|
case <-ctx.Done():
|
||||||
|
return either.Left[A](context.Cause(ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer returns the current time after waiting for the specified delay.
|
||||||
|
// This is useful for creating time-based computations.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - delay: The duration to wait before returning the time
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that produces the current time after the delay.
|
||||||
|
func Timer(delay time.Duration) ReaderIOResult[time.Time] {
|
||||||
|
return function.Pipe2(
|
||||||
|
io.Now,
|
||||||
|
FromIO[time.Time],
|
||||||
|
Delay[time.Time](delay),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer creates a [ReaderIOResult] by lazily generating a new computation each time it's executed.
|
||||||
|
// This is useful for creating computations that should be re-evaluated on each execution.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - gen: Lazy generator function that produces a ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that generates a fresh computation on each execution.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Defer[A any](gen Lazy[ReaderIOResult[A]]) ReaderIOResult[A] {
|
||||||
|
return RIOR.Defer(gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryCatch wraps a function that returns a tuple (value) into a [ReaderIOResult].
|
||||||
|
// This is the standard way to convert Go error-returning functions into ReaderIOResult.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Function that takes a context and returns a function producing (value)
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that wraps the error-returning function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOResult[A] {
|
||||||
|
return RIOR.TryCatch(f, errors.IdentityError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadAlt provides an alternative [ReaderIOResult] if the first one fails.
|
||||||
|
// The alternative is lazily evaluated only if needed.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - first: The primary ReaderIOResult to try
|
||||||
|
// - second: Lazy alternative ReaderIOResult to use if first fails
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that tries the first, then the second if first fails.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadAlt[A any](first ReaderIOResult[A], second Lazy[ReaderIOResult[A]]) ReaderIOResult[A] {
|
||||||
|
return RIOR.MonadAlt(first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt provides an alternative [ReaderIOResult] if the first one fails.
|
||||||
|
// This is the curried version of [MonadAlt].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - second: Lazy alternative ReaderIOResult to use if first fails
|
||||||
|
//
|
||||||
|
// Returns a function that provides fallback behavior.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Alt[A any](second Lazy[ReaderIOResult[A]]) Operator[A, A] {
|
||||||
|
return RIOR.Alt(second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memoize computes the value of the provided [ReaderIOResult] monad lazily but exactly once.
|
||||||
|
// The context used to compute the value is the context of the first call, so do not use this
|
||||||
|
// method if the value has a functional dependency on the content of the context.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - rdr: The ReaderIOResult to memoize
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult that caches its result after the first execution.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Memoize[A any](rdr ReaderIOResult[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.Memoize(rdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten converts a nested [ReaderIOResult] into a flat [ReaderIOResult].
|
||||||
|
// This is equivalent to [MonadChain] with the identity function.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - rdr: The nested ReaderIOResult to flatten
|
||||||
|
//
|
||||||
|
// Returns a flattened ReaderIOResult.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Flatten[A any](rdr ReaderIOResult[ReaderIOResult[A]]) ReaderIOResult[A] {
|
||||||
|
return RIOR.Flatten(rdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadFlap applies a value to a function wrapped in a [ReaderIOResult].
|
||||||
|
// This is the reverse of [MonadAp], useful in certain composition scenarios.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - fab: ReaderIOResult containing a function
|
||||||
|
// - a: The value to apply to the function
|
||||||
|
//
|
||||||
|
// Returns a ReaderIOResult with the function applied to the value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadFlap[B, A any](fab ReaderIOResult[func(A) B], a A) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadFlap(fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flap applies a value to a function wrapped in a [ReaderIOResult].
|
||||||
|
// This is the curried version of [MonadFlap].
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - a: The value to apply to the function
|
||||||
|
//
|
||||||
|
// Returns a function that applies the value to a ReaderIOResult function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
||||||
|
return RIOR.Flap[context.Context, B](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold handles both success and error cases of a [ReaderIOResult] by providing handlers for each.
|
||||||
|
// Both handlers return ReaderIOResult, allowing for further composition.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - onLeft: Handler for error case
|
||||||
|
// - onRight: Handler for success case
|
||||||
|
//
|
||||||
|
// Returns a function that folds a ReaderIOResult into a new ReaderIOResult.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Fold[A, B any](onLeft Kleisli[error, B], onRight Kleisli[A, B]) Operator[A, B] {
|
||||||
|
return RIOR.Fold(onLeft, onRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrElse extracts the value from a [ReaderIOResult], providing a default via a function if it fails.
|
||||||
|
// The result is a [ReaderIO] that always succeeds.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - onLeft: Function to provide a default value from the error
|
||||||
|
//
|
||||||
|
// Returns a function that converts a ReaderIOResult to a ReaderIO.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func GetOrElse[A any](onLeft func(error) ReaderIO[A]) func(ReaderIOResult[A]) ReaderIO[A] {
|
||||||
|
return RIOR.GetOrElse(onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrLeft transforms the error of a [ReaderIOResult] using the provided function.
|
||||||
|
// The success value is left unchanged.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - onLeft: Function to transform the error
|
||||||
|
//
|
||||||
|
// Returns a function that transforms the error of a ReaderIOResult.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func OrLeft[A any](onLeft func(error) ReaderIO[error]) Operator[A, A] {
|
||||||
|
return RIOR.OrLeft[A](onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func FromReaderEither[A any](ma ReaderEither[context.Context, error, A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromReaderEither(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func FromReaderResult[A any](ma ReaderResult[A]) ReaderIOResult[A] {
|
||||||
|
return RIOR.FromReaderEither(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func FromReaderOption[A any](onNone func() error) Kleisli[ReaderOption[context.Context, A], A] {
|
||||||
|
return RIOR.FromReaderOption[context.Context, A](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func MonadChainReaderK[A, B any](ma ReaderIOResult[A], f reader.Kleisli[context.Context, A, B]) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadChainReaderK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainReaderK[A, B any](f reader.Kleisli[context.Context, A, B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainReaderK(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstReaderK[A, B any](ma ReaderIOResult[A], f reader.Kleisli[context.Context, A, B]) ReaderIOResult[A] {
|
||||||
|
return RIOR.MonadChainFirstReaderK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstReaderK[A, B any](f reader.Kleisli[context.Context, A, B]) Operator[A, A] {
|
||||||
|
return RIOR.ChainFirstReaderK(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func MonadChainReaderResultK[A, B any](ma ReaderIOResult[A], f readerresult.Kleisli[A, B]) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadChainReaderResultK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainReaderResultK[A, B any](f readerresult.Kleisli[A, B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainReaderResultK(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstReaderResultK[A, B any](ma ReaderIOResult[A], f readerresult.Kleisli[A, B]) ReaderIOResult[A] {
|
||||||
|
return RIOR.MonadChainFirstReaderResultK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstReaderResultK[A, B any](f readerresult.Kleisli[A, B]) Operator[A, A] {
|
||||||
|
return RIOR.ChainFirstReaderResultK(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func MonadChainReaderIOK[A, B any](ma ReaderIOResult[A], f readerio.Kleisli[context.Context, A, B]) ReaderIOResult[B] {
|
||||||
|
return RIOR.MonadChainReaderIOK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainReaderIOK[A, B any](f readerio.Kleisli[context.Context, A, B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainReaderIOK(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstReaderIOK[A, B any](ma ReaderIOResult[A], f readerio.Kleisli[context.Context, A, B]) ReaderIOResult[A] {
|
||||||
|
return RIOR.MonadChainFirstReaderIOK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstReaderIOK[A, B any](f readerio.Kleisli[context.Context, A, B]) Operator[A, A] {
|
||||||
|
return RIOR.ChainFirstReaderIOK(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainReaderOptionK[A, B any](onNone func() error) func(readeroption.Kleisli[context.Context, A, B]) Operator[A, B] {
|
||||||
|
return RIOR.ChainReaderOptionK[context.Context, A, B](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstReaderOptionK[A, B any](onNone func() error) func(readeroption.Kleisli[context.Context, A, B]) Operator[A, A] {
|
||||||
|
return RIOR.ChainFirstReaderOptionK[context.Context, A, B](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func Read[A any](r context.Context) func(ReaderIOResult[A]) IOResult[A] {
|
||||||
|
return RIOR.Read[A](r)
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -30,7 +30,7 @@ var (
|
|||||||
benchErr = errors.New("benchmark error")
|
benchErr = errors.New("benchmark error")
|
||||||
benchCtx = context.Background()
|
benchCtx = context.Background()
|
||||||
benchResult Either[int]
|
benchResult Either[int]
|
||||||
benchRIOE ReaderIOEither[int]
|
benchRIOE ReaderIOResult[int]
|
||||||
benchInt int
|
benchInt int
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,14 +45,14 @@ func BenchmarkLeft(b *testing.B) {
|
|||||||
func BenchmarkRight(b *testing.B) {
|
func BenchmarkRight(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = Right[int](42)
|
benchRIOE = Right(42)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkOf(b *testing.B) {
|
func BenchmarkOf(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = Of[int](42)
|
benchRIOE = Of(42)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ func BenchmarkFromIO(b *testing.B) {
|
|||||||
io := func() int { return 42 }
|
io := func() int { return 42 }
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = FromIO[int](io)
|
benchRIOE = FromIO(io)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ func BenchmarkFromIOEither_Left(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark execution
|
// Benchmark execution
|
||||||
func BenchmarkExecute_Right(b *testing.B) {
|
func BenchmarkExecute_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -118,7 +118,7 @@ func BenchmarkExecute_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkExecute_WithContext(b *testing.B) {
|
func BenchmarkExecute_WithContext(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
ctx, cancel := context.WithCancel(benchCtx)
|
ctx, cancel := context.WithCancel(benchCtx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -130,7 +130,7 @@ func BenchmarkExecute_WithContext(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark functor operations
|
// Benchmark functor operations
|
||||||
func BenchmarkMonadMap_Right(b *testing.B) {
|
func BenchmarkMonadMap_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
mapper := func(a int) int { return a * 2 }
|
mapper := func(a int) int { return a * 2 }
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -150,8 +150,8 @@ func BenchmarkMonadMap_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMap_Right(b *testing.B) {
|
func BenchmarkMap_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
mapper := Map[int](func(a int) int { return a * 2 })
|
mapper := Map(func(a int) int { return a * 2 })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -161,7 +161,7 @@ func BenchmarkMap_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMap_Left(b *testing.B) {
|
func BenchmarkMap_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
mapper := Map[int](func(a int) int { return a * 2 })
|
mapper := Map(func(a int) int { return a * 2 })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -170,7 +170,7 @@ func BenchmarkMap_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMapTo_Right(b *testing.B) {
|
func BenchmarkMapTo_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
mapper := MapTo[int](99)
|
mapper := MapTo[int](99)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -181,8 +181,8 @@ func BenchmarkMapTo_Right(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark monad operations
|
// Benchmark monad operations
|
||||||
func BenchmarkMonadChain_Right(b *testing.B) {
|
func BenchmarkMonadChain_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
chainer := func(a int) ReaderIOEither[int] { return Right[int](a * 2) }
|
chainer := func(a int) ReaderIOResult[int] { return Right(a * 2) }
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -192,7 +192,7 @@ func BenchmarkMonadChain_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMonadChain_Left(b *testing.B) {
|
func BenchmarkMonadChain_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
chainer := func(a int) ReaderIOEither[int] { return Right[int](a * 2) }
|
chainer := func(a int) ReaderIOResult[int] { return Right(a * 2) }
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -201,8 +201,8 @@ func BenchmarkMonadChain_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChain_Right(b *testing.B) {
|
func BenchmarkChain_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
chainer := Chain[int](func(a int) ReaderIOEither[int] { return Right[int](a * 2) })
|
chainer := Chain(func(a int) ReaderIOResult[int] { return Right(a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -212,7 +212,7 @@ func BenchmarkChain_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkChain_Left(b *testing.B) {
|
func BenchmarkChain_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
chainer := Chain[int](func(a int) ReaderIOEither[int] { return Right[int](a * 2) })
|
chainer := Chain(func(a int) ReaderIOResult[int] { return Right(a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -221,8 +221,8 @@ func BenchmarkChain_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChainFirst_Right(b *testing.B) {
|
func BenchmarkChainFirst_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
chainer := ChainFirst[int](func(a int) ReaderIOEither[string] { return Right[string]("logged") })
|
chainer := ChainFirst(func(a int) ReaderIOResult[string] { return Right("logged") })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -232,7 +232,7 @@ func BenchmarkChainFirst_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkChainFirst_Left(b *testing.B) {
|
func BenchmarkChainFirst_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
chainer := ChainFirst[int](func(a int) ReaderIOEither[string] { return Right[string]("logged") })
|
chainer := ChainFirst(func(a int) ReaderIOResult[string] { return Right("logged") })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -241,7 +241,7 @@ func BenchmarkChainFirst_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFlatten_Right(b *testing.B) {
|
func BenchmarkFlatten_Right(b *testing.B) {
|
||||||
nested := Right[ReaderIOEither[int]](Right[int](42))
|
nested := Right(Right(42))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -250,7 +250,7 @@ func BenchmarkFlatten_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFlatten_Left(b *testing.B) {
|
func BenchmarkFlatten_Left(b *testing.B) {
|
||||||
nested := Left[ReaderIOEither[int]](benchErr)
|
nested := Left[ReaderIOResult[int]](benchErr)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -260,8 +260,8 @@ func BenchmarkFlatten_Left(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark applicative operations
|
// Benchmark applicative operations
|
||||||
func BenchmarkMonadApSeq_RightRight(b *testing.B) {
|
func BenchmarkMonadApSeq_RightRight(b *testing.B) {
|
||||||
fab := Right[func(int) int](func(a int) int { return a * 2 })
|
fab := Right(func(a int) int { return a * 2 })
|
||||||
fa := Right[int](42)
|
fa := Right(42)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -270,7 +270,7 @@ func BenchmarkMonadApSeq_RightRight(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadApSeq_RightLeft(b *testing.B) {
|
func BenchmarkMonadApSeq_RightLeft(b *testing.B) {
|
||||||
fab := Right[func(int) int](func(a int) int { return a * 2 })
|
fab := Right(func(a int) int { return a * 2 })
|
||||||
fa := Left[int](benchErr)
|
fa := Left[int](benchErr)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -281,7 +281,7 @@ func BenchmarkMonadApSeq_RightLeft(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMonadApSeq_LeftRight(b *testing.B) {
|
func BenchmarkMonadApSeq_LeftRight(b *testing.B) {
|
||||||
fab := Left[func(int) int](benchErr)
|
fab := Left[func(int) int](benchErr)
|
||||||
fa := Right[int](42)
|
fa := Right(42)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -290,8 +290,8 @@ func BenchmarkMonadApSeq_LeftRight(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadApPar_RightRight(b *testing.B) {
|
func BenchmarkMonadApPar_RightRight(b *testing.B) {
|
||||||
fab := Right[func(int) int](func(a int) int { return a * 2 })
|
fab := Right(func(a int) int { return a * 2 })
|
||||||
fa := Right[int](42)
|
fa := Right(42)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -300,7 +300,7 @@ func BenchmarkMonadApPar_RightRight(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadApPar_RightLeft(b *testing.B) {
|
func BenchmarkMonadApPar_RightLeft(b *testing.B) {
|
||||||
fab := Right[func(int) int](func(a int) int { return a * 2 })
|
fab := Right(func(a int) int { return a * 2 })
|
||||||
fa := Left[int](benchErr)
|
fa := Left[int](benchErr)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -311,7 +311,7 @@ func BenchmarkMonadApPar_RightLeft(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMonadApPar_LeftRight(b *testing.B) {
|
func BenchmarkMonadApPar_LeftRight(b *testing.B) {
|
||||||
fab := Left[func(int) int](benchErr)
|
fab := Left[func(int) int](benchErr)
|
||||||
fa := Right[int](42)
|
fa := Right(42)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -321,8 +321,8 @@ func BenchmarkMonadApPar_LeftRight(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark execution of applicative operations
|
// Benchmark execution of applicative operations
|
||||||
func BenchmarkExecuteApSeq_RightRight(b *testing.B) {
|
func BenchmarkExecuteApSeq_RightRight(b *testing.B) {
|
||||||
fab := Right[func(int) int](func(a int) int { return a * 2 })
|
fab := Right(func(a int) int { return a * 2 })
|
||||||
fa := Right[int](42)
|
fa := Right(42)
|
||||||
rioe := MonadApSeq(fab, fa)
|
rioe := MonadApSeq(fab, fa)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -332,8 +332,8 @@ func BenchmarkExecuteApSeq_RightRight(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkExecuteApPar_RightRight(b *testing.B) {
|
func BenchmarkExecuteApPar_RightRight(b *testing.B) {
|
||||||
fab := Right[func(int) int](func(a int) int { return a * 2 })
|
fab := Right(func(a int) int { return a * 2 })
|
||||||
fa := Right[int](42)
|
fa := Right(42)
|
||||||
rioe := MonadApPar(fab, fa)
|
rioe := MonadApPar(fab, fa)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -344,8 +344,8 @@ func BenchmarkExecuteApPar_RightRight(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark alternative operations
|
// Benchmark alternative operations
|
||||||
func BenchmarkAlt_RightRight(b *testing.B) {
|
func BenchmarkAlt_RightRight(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
alternative := Alt[int](func() ReaderIOEither[int] { return Right[int](99) })
|
alternative := Alt(func() ReaderIOResult[int] { return Right(99) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -355,7 +355,7 @@ func BenchmarkAlt_RightRight(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkAlt_LeftRight(b *testing.B) {
|
func BenchmarkAlt_LeftRight(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
alternative := Alt[int](func() ReaderIOEither[int] { return Right[int](99) })
|
alternative := Alt(func() ReaderIOResult[int] { return Right(99) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -364,8 +364,8 @@ func BenchmarkAlt_LeftRight(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkOrElse_Right(b *testing.B) {
|
func BenchmarkOrElse_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
recover := OrElse[int](func(e error) ReaderIOEither[int] { return Right[int](0) })
|
recover := OrElse(func(e error) ReaderIOResult[int] { return Right(0) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -375,7 +375,7 @@ func BenchmarkOrElse_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkOrElse_Left(b *testing.B) {
|
func BenchmarkOrElse_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
recover := OrElse[int](func(e error) ReaderIOEither[int] { return Right[int](0) })
|
recover := OrElse(func(e error) ReaderIOResult[int] { return Right(0) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -385,8 +385,8 @@ func BenchmarkOrElse_Left(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark chain operations with different types
|
// Benchmark chain operations with different types
|
||||||
func BenchmarkChainEitherK_Right(b *testing.B) {
|
func BenchmarkChainEitherK_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
chainer := ChainEitherK[int](func(a int) Either[int] { return E.Right[error](a * 2) })
|
chainer := ChainEitherK(func(a int) Either[int] { return E.Right[error](a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -396,7 +396,7 @@ func BenchmarkChainEitherK_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkChainEitherK_Left(b *testing.B) {
|
func BenchmarkChainEitherK_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
chainer := ChainEitherK[int](func(a int) Either[int] { return E.Right[error](a * 2) })
|
chainer := ChainEitherK(func(a int) Either[int] { return E.Right[error](a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -405,8 +405,8 @@ func BenchmarkChainEitherK_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChainIOK_Right(b *testing.B) {
|
func BenchmarkChainIOK_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
chainer := ChainIOK[int](func(a int) func() int { return func() int { return a * 2 } })
|
chainer := ChainIOK(func(a int) func() int { return func() int { return a * 2 } })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -416,7 +416,7 @@ func BenchmarkChainIOK_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkChainIOK_Left(b *testing.B) {
|
func BenchmarkChainIOK_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
chainer := ChainIOK[int](func(a int) func() int { return func() int { return a * 2 } })
|
chainer := ChainIOK(func(a int) func() int { return func() int { return a * 2 } })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -425,8 +425,8 @@ func BenchmarkChainIOK_Left(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChainIOEitherK_Right(b *testing.B) {
|
func BenchmarkChainIOEitherK_Right(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
chainer := ChainIOEitherK[int](func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
|
chainer := ChainIOEitherK(func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -436,7 +436,7 @@ func BenchmarkChainIOEitherK_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkChainIOEitherK_Left(b *testing.B) {
|
func BenchmarkChainIOEitherK_Left(b *testing.B) {
|
||||||
rioe := Left[int](benchErr)
|
rioe := Left[int](benchErr)
|
||||||
chainer := ChainIOEitherK[int](func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
|
chainer := ChainIOEitherK(func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -453,7 +453,7 @@ func BenchmarkAsk(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkDefer(b *testing.B) {
|
func BenchmarkDefer(b *testing.B) {
|
||||||
gen := func() ReaderIOEither[int] { return Right[int](42) }
|
gen := func() ReaderIOResult[int] { return Right(42) }
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = Defer(gen)
|
benchRIOE = Defer(gen)
|
||||||
@@ -461,7 +461,7 @@ func BenchmarkDefer(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMemoize(b *testing.B) {
|
func BenchmarkMemoize(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = Memoize(rioe)
|
benchRIOE = Memoize(rioe)
|
||||||
@@ -470,7 +470,7 @@ func BenchmarkMemoize(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark delay operations
|
// Benchmark delay operations
|
||||||
func BenchmarkDelay_Construction(b *testing.B) {
|
func BenchmarkDelay_Construction(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = Delay[int](time.Millisecond)(rioe)
|
benchRIOE = Delay[int](time.Millisecond)(rioe)
|
||||||
@@ -531,13 +531,13 @@ func BenchmarkExecuteTryCatch_Error(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark pipeline operations
|
// Benchmark pipeline operations
|
||||||
func BenchmarkPipeline_Map_Right(b *testing.B) {
|
func BenchmarkPipeline_Map_Right(b *testing.B) {
|
||||||
rioe := Right[int](21)
|
rioe := Right(21)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = F.Pipe1(
|
benchRIOE = F.Pipe1(
|
||||||
rioe,
|
rioe,
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -549,19 +549,19 @@ func BenchmarkPipeline_Map_Left(b *testing.B) {
|
|||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = F.Pipe1(
|
benchRIOE = F.Pipe1(
|
||||||
rioe,
|
rioe,
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPipeline_Chain_Right(b *testing.B) {
|
func BenchmarkPipeline_Chain_Right(b *testing.B) {
|
||||||
rioe := Right[int](21)
|
rioe := Right(21)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = F.Pipe1(
|
benchRIOE = F.Pipe1(
|
||||||
rioe,
|
rioe,
|
||||||
Chain[int](func(x int) ReaderIOEither[int] { return Right[int](x * 2) }),
|
Chain(func(x int) ReaderIOResult[int] { return Right(x * 2) }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,21 +573,21 @@ func BenchmarkPipeline_Chain_Left(b *testing.B) {
|
|||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = F.Pipe1(
|
benchRIOE = F.Pipe1(
|
||||||
rioe,
|
rioe,
|
||||||
Chain[int](func(x int) ReaderIOEither[int] { return Right[int](x * 2) }),
|
Chain(func(x int) ReaderIOResult[int] { return Right(x * 2) }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPipeline_Complex_Right(b *testing.B) {
|
func BenchmarkPipeline_Complex_Right(b *testing.B) {
|
||||||
rioe := Right[int](10)
|
rioe := Right(10)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = F.Pipe3(
|
benchRIOE = F.Pipe3(
|
||||||
rioe,
|
rioe,
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
Chain[int](func(x int) ReaderIOEither[int] { return Right[int](x + 1) }),
|
Chain(func(x int) ReaderIOResult[int] { return Right(x + 1) }),
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -599,19 +599,19 @@ func BenchmarkPipeline_Complex_Left(b *testing.B) {
|
|||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchRIOE = F.Pipe3(
|
benchRIOE = F.Pipe3(
|
||||||
rioe,
|
rioe,
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
Chain[int](func(x int) ReaderIOEither[int] { return Right[int](x + 1) }),
|
Chain(func(x int) ReaderIOResult[int] { return Right(x + 1) }),
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkExecutePipeline_Complex_Right(b *testing.B) {
|
func BenchmarkExecutePipeline_Complex_Right(b *testing.B) {
|
||||||
rioe := F.Pipe3(
|
rioe := F.Pipe3(
|
||||||
Right[int](10),
|
Right(10),
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
Chain[int](func(x int) ReaderIOEither[int] { return Right[int](x + 1) }),
|
Chain(func(x int) ReaderIOResult[int] { return Right(x + 1) }),
|
||||||
Map[int](func(x int) int { return x * 2 }),
|
Map(func(x int) int { return x * 2 }),
|
||||||
)
|
)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -632,12 +632,12 @@ func BenchmarkDo(b *testing.B) {
|
|||||||
func BenchmarkBind_Right(b *testing.B) {
|
func BenchmarkBind_Right(b *testing.B) {
|
||||||
type State struct{ value int }
|
type State struct{ value int }
|
||||||
initial := Do(State{})
|
initial := Do(State{})
|
||||||
binder := Bind[State, State](
|
binder := Bind(
|
||||||
func(v int) func(State) State {
|
func(v int) func(State) State {
|
||||||
return func(s State) State { return State{value: v} }
|
return func(s State) State { return State{value: v} }
|
||||||
},
|
},
|
||||||
func(s State) ReaderIOEither[int] {
|
func(s State) ReaderIOResult[int] {
|
||||||
return Right[int](42)
|
return Right(42)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -649,8 +649,8 @@ func BenchmarkBind_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkLet_Right(b *testing.B) {
|
func BenchmarkLet_Right(b *testing.B) {
|
||||||
type State struct{ value int }
|
type State struct{ value int }
|
||||||
initial := Right[State](State{value: 10})
|
initial := Right(State{value: 10})
|
||||||
letter := Let[State, State](
|
letter := Let(
|
||||||
func(v int) func(State) State {
|
func(v int) func(State) State {
|
||||||
return func(s State) State { return State{value: s.value + v} }
|
return func(s State) State { return State{value: s.value + v} }
|
||||||
},
|
},
|
||||||
@@ -665,12 +665,12 @@ func BenchmarkLet_Right(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkApS_Right(b *testing.B) {
|
func BenchmarkApS_Right(b *testing.B) {
|
||||||
type State struct{ value int }
|
type State struct{ value int }
|
||||||
initial := Right[State](State{value: 10})
|
initial := Right(State{value: 10})
|
||||||
aps := ApS[State, State](
|
aps := ApS(
|
||||||
func(v int) func(State) State {
|
func(v int) func(State) State {
|
||||||
return func(s State) State { return State{value: v} }
|
return func(s State) State { return State{value: v} }
|
||||||
},
|
},
|
||||||
Right[int](42),
|
Right(42),
|
||||||
)
|
)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -682,8 +682,8 @@ func BenchmarkApS_Right(b *testing.B) {
|
|||||||
// Benchmark traverse operations
|
// Benchmark traverse operations
|
||||||
func BenchmarkTraverseArray_Empty(b *testing.B) {
|
func BenchmarkTraverseArray_Empty(b *testing.B) {
|
||||||
arr := []int{}
|
arr := []int{}
|
||||||
traverser := TraverseArray[int](func(x int) ReaderIOEither[int] {
|
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -694,8 +694,8 @@ func BenchmarkTraverseArray_Empty(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkTraverseArray_Small(b *testing.B) {
|
func BenchmarkTraverseArray_Small(b *testing.B) {
|
||||||
arr := []int{1, 2, 3}
|
arr := []int{1, 2, 3}
|
||||||
traverser := TraverseArray[int](func(x int) ReaderIOEither[int] {
|
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -709,8 +709,8 @@ func BenchmarkTraverseArray_Medium(b *testing.B) {
|
|||||||
for i := range arr {
|
for i := range arr {
|
||||||
arr[i] = i
|
arr[i] = i
|
||||||
}
|
}
|
||||||
traverser := TraverseArray[int](func(x int) ReaderIOEither[int] {
|
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -721,8 +721,8 @@ func BenchmarkTraverseArray_Medium(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkTraverseArraySeq_Small(b *testing.B) {
|
func BenchmarkTraverseArraySeq_Small(b *testing.B) {
|
||||||
arr := []int{1, 2, 3}
|
arr := []int{1, 2, 3}
|
||||||
traverser := TraverseArraySeq[int](func(x int) ReaderIOEither[int] {
|
traverser := TraverseArraySeq(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -733,8 +733,8 @@ func BenchmarkTraverseArraySeq_Small(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkTraverseArrayPar_Small(b *testing.B) {
|
func BenchmarkTraverseArrayPar_Small(b *testing.B) {
|
||||||
arr := []int{1, 2, 3}
|
arr := []int{1, 2, 3}
|
||||||
traverser := TraverseArrayPar[int](func(x int) ReaderIOEither[int] {
|
traverser := TraverseArrayPar(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -744,10 +744,10 @@ func BenchmarkTraverseArrayPar_Small(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkSequenceArray_Small(b *testing.B) {
|
func BenchmarkSequenceArray_Small(b *testing.B) {
|
||||||
arr := []ReaderIOEither[int]{
|
arr := []ReaderIOResult[int]{
|
||||||
Right[int](1),
|
Right(1),
|
||||||
Right[int](2),
|
Right(2),
|
||||||
Right[int](3),
|
Right(3),
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -758,8 +758,8 @@ func BenchmarkSequenceArray_Small(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkExecuteTraverseArray_Small(b *testing.B) {
|
func BenchmarkExecuteTraverseArray_Small(b *testing.B) {
|
||||||
arr := []int{1, 2, 3}
|
arr := []int{1, 2, 3}
|
||||||
rioe := TraverseArray[int](func(x int) ReaderIOEither[int] {
|
rioe := TraverseArray(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})(arr)
|
})(arr)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -770,8 +770,8 @@ func BenchmarkExecuteTraverseArray_Small(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkExecuteTraverseArraySeq_Small(b *testing.B) {
|
func BenchmarkExecuteTraverseArraySeq_Small(b *testing.B) {
|
||||||
arr := []int{1, 2, 3}
|
arr := []int{1, 2, 3}
|
||||||
rioe := TraverseArraySeq[int](func(x int) ReaderIOEither[int] {
|
rioe := TraverseArraySeq(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})(arr)
|
})(arr)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -782,8 +782,8 @@ func BenchmarkExecuteTraverseArraySeq_Small(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkExecuteTraverseArrayPar_Small(b *testing.B) {
|
func BenchmarkExecuteTraverseArrayPar_Small(b *testing.B) {
|
||||||
arr := []int{1, 2, 3}
|
arr := []int{1, 2, 3}
|
||||||
rioe := TraverseArrayPar[int](func(x int) ReaderIOEither[int] {
|
rioe := TraverseArrayPar(func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})(arr)
|
})(arr)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -795,8 +795,8 @@ func BenchmarkExecuteTraverseArrayPar_Small(b *testing.B) {
|
|||||||
// Benchmark record operations
|
// Benchmark record operations
|
||||||
func BenchmarkTraverseRecord_Small(b *testing.B) {
|
func BenchmarkTraverseRecord_Small(b *testing.B) {
|
||||||
rec := map[string]int{"a": 1, "b": 2, "c": 3}
|
rec := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
traverser := TraverseRecord[string, int](func(x int) ReaderIOEither[int] {
|
traverser := TraverseRecord[string](func(x int) ReaderIOResult[int] {
|
||||||
return Right[int](x * 2)
|
return Right(x * 2)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -806,10 +806,10 @@ func BenchmarkTraverseRecord_Small(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkSequenceRecord_Small(b *testing.B) {
|
func BenchmarkSequenceRecord_Small(b *testing.B) {
|
||||||
rec := map[string]ReaderIOEither[int]{
|
rec := map[string]ReaderIOResult[int]{
|
||||||
"a": Right[int](1),
|
"a": Right(1),
|
||||||
"b": Right[int](2),
|
"b": Right(2),
|
||||||
"c": Right[int](3),
|
"c": Right(3),
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -820,22 +820,22 @@ func BenchmarkSequenceRecord_Small(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark resource management
|
// Benchmark resource management
|
||||||
func BenchmarkWithResource_Success(b *testing.B) {
|
func BenchmarkWithResource_Success(b *testing.B) {
|
||||||
acquire := Right[int](42)
|
acquire := Right(42)
|
||||||
release := func(int) ReaderIOEither[int] { return Right[int](0) }
|
release := func(int) ReaderIOResult[int] { return Right(0) }
|
||||||
body := func(x int) ReaderIOEither[int] { return Right[int](x * 2) }
|
body := func(x int) ReaderIOResult[int] { return Right(x * 2) }
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = WithResource[int, int, int](acquire, release)(body)
|
_ = WithResource[int](acquire, release)(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkExecuteWithResource_Success(b *testing.B) {
|
func BenchmarkExecuteWithResource_Success(b *testing.B) {
|
||||||
acquire := Right[int](42)
|
acquire := Right(42)
|
||||||
release := func(int) ReaderIOEither[int] { return Right[int](0) }
|
release := func(int) ReaderIOResult[int] { return Right(0) }
|
||||||
body := func(x int) ReaderIOEither[int] { return Right[int](x * 2) }
|
body := func(x int) ReaderIOResult[int] { return Right(x * 2) }
|
||||||
rioe := WithResource[int, int, int](acquire, release)(body)
|
rioe := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -845,10 +845,10 @@ func BenchmarkExecuteWithResource_Success(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkExecuteWithResource_ErrorInBody(b *testing.B) {
|
func BenchmarkExecuteWithResource_ErrorInBody(b *testing.B) {
|
||||||
acquire := Right[int](42)
|
acquire := Right(42)
|
||||||
release := func(int) ReaderIOEither[int] { return Right[int](0) }
|
release := func(int) ReaderIOResult[int] { return Right(0) }
|
||||||
body := func(x int) ReaderIOEither[int] { return Left[int](benchErr) }
|
body := func(x int) ReaderIOResult[int] { return Left[int](benchErr) }
|
||||||
rioe := WithResource[int, int, int](acquire, release)(body)
|
rioe := WithResource[int](acquire, release)(body)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -859,7 +859,7 @@ func BenchmarkExecuteWithResource_ErrorInBody(b *testing.B) {
|
|||||||
|
|
||||||
// Benchmark context cancellation
|
// Benchmark context cancellation
|
||||||
func BenchmarkExecute_CanceledContext(b *testing.B) {
|
func BenchmarkExecute_CanceledContext(b *testing.B) {
|
||||||
rioe := Right[int](42)
|
rioe := Right(42)
|
||||||
ctx, cancel := context.WithCancel(benchCtx)
|
ctx, cancel := context.WithCancel(benchCtx)
|
||||||
cancel() // Cancel immediately
|
cancel() // Cancel immediately
|
||||||
|
|
||||||
@@ -871,8 +871,8 @@ func BenchmarkExecute_CanceledContext(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkExecuteApPar_CanceledContext(b *testing.B) {
|
func BenchmarkExecuteApPar_CanceledContext(b *testing.B) {
|
||||||
fab := Right[func(int) int](func(a int) int { return a * 2 })
|
fab := Right(func(a int) int { return a * 2 })
|
||||||
fa := Right[int](42)
|
fa := Right(42)
|
||||||
rioe := MonadApPar(fab, fa)
|
rioe := MonadApPar(fab, fa)
|
||||||
ctx, cancel := context.WithCancel(benchCtx)
|
ctx, cancel := context.WithCancel(benchCtx)
|
||||||
cancel() // Cancel immediately
|
cancel() // Cancel immediately
|
||||||
@@ -883,5 +883,3 @@ func BenchmarkExecuteApPar_CanceledContext(b *testing.B) {
|
|||||||
benchResult = rioe(ctx)()
|
benchResult = rioe(ctx)()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Made with Bob
|
|
||||||
877
v2/context/readerioresult/reader_extended_test.go
Normal file
877
v2/context/readerioresult/reader_extended_test.go
Normal file
@@ -0,0 +1,877 @@
|
|||||||
|
// 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"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
IOG "github.com/IBM/fp-go/v2/io"
|
||||||
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
R "github.com/IBM/fp-go/v2/reader"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFromEither(t *testing.T) {
|
||||||
|
t.Run("Right value", func(t *testing.T) {
|
||||||
|
either := E.Right[error]("success")
|
||||||
|
result := FromEither(either)
|
||||||
|
assert.Equal(t, E.Right[error]("success"), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Left value", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
either := E.Left[string](err)
|
||||||
|
result := FromEither(either)
|
||||||
|
assert.Equal(t, E.Left[string](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromResult(t *testing.T) {
|
||||||
|
t.Run("Success", func(t *testing.T) {
|
||||||
|
result := FromResult(E.Right[error](42))
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := FromResult(E.Left[int](err))
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLeft(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := Left[string](err)
|
||||||
|
assert.Equal(t, E.Left[string](err), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRight(t *testing.T) {
|
||||||
|
result := Right("success")
|
||||||
|
assert.Equal(t, E.Right[error]("success"), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOf(t *testing.T) {
|
||||||
|
result := Of(42)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadMap(t *testing.T) {
|
||||||
|
t.Run("Map over Right", func(t *testing.T) {
|
||||||
|
result := MonadMap(Of(5), func(x int) int { return x * 2 })
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Map over Left", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := MonadMap(Left[int](err), func(x int) int { return x * 2 })
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
t.Run("Map with success", func(t *testing.T) {
|
||||||
|
mapper := Map(func(x int) int { return x * 2 })
|
||||||
|
result := mapper(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Map with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
mapper := Map(func(x int) int { return x * 2 })
|
||||||
|
result := mapper(Left[int](err))
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadMapTo(t *testing.T) {
|
||||||
|
t.Run("MapTo with success", func(t *testing.T) {
|
||||||
|
result := MonadMapTo(Of("original"), 42)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("MapTo with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := MonadMapTo(Left[string](err), 42)
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapTo(t *testing.T) {
|
||||||
|
mapper := MapTo[string](42)
|
||||||
|
result := mapper(Of("original"))
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChain(t *testing.T) {
|
||||||
|
t.Run("Chain with success", func(t *testing.T) {
|
||||||
|
result := MonadChain(Of(5), func(x int) ReaderIOResult[int] {
|
||||||
|
return Of(x * 2)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Chain with error in first", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := MonadChain(Left[int](err), func(x int) ReaderIOResult[int] {
|
||||||
|
return Of(x * 2)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Chain with error in second", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := MonadChain(Of(5), func(x int) ReaderIOResult[int] {
|
||||||
|
return Left[int](err)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChain(t *testing.T) {
|
||||||
|
chainer := Chain(func(x int) ReaderIOResult[int] {
|
||||||
|
return Of(x * 2)
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainFirst(t *testing.T) {
|
||||||
|
t.Run("ChainFirst keeps first value", func(t *testing.T) {
|
||||||
|
result := MonadChainFirst(Of(5), func(x int) ReaderIOResult[string] {
|
||||||
|
return Of("ignored")
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Right[error](5), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ChainFirst propagates error from second", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := MonadChainFirst(Of(5), func(x int) ReaderIOResult[string] {
|
||||||
|
return Left[string](err)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainFirst(t *testing.T) {
|
||||||
|
chainer := ChainFirst(func(x int) ReaderIOResult[string] {
|
||||||
|
return Of("ignored")
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](5), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadApSeq(t *testing.T) {
|
||||||
|
t.Run("ApSeq with success", func(t *testing.T) {
|
||||||
|
fab := Of(func(x int) int { return x * 2 })
|
||||||
|
fa := Of(5)
|
||||||
|
result := MonadApSeq(fab, fa)
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ApSeq with error in function", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
fab := Left[func(int) int](err)
|
||||||
|
fa := Of(5)
|
||||||
|
result := MonadApSeq(fab, fa)
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ApSeq with error in value", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
fab := Of(func(x int) int { return x * 2 })
|
||||||
|
fa := Left[int](err)
|
||||||
|
result := MonadApSeq(fab, fa)
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApSeq(t *testing.T) {
|
||||||
|
fa := Of(5)
|
||||||
|
fab := Of(func(x int) int { return x * 2 })
|
||||||
|
result := MonadApSeq(fab, fa)
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApPar(t *testing.T) {
|
||||||
|
t.Run("ApPar with success", func(t *testing.T) {
|
||||||
|
fa := Of(5)
|
||||||
|
fab := Of(func(x int) int { return x * 2 })
|
||||||
|
result := MonadApPar(fab, fa)
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ApPar with cancelled context", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
fa := Of(5)
|
||||||
|
fab := Of(func(x int) int { return x * 2 })
|
||||||
|
result := MonadApPar(fab, fa)
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromPredicate(t *testing.T) {
|
||||||
|
t.Run("Predicate true", func(t *testing.T) {
|
||||||
|
pred := FromPredicate(
|
||||||
|
func(x int) bool { return x > 0 },
|
||||||
|
func(x int) error { return fmt.Errorf("value %d is not positive", x) },
|
||||||
|
)
|
||||||
|
result := pred(5)
|
||||||
|
assert.Equal(t, E.Right[error](5), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Predicate false", func(t *testing.T) {
|
||||||
|
pred := FromPredicate(
|
||||||
|
func(x int) bool { return x > 0 },
|
||||||
|
func(x int) error { return fmt.Errorf("value %d is not positive", x) },
|
||||||
|
)
|
||||||
|
result := pred(-5)
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrElse(t *testing.T) {
|
||||||
|
t.Run("OrElse with success", func(t *testing.T) {
|
||||||
|
fallback := OrElse(func(err error) ReaderIOResult[int] {
|
||||||
|
return Of(42)
|
||||||
|
})
|
||||||
|
result := fallback(Of(10))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OrElse with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
fallback := OrElse(func(err error) ReaderIOResult[int] {
|
||||||
|
return Of(42)
|
||||||
|
})
|
||||||
|
result := fallback(Left[int](err))
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsk(t *testing.T) {
|
||||||
|
result := Ask()
|
||||||
|
ctx := context.Background()
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
ctxResult := E.ToOption(res)
|
||||||
|
assert.True(t, O.IsSome(ctxResult))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainEitherK(t *testing.T) {
|
||||||
|
t.Run("ChainEitherK with success", func(t *testing.T) {
|
||||||
|
result := MonadChainEitherK(Of(5), func(x int) Either[int] {
|
||||||
|
return E.Right[error](x * 2)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ChainEitherK with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := MonadChainEitherK(Of(5), func(x int) Either[int] {
|
||||||
|
return E.Left[int](err)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainEitherK(t *testing.T) {
|
||||||
|
chainer := ChainEitherK(func(x int) Either[int] {
|
||||||
|
return E.Right[error](x * 2)
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainFirstEitherK(t *testing.T) {
|
||||||
|
t.Run("ChainFirstEitherK keeps first value", func(t *testing.T) {
|
||||||
|
result := MonadChainFirstEitherK(Of(5), func(x int) Either[string] {
|
||||||
|
return E.Right[error]("ignored")
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Right[error](5), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ChainFirstEitherK propagates error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := MonadChainFirstEitherK(Of(5), func(x int) Either[string] {
|
||||||
|
return E.Left[string](err)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainFirstEitherK(t *testing.T) {
|
||||||
|
chainer := ChainFirstEitherK(func(x int) Either[string] {
|
||||||
|
return E.Right[error]("ignored")
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](5), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainOptionK(t *testing.T) {
|
||||||
|
t.Run("ChainOptionK with Some", func(t *testing.T) {
|
||||||
|
chainer := ChainOptionK[int, int](func() error {
|
||||||
|
return errors.New("none error")
|
||||||
|
})(func(x int) Option[int] {
|
||||||
|
return O.Some(x * 2)
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ChainOptionK with None", func(t *testing.T) {
|
||||||
|
chainer := ChainOptionK[int, int](func() error {
|
||||||
|
return errors.New("none error")
|
||||||
|
})(func(x int) Option[int] {
|
||||||
|
return O.None[int]()
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromIOEither(t *testing.T) {
|
||||||
|
t.Run("FromIOEither with success", func(t *testing.T) {
|
||||||
|
ioe := IOE.Of[error](42)
|
||||||
|
result := FromIOEither(ioe)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FromIOEither with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
ioe := IOE.Left[int](err)
|
||||||
|
result := FromIOEither(ioe)
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromIOResult(t *testing.T) {
|
||||||
|
ioe := IOE.Of[error](42)
|
||||||
|
result := FromIOResult(ioe)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromIO(t *testing.T) {
|
||||||
|
io := IOG.Of(42)
|
||||||
|
result := FromIO(io)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromReader(t *testing.T) {
|
||||||
|
reader := R.Of[context.Context](42)
|
||||||
|
result := FromReader(reader)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromLazy(t *testing.T) {
|
||||||
|
lazy := func() int { return 42 }
|
||||||
|
result := FromLazy(lazy)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNever(t *testing.T) {
|
||||||
|
t.Run("Never with cancelled context", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
result := Never[int]()
|
||||||
|
|
||||||
|
// Cancel immediately
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Never with timeout", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
result := Never[int]()
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainIOK(t *testing.T) {
|
||||||
|
result := MonadChainIOK(Of(5), func(x int) IOG.IO[int] {
|
||||||
|
return IOG.Of(x * 2)
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainIOK(t *testing.T) {
|
||||||
|
chainer := ChainIOK(func(x int) IOG.IO[int] {
|
||||||
|
return IOG.Of(x * 2)
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainFirstIOK(t *testing.T) {
|
||||||
|
result := MonadChainFirstIOK(Of(5), func(x int) IOG.IO[string] {
|
||||||
|
return IOG.Of("ignored")
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Right[error](5), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainFirstIOK(t *testing.T) {
|
||||||
|
chainer := ChainFirstIOK(func(x int) IOG.IO[string] {
|
||||||
|
return IOG.Of("ignored")
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](5), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainIOEitherK(t *testing.T) {
|
||||||
|
t.Run("ChainIOEitherK with success", func(t *testing.T) {
|
||||||
|
chainer := ChainIOEitherK(func(x int) IOResult[int] {
|
||||||
|
return IOE.Of[error](x * 2)
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ChainIOEitherK with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
chainer := ChainIOEitherK(func(x int) IOResult[int] {
|
||||||
|
return IOE.Left[int](err)
|
||||||
|
})
|
||||||
|
result := chainer(Of(5))
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDelay(t *testing.T) {
|
||||||
|
t.Run("Delay with success", func(t *testing.T) {
|
||||||
|
start := time.Now()
|
||||||
|
delayed := Delay[int](100 * time.Millisecond)
|
||||||
|
result := delayed(Of(42))
|
||||||
|
res := result(context.Background())()
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
assert.GreaterOrEqual(t, elapsed, 100*time.Millisecond)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Delay with cancelled context", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
delayed := Delay[int](100 * time.Millisecond)
|
||||||
|
result := delayed(Of(42))
|
||||||
|
|
||||||
|
// Cancel after starting but before delay completes
|
||||||
|
cancel()
|
||||||
|
res := result(ctx)()
|
||||||
|
|
||||||
|
// The result might be either Left (if cancelled) or Right (if completed before cancel)
|
||||||
|
// This is a race condition, so we just verify it completes
|
||||||
|
assert.True(t, E.IsLeft(res) || E.IsRight(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefer(t *testing.T) {
|
||||||
|
counter := 0
|
||||||
|
deferred := Defer(func() ReaderIOResult[int] {
|
||||||
|
counter++
|
||||||
|
return Of(counter)
|
||||||
|
})
|
||||||
|
|
||||||
|
// First execution
|
||||||
|
res1 := deferred(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res1))
|
||||||
|
|
||||||
|
// Second execution should generate a new computation
|
||||||
|
res2 := deferred(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res2))
|
||||||
|
|
||||||
|
// Counter should be incremented for each execution
|
||||||
|
assert.Equal(t, 2, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTryCatch(t *testing.T) {
|
||||||
|
t.Run("TryCatch with success", func(t *testing.T) {
|
||||||
|
result := TryCatch(func(ctx context.Context) func() (int, error) {
|
||||||
|
return func() (int, error) {
|
||||||
|
return 42, nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("TryCatch with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
result := TryCatch(func(ctx context.Context) func() (int, error) {
|
||||||
|
return func() (int, error) {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
assert.Equal(t, E.Left[int](err), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadAlt(t *testing.T) {
|
||||||
|
t.Run("Alt with first success", func(t *testing.T) {
|
||||||
|
first := Of(42)
|
||||||
|
second := func() ReaderIOResult[int] { return Of(100) }
|
||||||
|
result := MonadAlt(first, second)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Alt with first error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
first := Left[int](err)
|
||||||
|
second := func() ReaderIOResult[int] { return Of(100) }
|
||||||
|
result := MonadAlt(first, second)
|
||||||
|
assert.Equal(t, E.Right[error](100), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAlt(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
alternative := Alt(func() ReaderIOResult[int] { return Of(100) })
|
||||||
|
result := alternative(Left[int](err))
|
||||||
|
assert.Equal(t, E.Right[error](100), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMemoize(t *testing.T) {
|
||||||
|
counter := 0
|
||||||
|
computation := Memoize(FromLazy(func() int {
|
||||||
|
counter++
|
||||||
|
return counter
|
||||||
|
}))
|
||||||
|
|
||||||
|
// First execution
|
||||||
|
res1 := computation(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res1))
|
||||||
|
val1 := E.ToOption(res1)
|
||||||
|
v1, _ := O.Unwrap(val1)
|
||||||
|
assert.Equal(t, 1, v1)
|
||||||
|
|
||||||
|
// Second execution should return cached value
|
||||||
|
res2 := computation(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res2))
|
||||||
|
val2 := E.ToOption(res2)
|
||||||
|
v2, _ := O.Unwrap(val2)
|
||||||
|
assert.Equal(t, 1, v2)
|
||||||
|
|
||||||
|
// Counter should only be incremented once
|
||||||
|
assert.Equal(t, 1, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatten(t *testing.T) {
|
||||||
|
nested := Of(Of(42))
|
||||||
|
result := Flatten(nested)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadFlap(t *testing.T) {
|
||||||
|
fab := Of(func(x int) int { return x * 2 })
|
||||||
|
result := MonadFlap(fab, 5)
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlap(t *testing.T) {
|
||||||
|
flapper := Flap[int](5)
|
||||||
|
result := flapper(Of(func(x int) int { return x * 2 }))
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFold(t *testing.T) {
|
||||||
|
t.Run("Fold with success", func(t *testing.T) {
|
||||||
|
folder := Fold(
|
||||||
|
func(err error) ReaderIOResult[string] {
|
||||||
|
return Of("error: " + err.Error())
|
||||||
|
},
|
||||||
|
func(x int) ReaderIOResult[string] {
|
||||||
|
return Of(fmt.Sprintf("success: %d", x))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
result := folder(Of(42))
|
||||||
|
assert.Equal(t, E.Right[error]("success: 42"), result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Fold with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
folder := Fold(
|
||||||
|
func(err error) ReaderIOResult[string] {
|
||||||
|
return Of("error: " + err.Error())
|
||||||
|
},
|
||||||
|
func(x int) ReaderIOResult[string] {
|
||||||
|
return Of(fmt.Sprintf("success: %d", x))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
result := folder(Left[int](err))
|
||||||
|
assert.Equal(t, E.Right[error]("error: test error"), result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOrElse(t *testing.T) {
|
||||||
|
t.Run("GetOrElse with success", func(t *testing.T) {
|
||||||
|
getter := GetOrElse(func(err error) ReaderIO[int] {
|
||||||
|
return func(ctx context.Context) IOG.IO[int] {
|
||||||
|
return IOG.Of(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
result := getter(Of(42))
|
||||||
|
assert.Equal(t, 42, result(context.Background())())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetOrElse with error", func(t *testing.T) {
|
||||||
|
err := errors.New("test error")
|
||||||
|
getter := GetOrElse(func(err error) ReaderIO[int] {
|
||||||
|
return func(ctx context.Context) IOG.IO[int] {
|
||||||
|
return IOG.Of(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
result := getter(Left[int](err))
|
||||||
|
assert.Equal(t, 0, result(context.Background())())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithContext(t *testing.T) {
|
||||||
|
t.Run("WithContext with valid context", func(t *testing.T) {
|
||||||
|
computation := WithContext(Of(42))
|
||||||
|
result := computation(context.Background())()
|
||||||
|
assert.Equal(t, E.Right[error](42), result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("WithContext with cancelled context", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
computation := WithContext(Of(42))
|
||||||
|
result := computation(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEitherize0(t *testing.T) {
|
||||||
|
f := func(ctx context.Context) (int, error) {
|
||||||
|
return 42, nil
|
||||||
|
}
|
||||||
|
eitherized := Eitherize0(f)
|
||||||
|
result := eitherized()
|
||||||
|
assert.Equal(t, E.Right[error](42), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUneitherize0(t *testing.T) {
|
||||||
|
f := func() ReaderIOResult[int] {
|
||||||
|
return Of(42)
|
||||||
|
}
|
||||||
|
uneitherized := Uneitherize0(f)
|
||||||
|
result, err := uneitherized(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 42, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEitherize1(t *testing.T) {
|
||||||
|
f := func(ctx context.Context, x int) (int, error) {
|
||||||
|
return x * 2, nil
|
||||||
|
}
|
||||||
|
eitherized := Eitherize1(f)
|
||||||
|
result := eitherized(5)
|
||||||
|
assert.Equal(t, E.Right[error](10), result(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUneitherize1(t *testing.T) {
|
||||||
|
f := func(x int) ReaderIOResult[int] {
|
||||||
|
return Of(x * 2)
|
||||||
|
}
|
||||||
|
uneitherized := Uneitherize1(f)
|
||||||
|
result, err := uneitherized(context.Background(), 5)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 10, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceT2(t *testing.T) {
|
||||||
|
result := SequenceT2(Of(1), Of(2))
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
tuple := E.ToOption(res)
|
||||||
|
assert.True(t, O.IsSome(tuple))
|
||||||
|
t1, _ := O.Unwrap(tuple)
|
||||||
|
assert.Equal(t, 1, t1.F1)
|
||||||
|
assert.Equal(t, 2, t1.F2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceSeqT2(t *testing.T) {
|
||||||
|
result := SequenceSeqT2(Of(1), Of(2))
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceParT2(t *testing.T) {
|
||||||
|
result := SequenceParT2(Of(1), Of(2))
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraverseArray(t *testing.T) {
|
||||||
|
t.Run("TraverseArray with success", func(t *testing.T) {
|
||||||
|
arr := []int{1, 2, 3}
|
||||||
|
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
|
||||||
|
return Of(x * 2)
|
||||||
|
})
|
||||||
|
result := traverser(arr)
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
arrOpt := E.ToOption(res)
|
||||||
|
assert.True(t, O.IsSome(arrOpt))
|
||||||
|
resultArr, _ := O.Unwrap(arrOpt)
|
||||||
|
assert.Equal(t, []int{2, 4, 6}, resultArr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("TraverseArray with error", func(t *testing.T) {
|
||||||
|
arr := []int{1, 2, 3}
|
||||||
|
err := errors.New("test error")
|
||||||
|
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
|
||||||
|
if x == 2 {
|
||||||
|
return Left[int](err)
|
||||||
|
}
|
||||||
|
return Of(x * 2)
|
||||||
|
})
|
||||||
|
result := traverser(arr)
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceArray(t *testing.T) {
|
||||||
|
arr := []ReaderIOResult[int]{Of(1), Of(2), Of(3)}
|
||||||
|
result := SequenceArray(arr)
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
arrOpt := E.ToOption(res)
|
||||||
|
assert.True(t, O.IsSome(arrOpt))
|
||||||
|
resultArr, _ := O.Unwrap(arrOpt)
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, resultArr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraverseRecord(t *testing.T) {
|
||||||
|
rec := map[string]int{"a": 1, "b": 2}
|
||||||
|
result := TraverseRecord[string](func(x int) ReaderIOResult[int] {
|
||||||
|
return Of(x * 2)
|
||||||
|
})(rec)
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
recOpt := E.ToOption(res)
|
||||||
|
assert.True(t, O.IsSome(recOpt))
|
||||||
|
resultRec, _ := O.Unwrap(recOpt)
|
||||||
|
assert.Equal(t, 2, resultRec["a"])
|
||||||
|
assert.Equal(t, 4, resultRec["b"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceRecord(t *testing.T) {
|
||||||
|
rec := map[string]ReaderIOResult[int]{
|
||||||
|
"a": Of(1),
|
||||||
|
"b": Of(2),
|
||||||
|
}
|
||||||
|
result := SequenceRecord(rec)
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
recOpt := E.ToOption(res)
|
||||||
|
assert.True(t, O.IsSome(recOpt))
|
||||||
|
resultRec, _ := O.Unwrap(recOpt)
|
||||||
|
assert.Equal(t, 1, resultRec["a"])
|
||||||
|
assert.Equal(t, 2, resultRec["b"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAltSemigroup(t *testing.T) {
|
||||||
|
sg := AltSemigroup[int]()
|
||||||
|
err := errors.New("test error")
|
||||||
|
|
||||||
|
result := sg.Concat(Left[int](err), Of(42))
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.Equal(t, E.Right[error](42), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplicativeMonoid(t *testing.T) {
|
||||||
|
// Test with int addition monoid
|
||||||
|
intAddMonoid := ApplicativeMonoid(M.MakeMonoid(
|
||||||
|
func(a, b int) int { return a + b },
|
||||||
|
0,
|
||||||
|
))
|
||||||
|
|
||||||
|
result := intAddMonoid.Concat(Of(5), Of(10))
|
||||||
|
res := result(context.Background())()
|
||||||
|
assert.Equal(t, E.Right[error](15), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBracket(t *testing.T) {
|
||||||
|
t.Run("Bracket with success", func(t *testing.T) {
|
||||||
|
var acquired, released bool
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
acquired = true
|
||||||
|
return 42
|
||||||
|
})
|
||||||
|
|
||||||
|
use := func(x int) ReaderIOResult[int] {
|
||||||
|
return Of(x * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
release := func(x int, result Either[int]) ReaderIOResult[any] {
|
||||||
|
return FromLazy(func() any {
|
||||||
|
released = true
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Bracket(acquire, use, release)
|
||||||
|
res := result(context.Background())()
|
||||||
|
|
||||||
|
assert.True(t, acquired)
|
||||||
|
assert.True(t, released)
|
||||||
|
assert.Equal(t, E.Right[error](84), res)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Bracket with error in use", func(t *testing.T) {
|
||||||
|
var acquired, released bool
|
||||||
|
err := errors.New("use error")
|
||||||
|
|
||||||
|
acquire := FromLazy(func() int {
|
||||||
|
acquired = true
|
||||||
|
return 42
|
||||||
|
})
|
||||||
|
|
||||||
|
use := func(x int) ReaderIOResult[int] {
|
||||||
|
return Left[int](err)
|
||||||
|
}
|
||||||
|
|
||||||
|
release := func(x int, result Either[int]) ReaderIOResult[any] {
|
||||||
|
return FromLazy(func() any {
|
||||||
|
released = true
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Bracket(acquire, use, release)
|
||||||
|
res := result(context.Background())()
|
||||||
|
|
||||||
|
assert.True(t, acquired)
|
||||||
|
assert.True(t, released)
|
||||||
|
assert.Equal(t, E.Left[int](err), res)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -143,7 +143,7 @@ func TestCanceledApply(t *testing.T) {
|
|||||||
|
|
||||||
applied := F.Pipe1(
|
applied := F.Pipe1(
|
||||||
fct,
|
fct,
|
||||||
Ap[string, string](errValue),
|
Ap[string](errValue),
|
||||||
)
|
)
|
||||||
|
|
||||||
res := applied(context.Background())()
|
res := applied(context.Background())()
|
||||||
@@ -156,7 +156,7 @@ func TestRegularApply(t *testing.T) {
|
|||||||
|
|
||||||
applied := F.Pipe1(
|
applied := F.Pipe1(
|
||||||
fct,
|
fct,
|
||||||
Ap[string, string](value),
|
Ap[string](value),
|
||||||
)
|
)
|
||||||
|
|
||||||
res := applied(context.Background())()
|
res := applied(context.Background())()
|
||||||
@@ -171,14 +171,14 @@ func TestWithResourceNoErrors(t *testing.T) {
|
|||||||
return countAcquire
|
return countAcquire
|
||||||
})
|
})
|
||||||
|
|
||||||
release := func(int) ReaderIOEither[int] {
|
release := func(int) ReaderIOResult[int] {
|
||||||
return FromLazy(func() int {
|
return FromLazy(func() int {
|
||||||
countRelease++
|
countRelease++
|
||||||
return countRelease
|
return countRelease
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
body := func(int) ReaderIOEither[int] {
|
body := func(int) ReaderIOResult[int] {
|
||||||
return FromLazy(func() int {
|
return FromLazy(func() int {
|
||||||
countBody++
|
countBody++
|
||||||
return countBody
|
return countBody
|
||||||
@@ -203,7 +203,7 @@ func TestWithResourceErrorInBody(t *testing.T) {
|
|||||||
return countAcquire
|
return countAcquire
|
||||||
})
|
})
|
||||||
|
|
||||||
release := func(int) ReaderIOEither[int] {
|
release := func(int) ReaderIOResult[int] {
|
||||||
return FromLazy(func() int {
|
return FromLazy(func() int {
|
||||||
countRelease++
|
countRelease++
|
||||||
return countRelease
|
return countRelease
|
||||||
@@ -211,7 +211,7 @@ func TestWithResourceErrorInBody(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := fmt.Errorf("error in body")
|
err := fmt.Errorf("error in body")
|
||||||
body := func(int) ReaderIOEither[int] {
|
body := func(int) ReaderIOResult[int] {
|
||||||
return Left[int](err)
|
return Left[int](err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,14 +231,14 @@ func TestWithResourceErrorInAcquire(t *testing.T) {
|
|||||||
err := fmt.Errorf("error in acquire")
|
err := fmt.Errorf("error in acquire")
|
||||||
acquire := Left[int](err)
|
acquire := Left[int](err)
|
||||||
|
|
||||||
release := func(int) ReaderIOEither[int] {
|
release := func(int) ReaderIOResult[int] {
|
||||||
return FromLazy(func() int {
|
return FromLazy(func() int {
|
||||||
countRelease++
|
countRelease++
|
||||||
return countRelease
|
return countRelease
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
body := func(int) ReaderIOEither[int] {
|
body := func(int) ReaderIOResult[int] {
|
||||||
return FromLazy(func() int {
|
return FromLazy(func() int {
|
||||||
countBody++
|
countBody++
|
||||||
return countBody
|
return countBody
|
||||||
@@ -264,11 +264,11 @@ func TestWithResourceErrorInRelease(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
err := fmt.Errorf("error in release")
|
err := fmt.Errorf("error in release")
|
||||||
release := func(int) ReaderIOEither[int] {
|
release := func(int) ReaderIOResult[int] {
|
||||||
return Left[int](err)
|
return Left[int](err)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := func(int) ReaderIOEither[int] {
|
body := func(int) ReaderIOResult[int] {
|
||||||
return FromLazy(func() int {
|
return FromLazy(func() int {
|
||||||
countBody++
|
countBody++
|
||||||
return countBody
|
return countBody
|
||||||
@@ -13,13 +13,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
RIOR "github.com/IBM/fp-go/v2/readerioresult"
|
||||||
|
|
||||||
"github.com/IBM/fp-go/v2/function"
|
|
||||||
RIE "github.com/IBM/fp-go/v2/readerioeither"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource.
|
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource.
|
||||||
@@ -32,22 +29,22 @@ import (
|
|||||||
// - onRelease: Releases the resource (always called, even on error)
|
// - onRelease: Releases the resource (always called, even on error)
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - onCreate: ReaderIOEither that creates the resource
|
// - onCreate: ReaderIOResult that creates the resource
|
||||||
// - onRelease: Function to release the resource
|
// - onRelease: Function to release the resource
|
||||||
//
|
//
|
||||||
// Returns a function that takes a resource-using function and returns a ReaderIOEither.
|
// Returns a function that takes a resource-using function and returns a ReaderIOResult.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// file := WithResource(
|
// file := WithResource(
|
||||||
// openFile("data.txt"),
|
// openFile("data.txt"),
|
||||||
// func(f *os.File) ReaderIOEither[any] {
|
// func(f *os.File) ReaderIOResult[any] {
|
||||||
// return TryCatch(func(ctx context.Context) func() (any, error) {
|
// return TryCatch(func(ctx context.Context) func() (any, error) {
|
||||||
// return func() (any, error) { return nil, f.Close() }
|
// return func() (any, error) { return nil, f.Close() }
|
||||||
// })
|
// })
|
||||||
// },
|
// },
|
||||||
// )
|
// )
|
||||||
// result := file(func(f *os.File) ReaderIOEither[string] {
|
// result := file(func(f *os.File) ReaderIOResult[string] {
|
||||||
// return TryCatch(func(ctx context.Context) func() (string, error) {
|
// return TryCatch(func(ctx context.Context) func() (string, error) {
|
||||||
// return func() (string, error) {
|
// return func() (string, error) {
|
||||||
// data, err := io.ReadAll(f)
|
// data, err := io.ReadAll(f)
|
||||||
@@ -55,9 +52,6 @@ import (
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
// })
|
// })
|
||||||
func WithResource[A, R, ANY any](onCreate ReaderIOEither[R], onRelease func(R) ReaderIOEither[ANY]) Kleisli[Kleisli[R, A], A] {
|
func WithResource[A, R, ANY any](onCreate ReaderIOResult[R], onRelease Kleisli[R, ANY]) Kleisli[Kleisli[R, A], A] {
|
||||||
return function.Flow2(
|
return RIOR.WithResource[A](onCreate, onRelease)
|
||||||
function.Bind2nd(function.Flow2[func(R) ReaderIOEither[A], Operator[A, A], R, ReaderIOEither[A], ReaderIOEither[A]], WithContext[A]),
|
|
||||||
RIE.WithResource[A, context.Context, error, R](WithContext(onCreate), onRelease),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -37,7 +37,7 @@ var (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
func closeFile(f *os.File) ReaderIOEither[string] {
|
func closeFile(f *os.File) ReaderIOResult[string] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
TryCatch(func(_ context.Context) func() (string, error) {
|
TryCatch(func(_ context.Context) func() (string, error) {
|
||||||
return func() (string, error) {
|
return func() (string, error) {
|
||||||
@@ -52,7 +52,7 @@ func ExampleWithResource() {
|
|||||||
|
|
||||||
stringReader := WithResource[string](openFile("data/file.txt"), closeFile)
|
stringReader := WithResource[string](openFile("data/file.txt"), closeFile)
|
||||||
|
|
||||||
rdr := stringReader(func(f *os.File) ReaderIOEither[string] {
|
rdr := stringReader(func(f *os.File) ReaderIOResult[string] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
TryCatch(func(_ context.Context) func() ([]byte, error) {
|
TryCatch(func(_ context.Context) func() ([]byte, error) {
|
||||||
return func() ([]byte, error) {
|
return func() ([]byte, error) {
|
||||||
@@ -13,21 +13,21 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/semigroup"
|
"github.com/IBM/fp-go/v2/semigroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Semigroup[A any] = semigroup.Semigroup[ReaderIOEither[A]]
|
Semigroup[A any] = semigroup.Semigroup[ReaderIOResult[A]]
|
||||||
)
|
)
|
||||||
|
|
||||||
// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative.
|
// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative.
|
||||||
// This creates a semigroup where combining two ReaderIOEither values means trying the first one,
|
// This creates a semigroup where combining two ReaderIOResult values means trying the first one,
|
||||||
// and if it fails, trying the second one. This is useful for implementing fallback behavior.
|
// and if it fails, trying the second one. This is useful for implementing fallback behavior.
|
||||||
//
|
//
|
||||||
// Returns a Semigroup for ReaderIOEither[A] with Alt-based combination.
|
// Returns a Semigroup for ReaderIOResult[A] with Alt-based combination.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -29,9 +29,9 @@ import (
|
|||||||
// The lock parameter should return a CancelFunc that releases the lock when called.
|
// The lock parameter should return a CancelFunc that releases the lock when called.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - lock: ReaderIOEither that acquires a lock and returns a CancelFunc to release it
|
// - lock: ReaderIOResult that acquires a lock and returns a CancelFunc to release it
|
||||||
//
|
//
|
||||||
// Returns a function that wraps a ReaderIOEither with lock protection.
|
// Returns a function that wraps a ReaderIOResult with lock protection.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -43,9 +43,9 @@ import (
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
// protectedOp := WithLock(lock)(myOperation)
|
// protectedOp := WithLock(lock)(myOperation)
|
||||||
func WithLock[A any](lock ReaderIOEither[context.CancelFunc]) Operator[A, A] {
|
func WithLock[A any](lock ReaderIOResult[context.CancelFunc]) Operator[A, A] {
|
||||||
return function.Flow2(
|
return function.Flow2(
|
||||||
function.Constant1[context.CancelFunc, ReaderIOEither[A]],
|
function.Constant1[context.CancelFunc, ReaderIOResult[A]],
|
||||||
WithResource[A](lock, function.Flow2(
|
WithResource[A](lock, function.Flow2(
|
||||||
io.FromImpure[context.CancelFunc],
|
io.FromImpure[context.CancelFunc],
|
||||||
FromIO[any],
|
FromIO[any],
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/function"
|
"github.com/IBM/fp-go/v2/function"
|
||||||
@@ -21,13 +21,13 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/internal/record"
|
"github.com/IBM/fp-go/v2/internal/record"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TraverseArray transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]].
|
// TraverseArray transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]].
|
||||||
// This uses the default applicative behavior (parallel or sequential based on useParallel flag).
|
// This uses the default applicative behavior (parallel or sequential based on useParallel flag).
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOResult of an array.
|
||||||
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return array.Traverse[[]A](
|
return array.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
@@ -38,14 +38,14 @@ func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndex transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]].
|
// TraverseArrayWithIndex transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]].
|
||||||
// The transformation function receives both the index and the element.
|
// The transformation function receives both the index and the element.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - f: Function that transforms each element with its index into a ReaderIOEither
|
// - f: Function that transforms each element with its index into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOResult of an array.
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
|
||||||
return array.TraverseWithIndex[[]A](
|
return array.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -55,23 +55,23 @@ func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SequenceArray converts a homogeneous sequence of ReaderIOEither into a ReaderIOEither of sequence.
|
// SequenceArray converts a homogeneous sequence of ReaderIOResult into a ReaderIOResult of sequence.
|
||||||
// This is equivalent to TraverseArray with the identity function.
|
// This is equivalent to TraverseArray with the identity function.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - ma: Array of ReaderIOEither values
|
// - ma: Array of ReaderIOResult values
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing an array of values.
|
// Returns a ReaderIOResult containing an array of values.
|
||||||
func SequenceArray[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
func SequenceArray[A any](ma []ReaderIOResult[A]) ReaderIOResult[[]A] {
|
||||||
return TraverseArray(function.Identity[ReaderIOEither[A]])(ma)
|
return TraverseArray(function.Identity[ReaderIOResult[A]])(ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecord transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]].
|
// TraverseRecord transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]].
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - f: Function that transforms each value into a ReaderIOEither
|
// - f: Function that transforms each value into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a function that transforms a map into a ReaderIOEither of a map.
|
// Returns a function that transforms a map into a ReaderIOResult of a map.
|
||||||
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.Traverse[map[K]A](
|
return record.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
@@ -82,14 +82,14 @@ func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, ma
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordWithIndex transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]].
|
// TraverseRecordWithIndex transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]].
|
||||||
// The transformation function receives both the key and the value.
|
// The transformation function receives both the key and the value.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - f: Function that transforms each key-value pair into a ReaderIOEither
|
// - f: Function that transforms each key-value pair into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a function that transforms a map into a ReaderIOEither of a map.
|
// Returns a function that transforms a map into a ReaderIOResult of a map.
|
||||||
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, map[K]B] {
|
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOResult[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.TraverseWithIndex[map[K]A](
|
return record.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -99,26 +99,26 @@ func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SequenceRecord converts a homogeneous map of ReaderIOEither into a ReaderIOEither of map.
|
// SequenceRecord converts a homogeneous map of ReaderIOResult into a ReaderIOResult of map.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - ma: Map of ReaderIOEither values
|
// - ma: Map of ReaderIOResult values
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing a map of values.
|
// Returns a ReaderIOResult containing a map of values.
|
||||||
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOResult[A]) ReaderIOResult[map[K]A] {
|
||||||
return TraverseRecord[K](function.Identity[ReaderIOEither[A]])(ma)
|
return TraverseRecord[K](function.Identity[ReaderIOResult[A]])(ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadTraverseArraySeq transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]].
|
// MonadTraverseArraySeq transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]].
|
||||||
// This explicitly uses sequential execution.
|
// This explicitly uses sequential execution.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - as: The array to traverse
|
// - as: The array to traverse
|
||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing an array of transformed values.
|
// Returns a ReaderIOResult containing an array of transformed values.
|
||||||
func MonadTraverseArraySeq[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B] {
|
func MonadTraverseArraySeq[A, B any](as []A, f Kleisli[A, B]) ReaderIOResult[[]B] {
|
||||||
return array.MonadTraverse[[]A](
|
return array.MonadTraverse(
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
ApSeq[[]B, B],
|
ApSeq[[]B, B],
|
||||||
@@ -127,13 +127,13 @@ func MonadTraverseArraySeq[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArraySeq transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]].
|
// TraverseArraySeq transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]].
|
||||||
// This is the curried version of [MonadTraverseArraySeq] with sequential execution.
|
// This is the curried version of [MonadTraverseArraySeq] with sequential execution.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOResult of an array.
|
||||||
func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return array.Traverse[[]A](
|
return array.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
@@ -144,8 +144,8 @@ func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]]
|
||||||
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []B] {
|
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
|
||||||
return array.TraverseWithIndex[[]A](
|
return array.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -155,20 +155,20 @@ func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) Kleis
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SequenceArraySeq converts a homogeneous sequence of ReaderIOEither into a ReaderIOEither of sequence.
|
// SequenceArraySeq converts a homogeneous sequence of ReaderIOResult into a ReaderIOResult of sequence.
|
||||||
// This explicitly uses sequential execution.
|
// This explicitly uses sequential execution.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - ma: Array of ReaderIOEither values
|
// - ma: Array of ReaderIOResult values
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing an array of values.
|
// Returns a ReaderIOResult containing an array of values.
|
||||||
func SequenceArraySeq[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
func SequenceArraySeq[A any](ma []ReaderIOResult[A]) ReaderIOResult[[]A] {
|
||||||
return MonadTraverseArraySeq(ma, function.Identity[ReaderIOEither[A]])
|
return MonadTraverseArraySeq(ma, function.Identity[ReaderIOResult[A]])
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadTraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// MonadTraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]]
|
||||||
func MonadTraverseRecordSeq[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) ReaderIOEither[map[K]B] {
|
func MonadTraverseRecordSeq[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) ReaderIOResult[map[K]B] {
|
||||||
return record.MonadTraverse[map[K]A](
|
return record.MonadTraverse(
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
ApSeq[map[K]B, B],
|
ApSeq[map[K]B, B],
|
||||||
@@ -177,7 +177,7 @@ func MonadTraverseRecordSeq[K comparable, A, B any](as map[K]A, f Kleisli[A, B])
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]]
|
||||||
func TraverseRecordSeq[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
func TraverseRecordSeq[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.Traverse[map[K]A](
|
return record.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
@@ -188,8 +188,8 @@ func TraverseRecordSeq[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]]
|
||||||
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, map[K]B] {
|
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOResult[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.TraverseWithIndex[map[K]A](
|
return record.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -200,20 +200,20 @@ func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
// SequenceRecordSeq converts a homogeneous sequence of either into an either of sequence
|
||||||
func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOResult[A]) ReaderIOResult[map[K]A] {
|
||||||
return MonadTraverseRecordSeq(ma, function.Identity[ReaderIOEither[A]])
|
return MonadTraverseRecordSeq(ma, function.Identity[ReaderIOResult[A]])
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadTraverseArrayPar transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]].
|
// MonadTraverseArrayPar transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]].
|
||||||
// This explicitly uses parallel execution.
|
// This explicitly uses parallel execution.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - as: The array to traverse
|
// - as: The array to traverse
|
||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing an array of transformed values.
|
// Returns a ReaderIOResult containing an array of transformed values.
|
||||||
func MonadTraverseArrayPar[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B] {
|
func MonadTraverseArrayPar[A, B any](as []A, f Kleisli[A, B]) ReaderIOResult[[]B] {
|
||||||
return array.MonadTraverse[[]A](
|
return array.MonadTraverse(
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
ApPar[[]B, B],
|
ApPar[[]B, B],
|
||||||
@@ -222,13 +222,13 @@ func MonadTraverseArrayPar[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayPar transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]].
|
// TraverseArrayPar transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]].
|
||||||
// This is the curried version of [MonadTraverseArrayPar] with parallel execution.
|
// This is the curried version of [MonadTraverseArrayPar] with parallel execution.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOResult
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOResult of an array.
|
||||||
func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return array.Traverse[[]A](
|
return array.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
@@ -239,8 +239,8 @@ func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]]
|
||||||
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []B] {
|
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
|
||||||
return array.TraverseWithIndex[[]A](
|
return array.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -250,18 +250,18 @@ func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) Kleis
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SequenceArrayPar converts a homogeneous sequence of ReaderIOEither into a ReaderIOEither of sequence.
|
// SequenceArrayPar converts a homogeneous sequence of ReaderIOResult into a ReaderIOResult of sequence.
|
||||||
// This explicitly uses parallel execution.
|
// This explicitly uses parallel execution.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - ma: Array of ReaderIOEither values
|
// - ma: Array of ReaderIOResult values
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing an array of values.
|
// Returns a ReaderIOResult containing an array of values.
|
||||||
func SequenceArrayPar[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
func SequenceArrayPar[A any](ma []ReaderIOResult[A]) ReaderIOResult[[]A] {
|
||||||
return MonadTraverseArrayPar(ma, function.Identity[ReaderIOEither[A]])
|
return MonadTraverseArrayPar(ma, function.Identity[ReaderIOResult[A]])
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]]
|
||||||
func TraverseRecordPar[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
func TraverseRecordPar[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.Traverse[map[K]A](
|
return record.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
@@ -272,8 +272,8 @@ func TraverseRecordPar[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]]
|
||||||
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, map[K]B] {
|
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOResult[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.TraverseWithIndex[map[K]A](
|
return record.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -283,9 +283,9 @@ func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEit
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadTraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// MonadTraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[map[K]B]]
|
||||||
func MonadTraverseRecordPar[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) ReaderIOEither[map[K]B] {
|
func MonadTraverseRecordPar[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) ReaderIOResult[map[K]B] {
|
||||||
return record.MonadTraverse[map[K]A](
|
return record.MonadTraverse(
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
ApPar[map[K]B, B],
|
ApPar[map[K]B, B],
|
||||||
@@ -294,13 +294,13 @@ func MonadTraverseRecordPar[K comparable, A, B any](as map[K]A, f Kleisli[A, B])
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SequenceRecordPar converts a homogeneous map of ReaderIOEither into a ReaderIOEither of map.
|
// SequenceRecordPar converts a homogeneous map of ReaderIOResult into a ReaderIOResult of map.
|
||||||
// This explicitly uses parallel execution.
|
// This explicitly uses parallel execution.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - ma: Map of ReaderIOEither values
|
// - ma: Map of ReaderIOResult values
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing a map of values.
|
// Returns a ReaderIOResult containing a map of values.
|
||||||
func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
|
func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOResult[A]) ReaderIOResult[map[K]A] {
|
||||||
return MonadTraverseRecordPar(ma, function.Identity[ReaderIOEither[A]])
|
return MonadTraverseRecordPar(ma, function.Identity[ReaderIOResult[A]])
|
||||||
}
|
}
|
||||||
@@ -13,19 +13,24 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readerioeither
|
package readerioresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/context/ioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/context/readerresult"
|
||||||
"github.com/IBM/fp-go/v2/either"
|
"github.com/IBM/fp-go/v2/either"
|
||||||
"github.com/IBM/fp-go/v2/io"
|
"github.com/IBM/fp-go/v2/io"
|
||||||
"github.com/IBM/fp-go/v2/ioeither"
|
"github.com/IBM/fp-go/v2/ioeither"
|
||||||
"github.com/IBM/fp-go/v2/lazy"
|
"github.com/IBM/fp-go/v2/lazy"
|
||||||
"github.com/IBM/fp-go/v2/option"
|
"github.com/IBM/fp-go/v2/option"
|
||||||
"github.com/IBM/fp-go/v2/reader"
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
"github.com/IBM/fp-go/v2/readerio"
|
"github.com/IBM/fp-go/v2/readerio"
|
||||||
"github.com/IBM/fp-go/v2/readerioeither"
|
RIOR "github.com/IBM/fp-go/v2/readerioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/readeroption"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -40,6 +45,8 @@ type (
|
|||||||
// Either[A] is equivalent to Either[error, A] from the either package.
|
// Either[A] is equivalent to Either[error, A] from the either package.
|
||||||
Either[A any] = either.Either[error, A]
|
Either[A any] = either.Either[error, A]
|
||||||
|
|
||||||
|
Result[A any] = result.Result[A]
|
||||||
|
|
||||||
// Lazy represents a deferred computation that produces a value of type A when executed.
|
// Lazy represents a deferred computation that produces a value of type A when executed.
|
||||||
// The computation is not executed until explicitly invoked.
|
// The computation is not executed until explicitly invoked.
|
||||||
Lazy[A any] = lazy.Lazy[A]
|
Lazy[A any] = lazy.Lazy[A]
|
||||||
@@ -56,6 +63,8 @@ type (
|
|||||||
// IOEither[A] is equivalent to func() Either[error, A]
|
// IOEither[A] is equivalent to func() Either[error, A]
|
||||||
IOEither[A any] = ioeither.IOEither[error, A]
|
IOEither[A any] = ioeither.IOEither[error, A]
|
||||||
|
|
||||||
|
IOResult[A any] = ioresult.IOResult[A]
|
||||||
|
|
||||||
// Reader represents a computation that depends on a context of type R.
|
// Reader represents a computation that depends on a context of type R.
|
||||||
// This is used for dependency injection and accessing shared context.
|
// This is used for dependency injection and accessing shared context.
|
||||||
//
|
//
|
||||||
@@ -68,21 +77,21 @@ type (
|
|||||||
// ReaderIO[A] is equivalent to func(context.Context) func() A
|
// ReaderIO[A] is equivalent to func(context.Context) func() A
|
||||||
ReaderIO[A any] = readerio.ReaderIO[context.Context, A]
|
ReaderIO[A any] = readerio.ReaderIO[context.Context, A]
|
||||||
|
|
||||||
// ReaderIOEither is the main type of this package. It represents a computation that:
|
// ReaderIOResult is the main type of this package. It represents a computation that:
|
||||||
// - Depends on a [context.Context] (Reader aspect)
|
// - Depends on a [context.Context] (Reader aspect)
|
||||||
// - Performs side effects (IO aspect)
|
// - Performs side effects (IO aspect)
|
||||||
// - Can fail with an [error] (Either aspect)
|
// - Can fail with an [error] (Either aspect)
|
||||||
// - Produces a value of type A on success
|
// - Produces a value of type A on success
|
||||||
//
|
//
|
||||||
// This is a specialization of [readerioeither.ReaderIOEither] with:
|
// This is a specialization of [readerioeither.ReaderIOResult] with:
|
||||||
// - Context type fixed to [context.Context]
|
// - Context type fixed to [context.Context]
|
||||||
// - Error type fixed to [error]
|
// - Error type fixed to [error]
|
||||||
//
|
//
|
||||||
// The type is defined as:
|
// The type is defined as:
|
||||||
// ReaderIOEither[A] = func(context.Context) func() Either[error, A]
|
// ReaderIOResult[A] = func(context.Context) func() Either[error, A]
|
||||||
//
|
//
|
||||||
// Example usage:
|
// Example usage:
|
||||||
// func fetchUser(id string) ReaderIOEither[User] {
|
// func fetchUser(id string) ReaderIOResult[User] {
|
||||||
// return func(ctx context.Context) func() Either[error, User] {
|
// return func(ctx context.Context) func() Either[error, User] {
|
||||||
// return func() Either[error, User] {
|
// return func() Either[error, User] {
|
||||||
// user, err := userService.Get(ctx, id)
|
// user, err := userService.Get(ctx, id)
|
||||||
@@ -97,14 +106,14 @@ type (
|
|||||||
// The computation is executed by providing a context and then invoking the result:
|
// The computation is executed by providing a context and then invoking the result:
|
||||||
// ctx := context.Background()
|
// ctx := context.Background()
|
||||||
// result := fetchUser("123")(ctx)()
|
// result := fetchUser("123")(ctx)()
|
||||||
ReaderIOEither[A any] = readerioeither.ReaderIOEither[context.Context, error, A]
|
ReaderIOResult[A any] = RIOR.ReaderIOResult[context.Context, A]
|
||||||
|
|
||||||
Kleisli[A, B any] = reader.Reader[A, ReaderIOEither[B]]
|
Kleisli[A, B any] = reader.Reader[A, ReaderIOResult[B]]
|
||||||
|
|
||||||
// Operator represents a transformation from one ReaderIOEither to another.
|
// Operator represents a transformation from one ReaderIOResult to another.
|
||||||
// This is useful for point-free style composition and building reusable transformations.
|
// This is useful for point-free style composition and building reusable transformations.
|
||||||
//
|
//
|
||||||
// Operator[A, B] is equivalent to Kleisli[ReaderIOEither[A], B]
|
// Operator[A, B] is equivalent to Kleisli[ReaderIOResult[A], B]
|
||||||
//
|
//
|
||||||
// Example usage:
|
// Example usage:
|
||||||
// // Define a reusable transformation
|
// // Define a reusable transformation
|
||||||
@@ -112,5 +121,9 @@ type (
|
|||||||
//
|
//
|
||||||
// // Apply the transformation
|
// // Apply the transformation
|
||||||
// result := toUpper(computation)
|
// result := toUpper(computation)
|
||||||
Operator[A, B any] = Kleisli[ReaderIOEither[A], B]
|
Operator[A, B any] = Kleisli[ReaderIOResult[A], B]
|
||||||
|
|
||||||
|
ReaderResult[A any] = readerresult.ReaderResult[A]
|
||||||
|
ReaderEither[R, E, A any] = readereither.ReaderEither[R, E, A]
|
||||||
|
ReaderOption[R, A any] = readeroption.ReaderOption[R, A]
|
||||||
)
|
)
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import "github.com/IBM/fp-go/v2/readereither"
|
import "github.com/IBM/fp-go/v2/readereither"
|
||||||
|
|
||||||
@@ -23,11 +23,11 @@ func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndex transforms an array
|
// TraverseArrayWithIndex transforms an array
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) Kleisli[[]A, []B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderResult[B]) Kleisli[[]A, []B] {
|
||||||
return readereither.TraverseArrayWithIndex(f)
|
return readereither.TraverseArrayWithIndex(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
// SequenceArray converts a homogeneous sequence of either into an either of sequence
|
||||||
func SequenceArray[A any](ma []ReaderEither[A]) ReaderEither[[]A] {
|
func SequenceArray[A any](ma []ReaderResult[A]) ReaderResult[[]A] {
|
||||||
return readereither.SequenceArray(ma)
|
return readereither.SequenceArray(ma)
|
||||||
}
|
}
|
||||||
@@ -13,11 +13,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
|
||||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
G "github.com/IBM/fp-go/v2/readereither/generic"
|
G "github.com/IBM/fp-go/v2/readereither/generic"
|
||||||
)
|
)
|
||||||
@@ -34,8 +33,8 @@ import (
|
|||||||
// result := readereither.Do(State{})
|
// result := readereither.Do(State{})
|
||||||
func Do[S any](
|
func Do[S any](
|
||||||
empty S,
|
empty S,
|
||||||
) ReaderEither[S] {
|
) ReaderResult[S] {
|
||||||
return G.Do[ReaderEither[S], context.Context, error, S](empty)
|
return G.Do[ReaderResult[S]](empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
|
||||||
@@ -58,7 +57,7 @@ func Do[S any](
|
|||||||
// func(uid string) func(State) State {
|
// func(uid string) func(State) State {
|
||||||
// return func(s State) State { s.UserID = uid; return s }
|
// return func(s State) State { s.UserID = uid; return s }
|
||||||
// },
|
// },
|
||||||
// func(s State) readereither.ReaderEither[string] {
|
// func(s State) readereither.ReaderResult[string] {
|
||||||
// return func(ctx context.Context) either.Either[error, string] {
|
// return func(ctx context.Context) either.Either[error, string] {
|
||||||
// if uid, ok := ctx.Value("userID").(string); ok {
|
// if uid, ok := ctx.Value("userID").(string); ok {
|
||||||
// return either.Right[error](uid)
|
// return either.Right[error](uid)
|
||||||
@@ -71,7 +70,7 @@ func Do[S any](
|
|||||||
// func(tid string) func(State) State {
|
// func(tid string) func(State) State {
|
||||||
// return func(s State) State { s.TenantID = tid; return s }
|
// return func(s State) State { s.TenantID = tid; return s }
|
||||||
// },
|
// },
|
||||||
// func(s State) readereither.ReaderEither[string] {
|
// func(s State) readereither.ReaderResult[string] {
|
||||||
// // This can access s.UserID from the previous step
|
// // This can access s.UserID from the previous step
|
||||||
// return func(ctx context.Context) either.Either[error, string] {
|
// return func(ctx context.Context) either.Either[error, string] {
|
||||||
// return either.Right[error]("tenant-" + s.UserID)
|
// return either.Right[error]("tenant-" + s.UserID)
|
||||||
@@ -82,31 +81,31 @@ func Do[S any](
|
|||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f Kleisli[S1, T],
|
f Kleisli[S1, T],
|
||||||
) Kleisli[ReaderEither[S1], S2] {
|
) Kleisli[ReaderResult[S1], S2] {
|
||||||
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
|
return G.Bind[ReaderResult[S1], ReaderResult[S2]](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
func Let[S1, S2, T any](
|
func Let[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) Kleisli[ReaderEither[S1], S2] {
|
) Kleisli[ReaderResult[S1], S2] {
|
||||||
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
|
return G.Let[ReaderResult[S1], ReaderResult[S2]](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
func LetTo[S1, S2, T any](
|
func LetTo[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) Kleisli[ReaderEither[S1], S2] {
|
) Kleisli[ReaderResult[S1], S2] {
|
||||||
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
|
return G.LetTo[ReaderResult[S1], ReaderResult[S2]](setter, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindTo initializes a new state [S1] from a value [T]
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) Kleisli[ReaderEither[T], S1] {
|
) Kleisli[ReaderResult[T], S1] {
|
||||||
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter)
|
return G.BindTo[ReaderResult[S1], ReaderResult[T]](setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering
|
||||||
@@ -148,9 +147,9 @@ func BindTo[S1, T any](
|
|||||||
// )
|
// )
|
||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa ReaderEither[T],
|
fa ReaderResult[T],
|
||||||
) Kleisli[ReaderEither[S1], S2] {
|
) Kleisli[ReaderResult[S1], S2] {
|
||||||
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
|
return G.ApS[ReaderResult[S1], ReaderResult[S2]](setter, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApSL is a variant of ApS that uses a lens to focus on a specific field in the state.
|
// ApSL is a variant of ApS that uses a lens to focus on a specific field in the state.
|
||||||
@@ -159,10 +158,10 @@ func ApS[S1, S2, T any](
|
|||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - lens: A lens that focuses on a field of type T within state S
|
// - lens: A lens that focuses on a field of type T within state S
|
||||||
// - fa: A ReaderEither computation that produces a value of type T
|
// - fa: A ReaderResult computation that produces a value of type T
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that transforms ReaderEither[S] to ReaderEither[S] by setting the focused field
|
// - A function that transforms ReaderResult[S] to ReaderResult[S] by setting the focused field
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -186,8 +185,8 @@ func ApS[S1, S2, T any](
|
|||||||
// )
|
// )
|
||||||
func ApSL[S, T any](
|
func ApSL[S, T any](
|
||||||
lens L.Lens[S, T],
|
lens L.Lens[S, T],
|
||||||
fa ReaderEither[T],
|
fa ReaderResult[T],
|
||||||
) Kleisli[ReaderEither[S], S] {
|
) Kleisli[ReaderResult[S], S] {
|
||||||
return ApS(lens.Set, fa)
|
return ApS(lens.Set, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,10 +198,10 @@ func ApSL[S, T any](
|
|||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - lens: A lens that focuses on a field of type T within state S
|
// - lens: A lens that focuses on a field of type T within state S
|
||||||
// - f: A function that takes the current field value and returns a ReaderEither computation
|
// - f: A function that takes the current field value and returns a ReaderResult computation
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that transforms ReaderEither[S] to ReaderEither[S]
|
// - A function that transforms ReaderResult[S] to ReaderResult[S]
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -215,7 +214,7 @@ func ApSL[S, T any](
|
|||||||
// func(c Counter, v int) Counter { c.Value = v; return c },
|
// func(c Counter, v int) Counter { c.Value = v; return c },
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// increment := func(v int) readereither.ReaderEither[int] {
|
// increment := func(v int) readereither.ReaderResult[int] {
|
||||||
// return func(ctx context.Context) either.Either[error, int] {
|
// return func(ctx context.Context) either.Either[error, int] {
|
||||||
// if v >= 100 {
|
// if v >= 100 {
|
||||||
// return either.Left[int](errors.New("value too large"))
|
// return either.Left[int](errors.New("value too large"))
|
||||||
@@ -231,10 +230,8 @@ func ApSL[S, T any](
|
|||||||
func BindL[S, T any](
|
func BindL[S, T any](
|
||||||
lens L.Lens[S, T],
|
lens L.Lens[S, T],
|
||||||
f Kleisli[T, T],
|
f Kleisli[T, T],
|
||||||
) Kleisli[ReaderEither[S], S] {
|
) Kleisli[ReaderResult[S], S] {
|
||||||
return Bind[S, S, T](lens.Set, func(s S) ReaderEither[T] {
|
return Bind(lens.Set, F.Flow2(lens.Get, f))
|
||||||
return f(lens.Get(s))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LetL is a variant of Let that uses a lens to focus on a specific field in the state.
|
// LetL is a variant of Let that uses a lens to focus on a specific field in the state.
|
||||||
@@ -245,7 +242,7 @@ func BindL[S, T any](
|
|||||||
// - f: A pure function that transforms the field value
|
// - f: A pure function that transforms the field value
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that transforms ReaderEither[S] to ReaderEither[S]
|
// - A function that transforms ReaderResult[S] to ReaderResult[S]
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -268,10 +265,8 @@ func BindL[S, T any](
|
|||||||
func LetL[S, T any](
|
func LetL[S, T any](
|
||||||
lens L.Lens[S, T],
|
lens L.Lens[S, T],
|
||||||
f func(T) T,
|
f func(T) T,
|
||||||
) Kleisli[ReaderEither[S], S] {
|
) Kleisli[ReaderResult[S], S] {
|
||||||
return Let[S, S, T](lens.Set, func(s S) T {
|
return Let(lens.Set, F.Flow2(lens.Get, f))
|
||||||
return f(lens.Get(s))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LetToL is a variant of LetTo that uses a lens to focus on a specific field in the state.
|
// LetToL is a variant of LetTo that uses a lens to focus on a specific field in the state.
|
||||||
@@ -282,7 +277,7 @@ func LetL[S, T any](
|
|||||||
// - b: The constant value to set
|
// - b: The constant value to set
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - A function that transforms ReaderEither[S] to ReaderEither[S]
|
// - A function that transforms ReaderResult[S] to ReaderResult[S]
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -304,6 +299,6 @@ func LetL[S, T any](
|
|||||||
func LetToL[S, T any](
|
func LetToL[S, T any](
|
||||||
lens L.Lens[S, T],
|
lens L.Lens[S, T],
|
||||||
b T,
|
b T,
|
||||||
) Kleisli[ReaderEither[S], S] {
|
) Kleisli[ReaderResult[S], S] {
|
||||||
return LetTo[S, S, T](lens.Set, b)
|
return LetTo(lens.Set, b)
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -25,11 +25,11 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getLastName(s utils.Initial) ReaderEither[string] {
|
func getLastName(s utils.Initial) ReaderResult[string] {
|
||||||
return Of("Doe")
|
return Of("Doe")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGivenName(s utils.WithLastName) ReaderEither[string] {
|
func getGivenName(s utils.WithLastName) ReaderResult[string] {
|
||||||
return Of("John")
|
return Of("John")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -21,8 +21,8 @@ import (
|
|||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
)
|
)
|
||||||
|
|
||||||
// withContext wraps an existing ReaderEither and performs a context check for cancellation before deletating
|
// withContext wraps an existing ReaderResult and performs a context check for cancellation before deletating
|
||||||
func WithContext[A any](ma ReaderEither[A]) ReaderEither[A] {
|
func WithContext[A any](ma ReaderResult[A]) ReaderResult[A] {
|
||||||
return func(ctx context.Context) E.Either[error, A] {
|
return func(ctx context.Context) E.Either[error, A] {
|
||||||
if err := context.Cause(ctx); err != nil {
|
if err := context.Cause(ctx); err != nil {
|
||||||
return E.Left[A](err)
|
return E.Left[A](err)
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
||||||
|
|
||||||
func Curry0[A any](f func(context.Context) (A, error)) ReaderEither[A] {
|
func Curry0[A any](f func(context.Context) (A, error)) ReaderResult[A] {
|
||||||
return readereither.Curry0(f)
|
return readereither.Curry0(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,11 +18,10 @@ package exec
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/IBM/fp-go/v2/context/readereither"
|
|
||||||
"github.com/IBM/fp-go/v2/either"
|
|
||||||
"github.com/IBM/fp-go/v2/exec"
|
"github.com/IBM/fp-go/v2/exec"
|
||||||
"github.com/IBM/fp-go/v2/function"
|
"github.com/IBM/fp-go/v2/function"
|
||||||
INTE "github.com/IBM/fp-go/v2/internal/exec"
|
INTE "github.com/IBM/fp-go/v2/internal/exec"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -32,8 +31,8 @@ var (
|
|||||||
Command = function.Curry3(command)
|
Command = function.Curry3(command)
|
||||||
)
|
)
|
||||||
|
|
||||||
func command(name string, args []string, in []byte) readereither.ReaderEither[exec.CommandOutput] {
|
func command(name string, args []string, in []byte) ReaderResult[exec.CommandOutput] {
|
||||||
return func(ctx context.Context) either.Either[error, exec.CommandOutput] {
|
return func(ctx context.Context) Result[exec.CommandOutput] {
|
||||||
return either.TryCatchError(INTE.Exec(ctx, name, args, in))
|
return result.TryCatchError(INTE.Exec(ctx, name, args, in))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
27
v2/context/readerresult/exec/type.go
Normal file
27
v2/context/readerresult/exec/type.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// 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 readerresult implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error
|
||||||
|
package exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/context/readerresult"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Result[T any] = result.Result[T]
|
||||||
|
ReaderResult[T any] = readerresult.ReaderResult[T]
|
||||||
|
)
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
// these functions curry a golang function with the context as the firsr parameter into a either reader with the context as the last parameter
|
||||||
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
// this goes back to the advice in https://pkg.go.dev/context to put the context as a first parameter as a convention
|
||||||
|
|
||||||
func From0[A any](f func(context.Context) (A, error)) func() ReaderEither[A] {
|
func From0[A any](f func(context.Context) (A, error)) func() ReaderResult[A] {
|
||||||
return readereither.From0(f)
|
return readereither.From0(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,10 +32,10 @@ func From1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A] {
|
|||||||
return readereither.From1(f)
|
return readereither.From1(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func From2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1, T2) ReaderEither[A] {
|
func From2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1, T2) ReaderResult[A] {
|
||||||
return readereither.From2(f)
|
return readereither.From2(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderEither[A] {
|
func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderResult[A] {
|
||||||
return readereither.From3(f)
|
return readereither.From3(f)
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -21,19 +21,19 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/readereither"
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromEither[A any](e Either[A]) ReaderEither[A] {
|
func FromEither[A any](e Either[A]) ReaderResult[A] {
|
||||||
return readereither.FromEither[context.Context](e)
|
return readereither.FromEither[context.Context](e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Left[A any](l error) ReaderEither[A] {
|
func Left[A any](l error) ReaderResult[A] {
|
||||||
return readereither.Left[context.Context, A](l)
|
return readereither.Left[context.Context, A](l)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Right[A any](r A) ReaderEither[A] {
|
func Right[A any](r A) ReaderResult[A] {
|
||||||
return readereither.Right[context.Context, error](r)
|
return readereither.Right[context.Context, error](r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadMap[A, B any](fa ReaderEither[A], f func(A) B) ReaderEither[B] {
|
func MonadMap[A, B any](fa ReaderResult[A], f func(A) B) ReaderResult[B] {
|
||||||
return readereither.MonadMap(fa, f)
|
return readereither.MonadMap(fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ func Map[A, B any](f func(A) B) Operator[A, B] {
|
|||||||
return readereither.Map[context.Context, error](f)
|
return readereither.Map[context.Context, error](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChain[A, B any](ma ReaderEither[A], f Kleisli[A, B]) ReaderEither[B] {
|
func MonadChain[A, B any](ma ReaderResult[A], f Kleisli[A, B]) ReaderResult[B] {
|
||||||
return readereither.MonadChain(ma, f)
|
return readereither.MonadChain(ma, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,15 +49,15 @@ func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
|||||||
return readereither.Chain(f)
|
return readereither.Chain(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Of[A any](a A) ReaderEither[A] {
|
func Of[A any](a A) ReaderResult[A] {
|
||||||
return readereither.Of[context.Context, error](a)
|
return readereither.Of[context.Context, error](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadAp[A, B any](fab ReaderEither[func(A) B], fa ReaderEither[A]) ReaderEither[B] {
|
func MonadAp[A, B any](fab ReaderResult[func(A) B], fa ReaderResult[A]) ReaderResult[B] {
|
||||||
return readereither.MonadAp(fab, fa)
|
return readereither.MonadAp(fab, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ap[A, B any](fa ReaderEither[A]) func(ReaderEither[func(A) B]) ReaderEither[B] {
|
func Ap[A, B any](fa ReaderResult[A]) func(ReaderResult[func(A) B]) ReaderResult[B] {
|
||||||
return readereither.Ap[B](fa)
|
return readereither.Ap[B](fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,19 +65,19 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A
|
|||||||
return readereither.FromPredicate[context.Context](pred, onFalse)
|
return readereither.FromPredicate[context.Context](pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrElse[A any](onLeft Kleisli[error, A]) Kleisli[ReaderEither[A], A] {
|
func OrElse[A any](onLeft Kleisli[error, A]) Kleisli[ReaderResult[A], A] {
|
||||||
return readereither.OrElse(onLeft)
|
return readereither.OrElse(onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Ask() ReaderEither[context.Context] {
|
func Ask() ReaderResult[context.Context] {
|
||||||
return readereither.Ask[context.Context, error]()
|
return readereither.Ask[context.Context, error]()
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChainEitherK[A, B any](ma ReaderEither[A], f func(A) Either[B]) ReaderEither[B] {
|
func MonadChainEitherK[A, B any](ma ReaderResult[A], f func(A) Either[B]) ReaderResult[B] {
|
||||||
return readereither.MonadChainEitherK(ma, f)
|
return readereither.MonadChainEitherK(ma, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChainEitherK[A, B any](f func(A) Either[B]) func(ma ReaderEither[A]) ReaderEither[B] {
|
func ChainEitherK[A, B any](f func(A) Either[B]) func(ma ReaderResult[A]) ReaderResult[B] {
|
||||||
return readereither.ChainEitherK[context.Context](f)
|
return readereither.ChainEitherK[context.Context](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,10 +85,15 @@ func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operato
|
|||||||
return readereither.ChainOptionK[context.Context, A, B](onNone)
|
return readereither.ChainOptionK[context.Context, A, B](onNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadFlap[B, A any](fab ReaderEither[func(A) B], a A) ReaderEither[B] {
|
func MonadFlap[B, A any](fab ReaderResult[func(A) B], a A) ReaderResult[B] {
|
||||||
return readereither.MonadFlap(fab, a)
|
return readereither.MonadFlap(fab, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
||||||
return readereither.Flap[context.Context, error, B](a)
|
return readereither.Flap[context.Context, error, B](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func Read[A any](r context.Context) func(ReaderResult[A]) Result[A] {
|
||||||
|
return readereither.Read[error, A](r)
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/readereither"
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
@@ -22,18 +22,18 @@ import (
|
|||||||
|
|
||||||
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
// SequenceT converts n inputs of higher kinded types into a higher kinded types of n strongly typed values, represented as a tuple
|
||||||
|
|
||||||
func SequenceT1[A any](a ReaderEither[A]) ReaderEither[tuple.Tuple1[A]] {
|
func SequenceT1[A any](a ReaderResult[A]) ReaderResult[tuple.Tuple1[A]] {
|
||||||
return readereither.SequenceT1(a)
|
return readereither.SequenceT1(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SequenceT2[A, B any](a ReaderEither[A], b ReaderEither[B]) ReaderEither[tuple.Tuple2[A, B]] {
|
func SequenceT2[A, B any](a ReaderResult[A], b ReaderResult[B]) ReaderResult[tuple.Tuple2[A, B]] {
|
||||||
return readereither.SequenceT2(a, b)
|
return readereither.SequenceT2(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SequenceT3[A, B, C any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C]) ReaderEither[tuple.Tuple3[A, B, C]] {
|
func SequenceT3[A, B, C any](a ReaderResult[A], b ReaderResult[B], c ReaderResult[C]) ReaderResult[tuple.Tuple3[A, B, C]] {
|
||||||
return readereither.SequenceT3(a, b, c)
|
return readereither.SequenceT3(a, b, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SequenceT4[A, B, C, D any](a ReaderEither[A], b ReaderEither[B], c ReaderEither[C], d ReaderEither[D]) ReaderEither[tuple.Tuple4[A, B, C, D]] {
|
func SequenceT4[A, B, C, D any](a ReaderResult[A], b ReaderResult[B], c ReaderResult[C], d ReaderResult[D]) ReaderResult[tuple.Tuple4[A, B, C, D]] {
|
||||||
return readereither.SequenceT4(a, b, c, d)
|
return readereither.SequenceT4(a, b, c, d)
|
||||||
}
|
}
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package readereither implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error
|
// package readerresult implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error
|
||||||
package readereither
|
package readerresult
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -23,14 +23,16 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/option"
|
"github.com/IBM/fp-go/v2/option"
|
||||||
"github.com/IBM/fp-go/v2/reader"
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
"github.com/IBM/fp-go/v2/readereither"
|
"github.com/IBM/fp-go/v2/readereither"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Option[A any] = option.Option[A]
|
Option[A any] = option.Option[A]
|
||||||
Either[A any] = either.Either[error, A]
|
Either[A any] = either.Either[error, A]
|
||||||
// ReaderEither is a specialization of the Reader monad for the typical golang scenario
|
Result[A any] = result.Result[A]
|
||||||
ReaderEither[A any] = readereither.ReaderEither[context.Context, error, A]
|
// ReaderResult is a specialization of the Reader monad for the typical golang scenario
|
||||||
|
ReaderResult[A any] = readereither.ReaderEither[context.Context, error, A]
|
||||||
|
|
||||||
Kleisli[A, B any] = reader.Reader[A, ReaderEither[B]]
|
Kleisli[A, B any] = reader.Reader[A, ReaderResult[B]]
|
||||||
Operator[A, B any] = Kleisli[ReaderEither[A], B]
|
Operator[A, B any] = Kleisli[ReaderResult[A], B]
|
||||||
)
|
)
|
||||||
8340
v2/coverage.out
Normal file
8340
v2/coverage.out
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ import (
|
|||||||
DIE "github.com/IBM/fp-go/v2/di/erasure"
|
DIE "github.com/IBM/fp-go/v2/di/erasure"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
IO "github.com/IBM/fp-go/v2/io"
|
IO "github.com/IBM/fp-go/v2/io"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOR "github.com/IBM/fp-go/v2/ioresult"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -34,5 +34,5 @@ var (
|
|||||||
var RunMain = F.Flow3(
|
var RunMain = F.Flow3(
|
||||||
DIE.MakeInjector,
|
DIE.MakeInjector,
|
||||||
Main,
|
Main,
|
||||||
IOE.Fold(IO.Of[error], F.Constant1[any](IO.Of[error](nil))),
|
IOR.Fold(IO.Of[error], F.Constant1[any](IO.Of[error](nil))),
|
||||||
)
|
)
|
||||||
|
|||||||
40
v2/di/doc.go
40
v2/di/doc.go
@@ -64,8 +64,8 @@ Creating and using dependencies:
|
|||||||
dbProvider := di.MakeProvider1(
|
dbProvider := di.MakeProvider1(
|
||||||
DBToken,
|
DBToken,
|
||||||
ConfigToken.Identity(),
|
ConfigToken.Identity(),
|
||||||
func(cfg Config) IOE.IOEither[error, Database] {
|
func(cfg Config) IOResult[Database] {
|
||||||
return IOE.Of[error](NewDatabase(cfg))
|
return ioresult.Of(NewDatabase(cfg))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ Creating and using dependencies:
|
|||||||
APIToken,
|
APIToken,
|
||||||
ConfigToken.Identity(),
|
ConfigToken.Identity(),
|
||||||
DBToken.Identity(),
|
DBToken.Identity(),
|
||||||
func(cfg Config, db Database) IOE.IOEither[error, APIService] {
|
func(cfg Config, db Database) IOResult[APIService] {
|
||||||
return IOE.Of[error](NewAPIService(cfg, db))
|
return ioresult.Of(NewAPIService(cfg, db))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ MakeProvider0 - No dependencies:
|
|||||||
|
|
||||||
provider := di.MakeProvider0(
|
provider := di.MakeProvider0(
|
||||||
token,
|
token,
|
||||||
IOE.Of[error](value),
|
ioresult.Of(value),
|
||||||
)
|
)
|
||||||
|
|
||||||
MakeProvider1 - One dependency:
|
MakeProvider1 - One dependency:
|
||||||
@@ -124,8 +124,8 @@ MakeProvider1 - One dependency:
|
|||||||
provider := di.MakeProvider1(
|
provider := di.MakeProvider1(
|
||||||
resultToken,
|
resultToken,
|
||||||
dep1Token.Identity(),
|
dep1Token.Identity(),
|
||||||
func(dep1 Dep1Type) IOE.IOEither[error, ResultType] {
|
func(dep1 Dep1Type) IOResult[ResultType] {
|
||||||
return IOE.Of[error](createResult(dep1))
|
return ioresult.Of(createResult(dep1))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -135,8 +135,8 @@ MakeProvider2 - Two dependencies:
|
|||||||
resultToken,
|
resultToken,
|
||||||
dep1Token.Identity(),
|
dep1Token.Identity(),
|
||||||
dep2Token.Identity(),
|
dep2Token.Identity(),
|
||||||
func(dep1 Dep1Type, dep2 Dep2Type) IOE.IOEither[error, ResultType] {
|
func(dep1 Dep1Type, dep2 Dep2Type) IOResult[ResultType] {
|
||||||
return IOE.Of[error](createResult(dep1, dep2))
|
return ioresult.Of(createResult(dep1, dep2))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ provider is registered:
|
|||||||
|
|
||||||
token := di.MakeTokenWithDefault0(
|
token := di.MakeTokenWithDefault0(
|
||||||
"ServiceName",
|
"ServiceName",
|
||||||
IOE.Of[error](defaultImplementation),
|
ioresult.Of(defaultImplementation),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Or with dependencies
|
// Or with dependencies
|
||||||
@@ -161,8 +161,8 @@ provider is registered:
|
|||||||
"ServiceName",
|
"ServiceName",
|
||||||
dep1Token.Identity(),
|
dep1Token.Identity(),
|
||||||
dep2Token.Identity(),
|
dep2Token.Identity(),
|
||||||
func(dep1 Dep1Type, dep2 Dep2Type) IOE.IOEither[error, ResultType] {
|
func(dep1 Dep1Type, dep2 Dep2Type) IOResult[ResultType] {
|
||||||
return IOE.Of[error](createDefault(dep1, dep2))
|
return ioresult.Of(createDefault(dep1, dep2))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -208,8 +208,8 @@ The framework provides a convenient pattern for running applications:
|
|||||||
mainProvider := di.MakeProvider1(
|
mainProvider := di.MakeProvider1(
|
||||||
di.InjMain,
|
di.InjMain,
|
||||||
APIToken.Identity(),
|
APIToken.Identity(),
|
||||||
func(api APIService) IOE.IOEither[error, any] {
|
func(api APIService) IOResult[any] {
|
||||||
return IOE.Of[error](api.Start())
|
return ioresult.Of(api.Start())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -247,8 +247,8 @@ Example 1: Configuration-based Service
|
|||||||
clientProvider := di.MakeProvider1(
|
clientProvider := di.MakeProvider1(
|
||||||
ClientToken,
|
ClientToken,
|
||||||
ConfigToken.Identity(),
|
ConfigToken.Identity(),
|
||||||
func(cfg Config) IOE.IOEither[error, HTTPClient] {
|
func(cfg Config) IOResult[HTTPClient] {
|
||||||
return IOE.Of[error](HTTPClient{config: cfg})
|
return ioresult.Of(HTTPClient{config: cfg})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -263,8 +263,8 @@ Example 2: Optional Dependencies
|
|||||||
serviceProvider := di.MakeProvider1(
|
serviceProvider := di.MakeProvider1(
|
||||||
ServiceToken,
|
ServiceToken,
|
||||||
CacheToken.Option(), // Optional dependency
|
CacheToken.Option(), // Optional dependency
|
||||||
func(cache O.Option[Cache]) IOE.IOEither[error, Service] {
|
func(cache Option[Cache]) IOResult[Service] {
|
||||||
return IOE.Of[error](NewService(cache))
|
return ioresult.Of(NewService(cache))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -279,8 +279,8 @@ Example 3: Lazy Dependencies
|
|||||||
reporterProvider := di.MakeProvider1(
|
reporterProvider := di.MakeProvider1(
|
||||||
ReporterToken,
|
ReporterToken,
|
||||||
DBToken.IOEither(), // Lazy dependency
|
DBToken.IOEither(), // Lazy dependency
|
||||||
func(dbIO IOE.IOEither[error, Database]) IOE.IOEither[error, Reporter] {
|
func(dbIO IOResult[Database]) IOResult[Reporter] {
|
||||||
return IOE.Of[error](NewReporter(dbIO))
|
return ioresult.Of(NewReporter(dbIO))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/errors"
|
"github.com/IBM/fp-go/v2/errors"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
I "github.com/IBM/fp-go/v2/identity"
|
I "github.com/IBM/fp-go/v2/identity"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOR "github.com/IBM/fp-go/v2/ioresult"
|
||||||
L "github.com/IBM/fp-go/v2/lazy"
|
L "github.com/IBM/fp-go/v2/lazy"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
R "github.com/IBM/fp-go/v2/record"
|
R "github.com/IBM/fp-go/v2/record"
|
||||||
@@ -42,8 +42,8 @@ var (
|
|||||||
missingProviderError = F.Flow4(
|
missingProviderError = F.Flow4(
|
||||||
Dependency.String,
|
Dependency.String,
|
||||||
errors.OnSome[string]("no provider for dependency [%s]"),
|
errors.OnSome[string]("no provider for dependency [%s]"),
|
||||||
IOE.Left[any, error],
|
IOR.Left[any],
|
||||||
F.Constant1[InjectableFactory, IOE.IOEither[error, any]],
|
F.Constant1[InjectableFactory, IOResult[any]],
|
||||||
)
|
)
|
||||||
|
|
||||||
// missingProviderErrorOrDefault returns the default [ProviderFactory] or an error
|
// missingProviderErrorOrDefault returns the default [ProviderFactory] or an error
|
||||||
@@ -56,7 +56,7 @@ var (
|
|||||||
emptyMulti any = A.Empty[any]()
|
emptyMulti any = A.Empty[any]()
|
||||||
|
|
||||||
// emptyMultiDependency returns a [ProviderFactory] for an empty, multi dependency
|
// emptyMultiDependency returns a [ProviderFactory] for an empty, multi dependency
|
||||||
emptyMultiDependency = F.Constant1[Dependency](F.Constant1[InjectableFactory](IOE.Of[error](emptyMulti)))
|
emptyMultiDependency = F.Constant1[Dependency](F.Constant1[InjectableFactory](IOR.Of(emptyMulti)))
|
||||||
|
|
||||||
// handleMissingProvider covers the case of a missing provider. It either
|
// handleMissingProvider covers the case of a missing provider. It either
|
||||||
// returns an error or an empty multi value provider
|
// returns an error or an empty multi value provider
|
||||||
@@ -93,21 +93,21 @@ var (
|
|||||||
|
|
||||||
// isMultiDependency tests if a dependency is a container dependency
|
// isMultiDependency tests if a dependency is a container dependency
|
||||||
func isMultiDependency(dep Dependency) bool {
|
func isMultiDependency(dep Dependency) bool {
|
||||||
return dep.Flag()&Multi == Multi
|
return dep.Flag()&MULTI == MULTI
|
||||||
}
|
}
|
||||||
|
|
||||||
// isItemProvider tests if a provivder provides a single item
|
// isItemProvider tests if a provivder provides a single item
|
||||||
func isItemProvider(provider Provider) bool {
|
func isItemProvider(provider Provider) bool {
|
||||||
return provider.Provides().Flag()&Item == Item
|
return provider.Provides().Flag()&ITEM == ITEM
|
||||||
}
|
}
|
||||||
|
|
||||||
// itemProviderFactory combines multiple factories into one, returning an array
|
// itemProviderFactory combines multiple factories into one, returning an array
|
||||||
func itemProviderFactory(fcts []ProviderFactory) ProviderFactory {
|
func itemProviderFactory(fcts []ProviderFactory) ProviderFactory {
|
||||||
return func(inj InjectableFactory) IOE.IOEither[error, any] {
|
return func(inj InjectableFactory) IOResult[any] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
fcts,
|
fcts,
|
||||||
IOE.TraverseArray(I.Flap[IOE.IOEither[error, any]](inj)),
|
IOR.TraverseArray(I.Flap[IOResult[any]](inj)),
|
||||||
IOE.Map[error](F.ToAny[[]any]),
|
IOR.Map(F.ToAny[[]any]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ func itemProviderFactory(fcts []ProviderFactory) ProviderFactory {
|
|||||||
// makes sure to transitively resolve the required dependencies.
|
// makes sure to transitively resolve the required dependencies.
|
||||||
func MakeInjector(providers []Provider) InjectableFactory {
|
func MakeInjector(providers []Provider) InjectableFactory {
|
||||||
|
|
||||||
type Result = IOE.IOEither[error, any]
|
type Result = IOResult[any]
|
||||||
type LazyResult = L.Lazy[Result]
|
type LazyResult = L.Lazy[Result]
|
||||||
|
|
||||||
// resolved stores the values resolved so far, key is the string ID
|
// resolved stores the values resolved so far, key is the string ID
|
||||||
@@ -148,11 +148,11 @@ func MakeInjector(providers []Provider) InjectableFactory {
|
|||||||
T.Map2(F.Flow3(
|
T.Map2(F.Flow3(
|
||||||
Dependency.Id,
|
Dependency.Id,
|
||||||
R.Lookup[ProviderFactory, string],
|
R.Lookup[ProviderFactory, string],
|
||||||
I.Ap[O.Option[ProviderFactory]](factoryByID),
|
I.Ap[Option[ProviderFactory]](factoryByID),
|
||||||
), handleMissingProvider),
|
), handleMissingProvider),
|
||||||
T.Tupled2(O.MonadGetOrElse[ProviderFactory]),
|
T.Tupled2(O.MonadGetOrElse[ProviderFactory]),
|
||||||
I.Ap[IOE.IOEither[error, any]](injFct),
|
I.Ap[IOResult[any]](injFct),
|
||||||
IOE.Memoize[error, any],
|
IOR.Memoize[any],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,25 +19,23 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
A "github.com/IBM/fp-go/v2/array"
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
I "github.com/IBM/fp-go/v2/identity"
|
I "github.com/IBM/fp-go/v2/identity"
|
||||||
IO "github.com/IBM/fp-go/v2/io"
|
IO "github.com/IBM/fp-go/v2/io"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
IOO "github.com/IBM/fp-go/v2/iooption"
|
|
||||||
Int "github.com/IBM/fp-go/v2/number/integer"
|
Int "github.com/IBM/fp-go/v2/number/integer"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
|
||||||
R "github.com/IBM/fp-go/v2/record"
|
R "github.com/IBM/fp-go/v2/record"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// InjectableFactory is a factory function that can create an untyped instance of a service based on its [Dependency] identifier
|
// InjectableFactory is a factory function that can create an untyped instance of a service based on its [Dependency] identifier
|
||||||
InjectableFactory = func(Dependency) IOE.IOEither[error, any]
|
InjectableFactory = func(Dependency) IOResult[any]
|
||||||
ProviderFactory = func(InjectableFactory) IOE.IOEither[error, any]
|
ProviderFactory = func(InjectableFactory) IOResult[any]
|
||||||
|
|
||||||
paramIndex = map[int]int
|
paramIndex = map[int]int
|
||||||
paramValue = map[int]any
|
paramValue = map[int]any
|
||||||
handler = func(paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue]
|
handler = func(paramIndex) func([]IOResult[any]) IOResult[paramValue]
|
||||||
mapping = map[int]paramIndex
|
mapping = map[int]paramIndex
|
||||||
|
|
||||||
Provider interface {
|
Provider interface {
|
||||||
@@ -83,50 +81,50 @@ var (
|
|||||||
mergeMaps = R.UnionLastMonoid[int, any]()
|
mergeMaps = R.UnionLastMonoid[int, any]()
|
||||||
collectParams = R.CollectOrd[any, any](Int.Ord)(F.SK[int, any])
|
collectParams = R.CollectOrd[any, any](Int.Ord)(F.SK[int, any])
|
||||||
|
|
||||||
mapDeps = F.Curry2(A.MonadMap[Dependency, IOE.IOEither[error, any]])
|
mapDeps = F.Curry2(A.MonadMap[Dependency, IOResult[any]])
|
||||||
|
|
||||||
handlers = map[int]handler{
|
handlers = map[int]handler{
|
||||||
Identity: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
IDENTITY: func(mp paramIndex) func([]IOResult[any]) IOResult[paramValue] {
|
||||||
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
return func(res []IOResult[any]) IOResult[paramValue] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
mp,
|
mp,
|
||||||
IOE.TraverseRecord[int](getAt(res)),
|
IOE.TraverseRecord[int](getAt(res)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Option: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
OPTION: func(mp paramIndex) func([]IOResult[any]) IOResult[paramValue] {
|
||||||
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
return func(res []IOResult[any]) IOResult[paramValue] {
|
||||||
return F.Pipe3(
|
return F.Pipe3(
|
||||||
mp,
|
mp,
|
||||||
IO.TraverseRecord[int](getAt(res)),
|
IO.TraverseRecord[int](getAt(res)),
|
||||||
IO.Map(R.Map[int](F.Flow2(
|
IO.Map(R.Map[int](F.Flow2(
|
||||||
E.ToOption[error, any],
|
result.ToOption[any],
|
||||||
F.ToAny[O.Option[any]],
|
F.ToAny[Option[any]],
|
||||||
))),
|
))),
|
||||||
IOE.FromIO[error, paramValue],
|
IOE.FromIO[error, paramValue],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
IOEither: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
IOEITHER: func(mp paramIndex) func([]IOResult[any]) IOResult[paramValue] {
|
||||||
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
return func(res []IOResult[any]) IOResult[paramValue] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
mp,
|
mp,
|
||||||
R.Map[int](F.Flow2(
|
R.Map[int](F.Flow2(
|
||||||
getAt(res),
|
getAt(res),
|
||||||
F.ToAny[IOE.IOEither[error, any]],
|
F.ToAny[IOResult[any]],
|
||||||
)),
|
)),
|
||||||
IOE.Of[error, paramValue],
|
IOE.Of[error, paramValue],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
IOOption: func(mp paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
IOOPTION: func(mp paramIndex) func([]IOResult[any]) IOResult[paramValue] {
|
||||||
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
return func(res []IOResult[any]) IOResult[paramValue] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
mp,
|
mp,
|
||||||
R.Map[int](F.Flow3(
|
R.Map[int](F.Flow3(
|
||||||
getAt(res),
|
getAt(res),
|
||||||
IOE.ToIOOption[error, any],
|
IOE.ToIOOption[error, any],
|
||||||
F.ToAny[IOO.IOOption[any]],
|
F.ToAny[IOOption[any]],
|
||||||
)),
|
)),
|
||||||
IOE.Of[error, paramValue],
|
IOE.Of[error, paramValue],
|
||||||
)
|
)
|
||||||
@@ -141,23 +139,23 @@ func getAt[T any](ar []T) func(idx int) T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMapping(mp mapping) func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] {
|
func handleMapping(mp mapping) func(res []IOResult[any]) IOResult[[]any] {
|
||||||
preFct := F.Pipe1(
|
preFct := F.Pipe1(
|
||||||
mp,
|
mp,
|
||||||
R.Collect(func(idx int, p paramIndex) func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue] {
|
R.Collect(func(idx int, p paramIndex) func([]IOResult[any]) IOResult[paramValue] {
|
||||||
return handlers[idx](p)
|
return handlers[idx](p)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
doFct := F.Flow2(
|
doFct := F.Flow2(
|
||||||
I.Flap[IOE.IOEither[error, paramValue], []IOE.IOEither[error, any]],
|
I.Flap[IOResult[paramValue], []IOResult[any]],
|
||||||
IOE.TraverseArray[error, func([]IOE.IOEither[error, any]) IOE.IOEither[error, paramValue], paramValue],
|
IOE.TraverseArray[error, func([]IOResult[any]) IOResult[paramValue], paramValue],
|
||||||
)
|
)
|
||||||
postFct := IOE.Map[error](F.Flow2(
|
postFct := IOE.Map[error](F.Flow2(
|
||||||
A.Fold(mergeMaps),
|
A.Fold(mergeMaps),
|
||||||
collectParams,
|
collectParams,
|
||||||
))
|
))
|
||||||
|
|
||||||
return func(res []IOE.IOEither[error, any]) IOE.IOEither[error, []any] {
|
return func(res []IOResult[any]) IOResult[[]any] {
|
||||||
return F.Pipe2(
|
return F.Pipe2(
|
||||||
preFct,
|
preFct,
|
||||||
doFct(res),
|
doFct(res),
|
||||||
@@ -170,7 +168,7 @@ func handleMapping(mp mapping) func(res []IOE.IOEither[error, any]) IOE.IOEither
|
|||||||
// a function that accepts the resolved dependencies to return a result
|
// a function that accepts the resolved dependencies to return a result
|
||||||
func MakeProviderFactory(
|
func MakeProviderFactory(
|
||||||
deps []Dependency,
|
deps []Dependency,
|
||||||
fct func(param ...any) IOE.IOEither[error, any]) ProviderFactory {
|
fct func(param ...any) IOResult[any]) ProviderFactory {
|
||||||
|
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
mapDeps(deps),
|
mapDeps(deps),
|
||||||
|
|||||||
@@ -17,20 +17,18 @@ package erasure
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
BehaviourMask = 0x0f
|
BehaviourMask = 0x0f
|
||||||
Identity = 0 // required dependency
|
IDENTITY = 0 // required dependency
|
||||||
Option = 1 // optional dependency
|
OPTION = 1 // optional dependency
|
||||||
IOEither = 2 // lazy and required
|
IOEITHER = 2 // lazy and required
|
||||||
IOOption = 3 // lazy and optional
|
IOOPTION = 3 // lazy and optional
|
||||||
|
|
||||||
TypeMask = 0xf0
|
TypeMask = 0xf0
|
||||||
Multi = 1 << 4 // array of implementations
|
MULTI = 1 << 4 // array of implementations
|
||||||
Item = 2 << 4 // item of a multi token
|
ITEM = 2 << 4 // item of a multi token
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dependency describes the relationship to a service
|
// Dependency describes the relationship to a service
|
||||||
@@ -41,5 +39,5 @@ type Dependency interface {
|
|||||||
// Flag returns a tag that identifies the behaviour of the dependency
|
// Flag returns a tag that identifies the behaviour of the dependency
|
||||||
Flag() int
|
Flag() int
|
||||||
// ProviderFactory optionally returns an attached [ProviderFactory] that represents the default for this dependency
|
// ProviderFactory optionally returns an attached [ProviderFactory] that represents the default for this dependency
|
||||||
ProviderFactory() O.Option[ProviderFactory]
|
ProviderFactory() Option[ProviderFactory]
|
||||||
}
|
}
|
||||||
|
|||||||
13
v2/di/erasure/types.go
Normal file
13
v2/di/erasure/types.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package erasure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/iooption"
|
||||||
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Option[T any] = option.Option[T]
|
||||||
|
IOResult[T any] = ioresult.IOResult[T]
|
||||||
|
IOOption[T any] = iooption.IOOption[T]
|
||||||
|
)
|
||||||
3186
v2/di/gen.go
3186
v2/di/gen.go
File diff suppressed because it is too large
Load Diff
@@ -19,14 +19,14 @@ import (
|
|||||||
DIE "github.com/IBM/fp-go/v2/di/erasure"
|
DIE "github.com/IBM/fp-go/v2/di/erasure"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/IBM/fp-go/v2/identity"
|
"github.com/IBM/fp-go/v2/identity"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOR "github.com/IBM/fp-go/v2/ioresult"
|
||||||
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
RIOR "github.com/IBM/fp-go/v2/readerioresult"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Resolve performs a type safe resolution of a dependency
|
// Resolve performs a type safe resolution of a dependency
|
||||||
func Resolve[T any](token InjectionToken[T]) RIOE.ReaderIOEither[DIE.InjectableFactory, error, T] {
|
func Resolve[T any](token InjectionToken[T]) RIOR.ReaderIOResult[DIE.InjectableFactory, T] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
identity.Ap[IOE.IOEither[error, any]](asDependency(token)),
|
identity.Ap[IOResult[any]](asDependency(token)),
|
||||||
IOE.ChainEitherK(token.Unerase),
|
IOR.ChainResultK(token.Unerase),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/errors"
|
"github.com/IBM/fp-go/v2/errors"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[error, T] {
|
func lookupAt[T any](idx int, token Dependency[T]) func(params []any) Result[T] {
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
A.Lookup[any](idx),
|
A.Lookup[any](idx),
|
||||||
E.FromOption[any](errors.OnNone("No parameter at position %d", idx)),
|
E.FromOption[any](errors.OnNone("No parameter at position %d", idx)),
|
||||||
@@ -32,7 +33,7 @@ func lookupAt[T any](idx int, token Dependency[T]) func(params []any) E.Either[e
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error, A]) IOE.IOEither[error, any] {
|
func eraseTuple[A, R any](f func(A) IOResult[R]) func(Result[A]) IOResult[any] {
|
||||||
return F.Flow3(
|
return F.Flow3(
|
||||||
IOE.FromEither[error, A],
|
IOE.FromEither[error, A],
|
||||||
IOE.Chain(f),
|
IOE.Chain(f),
|
||||||
@@ -40,8 +41,8 @@ func eraseTuple[A, R any](f func(A) IOE.IOEither[error, R]) func(E.Either[error,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any) IOE.IOEither[error, any] {
|
func eraseProviderFactory0[R any](f IOResult[R]) func(params ...any) IOResult[any] {
|
||||||
return func(_ ...any) IOE.IOEither[error, any] {
|
return func(_ ...any) IOResult[any] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
f,
|
f,
|
||||||
IOE.Map[error](F.ToAny[R]),
|
IOE.Map[error](F.ToAny[R]),
|
||||||
@@ -50,7 +51,7 @@ func eraseProviderFactory0[R any](f IOE.IOEither[error, R]) func(params ...any)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MakeProviderFactory0[R any](
|
func MakeProviderFactory0[R any](
|
||||||
fct IOE.IOEither[error, R],
|
fct IOResult[R],
|
||||||
) DIE.ProviderFactory {
|
) DIE.ProviderFactory {
|
||||||
return DIE.MakeProviderFactory(
|
return DIE.MakeProviderFactory(
|
||||||
A.Empty[DIE.Dependency](),
|
A.Empty[DIE.Dependency](),
|
||||||
@@ -59,13 +60,13 @@ func MakeProviderFactory0[R any](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MakeTokenWithDefault0 creates a unique [InjectionToken] for a specific type with an attached default [DIE.Provider]
|
// MakeTokenWithDefault0 creates a unique [InjectionToken] for a specific type with an attached default [DIE.Provider]
|
||||||
func MakeTokenWithDefault0[R any](name string, fct IOE.IOEither[error, R]) InjectionToken[R] {
|
func MakeTokenWithDefault0[R any](name string, fct IOResult[R]) InjectionToken[R] {
|
||||||
return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct))
|
return MakeTokenWithDefault[R](name, MakeProviderFactory0(fct))
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeProvider0[R any](
|
func MakeProvider0[R any](
|
||||||
token InjectionToken[R],
|
token InjectionToken[R],
|
||||||
fct IOE.IOEither[error, R],
|
fct IOResult[R],
|
||||||
) DIE.Provider {
|
) DIE.Provider {
|
||||||
return DIE.MakeProvider(
|
return DIE.MakeProvider(
|
||||||
token,
|
token,
|
||||||
@@ -75,5 +76,5 @@ func MakeProvider0[R any](
|
|||||||
|
|
||||||
// ConstProvider simple implementation for a provider with a constant value
|
// ConstProvider simple implementation for a provider with a constant value
|
||||||
func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider {
|
func ConstProvider[R any](token InjectionToken[R], value R) DIE.Provider {
|
||||||
return MakeProvider0[R](token, IOE.Of[error](value))
|
return MakeProvider0(token, ioresult.Of(value))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ import (
|
|||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,19 +40,19 @@ func TestSimpleProvider(t *testing.T) {
|
|||||||
|
|
||||||
var staticCount int
|
var staticCount int
|
||||||
|
|
||||||
staticValue := func(value string) IOE.IOEither[error, string] {
|
staticValue := func(value string) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
staticCount++
|
staticCount++
|
||||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dynamicCount int
|
var dynamicCount int
|
||||||
|
|
||||||
dynamicValue := func(value string) IOE.IOEither[error, string] {
|
dynamicValue := func(value string) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
dynamicCount++
|
dynamicCount++
|
||||||
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,19 +82,19 @@ func TestOptionalProvider(t *testing.T) {
|
|||||||
|
|
||||||
var staticCount int
|
var staticCount int
|
||||||
|
|
||||||
staticValue := func(value string) IOE.IOEither[error, string] {
|
staticValue := func(value string) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
staticCount++
|
staticCount++
|
||||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dynamicCount int
|
var dynamicCount int
|
||||||
|
|
||||||
dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] {
|
dynamicValue := func(value Option[string]) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
dynamicCount++
|
dynamicCount++
|
||||||
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,10 +124,10 @@ func TestOptionalProviderMissingDependency(t *testing.T) {
|
|||||||
|
|
||||||
var dynamicCount int
|
var dynamicCount int
|
||||||
|
|
||||||
dynamicValue := func(value O.Option[string]) IOE.IOEither[error, string] {
|
dynamicValue := func(value Option[string]) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
dynamicCount++
|
dynamicCount++
|
||||||
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,10 +152,10 @@ func TestProviderMissingDependency(t *testing.T) {
|
|||||||
|
|
||||||
var dynamicCount int
|
var dynamicCount int
|
||||||
|
|
||||||
dynamicValue := func(value string) IOE.IOEither[error, string] {
|
dynamicValue := func(value string) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
dynamicCount++
|
dynamicCount++
|
||||||
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,31 +180,31 @@ func TestEagerAndLazyProvider(t *testing.T) {
|
|||||||
|
|
||||||
var staticCount int
|
var staticCount int
|
||||||
|
|
||||||
staticValue := func(value string) IOE.IOEither[error, string] {
|
staticValue := func(value string) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
staticCount++
|
staticCount++
|
||||||
return E.Of[error](fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Static based on [%s], at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dynamicCount int
|
var dynamicCount int
|
||||||
|
|
||||||
dynamicValue := func(value string) IOE.IOEither[error, string] {
|
dynamicValue := func(value string) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
dynamicCount++
|
dynamicCount++
|
||||||
return E.Of[error](fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
return result.Of(fmt.Sprintf("Dynamic based on [%s] at [%s]", value, time.Now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var lazyEagerCount int
|
var lazyEagerCount int
|
||||||
|
|
||||||
lazyEager := func(laz IOE.IOEither[error, string], eager string) IOE.IOEither[error, string] {
|
lazyEager := func(laz IOResult[string], eager string) IOResult[string] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
laz,
|
laz,
|
||||||
IOE.Chain(func(lazValue string) IOE.IOEither[error, string] {
|
IOE.Chain(func(lazValue string) IOResult[string] {
|
||||||
return func() E.Either[error, string] {
|
return func() Result[string] {
|
||||||
lazyEagerCount++
|
lazyEagerCount++
|
||||||
return E.Of[error](fmt.Sprintf("Dynamic based on [%s], [%s] at [%s]", lazValue, eager, time.Now()))
|
return result.Of(fmt.Sprintf("Dynamic based on [%s], [%s] at [%s]", lazValue, eager, time.Now()))
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -248,7 +249,7 @@ func TestItemProvider(t *testing.T) {
|
|||||||
|
|
||||||
value := multiInj()
|
value := multiInj()
|
||||||
|
|
||||||
assert.Equal(t, E.Of[error](A.From("Value1", "Value2")), value)
|
assert.Equal(t, result.Of(A.From("Value1", "Value2")), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyItemProvider(t *testing.T) {
|
func TestEmptyItemProvider(t *testing.T) {
|
||||||
@@ -269,7 +270,7 @@ func TestEmptyItemProvider(t *testing.T) {
|
|||||||
|
|
||||||
value := multiInj()
|
value := multiInj()
|
||||||
|
|
||||||
assert.Equal(t, E.Of[error](A.Empty[string]()), value)
|
assert.Equal(t, result.Of(A.Empty[string]()), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDependencyOnMultiProvider(t *testing.T) {
|
func TestDependencyOnMultiProvider(t *testing.T) {
|
||||||
@@ -283,8 +284,8 @@ func TestDependencyOnMultiProvider(t *testing.T) {
|
|||||||
p1 := ConstProvider(INJ_KEY1, "Value3")
|
p1 := ConstProvider(INJ_KEY1, "Value3")
|
||||||
p2 := ConstProvider(INJ_KEY2, "Value4")
|
p2 := ConstProvider(INJ_KEY2, "Value4")
|
||||||
|
|
||||||
fromMulti := func(val string, multi []string) IOE.IOEither[error, string] {
|
fromMulti := func(val string, multi []string) IOResult[string] {
|
||||||
return IOE.Of[error](fmt.Sprintf("Val: %s, Multi: %s", val, multi))
|
return ioresult.Of(fmt.Sprintf("Val: %s, Multi: %s", val, multi))
|
||||||
}
|
}
|
||||||
p3 := MakeProvider2(INJ_KEY3, INJ_KEY1.Identity(), injMulti.Container().Identity(), fromMulti)
|
p3 := MakeProvider2(INJ_KEY3, INJ_KEY1.Identity(), injMulti.Container().Identity(), fromMulti)
|
||||||
|
|
||||||
@@ -295,19 +296,19 @@ func TestDependencyOnMultiProvider(t *testing.T) {
|
|||||||
|
|
||||||
v := r3(inj)()
|
v := r3(inj)()
|
||||||
|
|
||||||
assert.Equal(t, E.Of[error]("Val: Value3, Multi: [Value1 Value2]"), v)
|
assert.Equal(t, result.Of("Val: Value3, Multi: [Value1 Value2]"), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenWithDefaultProvider(t *testing.T) {
|
func TestTokenWithDefaultProvider(t *testing.T) {
|
||||||
// token without a default
|
// token without a default
|
||||||
injToken1 := MakeToken[string]("Token1")
|
injToken1 := MakeToken[string]("Token1")
|
||||||
// token with a default
|
// token with a default
|
||||||
injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
|
injToken2 := MakeTokenWithDefault0("Token2", ioresult.Of("Carsten"))
|
||||||
// dependency
|
// dependency
|
||||||
injToken3 := MakeToken[string]("Token3")
|
injToken3 := MakeToken[string]("Token3")
|
||||||
|
|
||||||
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] {
|
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOResult[string] {
|
||||||
return IOE.Of[error](fmt.Sprintf("Token: %s", data))
|
return ioresult.Of(fmt.Sprintf("Token: %s", data))
|
||||||
})
|
})
|
||||||
|
|
||||||
// populate the injector
|
// populate the injector
|
||||||
@@ -320,19 +321,19 @@ func TestTokenWithDefaultProvider(t *testing.T) {
|
|||||||
// inj1 should not be available
|
// inj1 should not be available
|
||||||
assert.True(t, E.IsLeft(r1(inj)()))
|
assert.True(t, E.IsLeft(r1(inj)()))
|
||||||
// r3 should work
|
// r3 should work
|
||||||
assert.Equal(t, E.Of[error]("Token: Carsten"), r3(inj)())
|
assert.Equal(t, result.Of("Token: Carsten"), r3(inj)())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenWithDefaultProviderAndOverride(t *testing.T) {
|
func TestTokenWithDefaultProviderAndOverride(t *testing.T) {
|
||||||
// token with a default
|
// token with a default
|
||||||
injToken2 := MakeTokenWithDefault0("Token2", IOE.Of[error]("Carsten"))
|
injToken2 := MakeTokenWithDefault0("Token2", ioresult.Of("Carsten"))
|
||||||
// dependency
|
// dependency
|
||||||
injToken3 := MakeToken[string]("Token3")
|
injToken3 := MakeToken[string]("Token3")
|
||||||
|
|
||||||
p2 := ConstProvider(injToken2, "Override")
|
p2 := ConstProvider(injToken2, "Override")
|
||||||
|
|
||||||
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOE.IOEither[error, string] {
|
p3 := MakeProvider1(injToken3, injToken2.Identity(), func(data string) IOResult[string] {
|
||||||
return IOE.Of[error](fmt.Sprintf("Token: %s", data))
|
return ioresult.Of(fmt.Sprintf("Token: %s", data))
|
||||||
})
|
})
|
||||||
|
|
||||||
// populate the injector
|
// populate the injector
|
||||||
@@ -342,5 +343,5 @@ func TestTokenWithDefaultProviderAndOverride(t *testing.T) {
|
|||||||
r3 := Resolve(injToken3)
|
r3 := Resolve(injToken3)
|
||||||
|
|
||||||
// r3 should work
|
// r3 should work
|
||||||
assert.Equal(t, E.Of[error]("Token: Override"), r3(inj)())
|
assert.Equal(t, result.Of("Token: Override"), r3(inj)())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
DIE "github.com/IBM/fp-go/v2/di/erasure"
|
DIE "github.com/IBM/fp-go/v2/di/erasure"
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
|
||||||
IO "github.com/IBM/fp-go/v2/io"
|
IO "github.com/IBM/fp-go/v2/io"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
|
||||||
IOO "github.com/IBM/fp-go/v2/iooption"
|
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +30,7 @@ import (
|
|||||||
type Dependency[T any] interface {
|
type Dependency[T any] interface {
|
||||||
DIE.Dependency
|
DIE.Dependency
|
||||||
// Unerase converts a value with erased type signature into a strongly typed value
|
// Unerase converts a value with erased type signature into a strongly typed value
|
||||||
Unerase(val any) E.Either[error, T]
|
Unerase(val any) Result[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
// InjectionToken uniquely identifies a dependency by giving it an Id, Type and name
|
// InjectionToken uniquely identifies a dependency by giving it an Id, Type and name
|
||||||
@@ -42,17 +39,17 @@ type InjectionToken[T any] interface {
|
|||||||
// Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`.
|
// Identity idenifies this dependency as a mandatory, required dependency, it will be resolved eagerly and injected as `T`.
|
||||||
// If the dependency cannot be resolved, the resolution process fails
|
// If the dependency cannot be resolved, the resolution process fails
|
||||||
Identity() Dependency[T]
|
Identity() Dependency[T]
|
||||||
// Option identifies this dependency as optional, it will be resolved eagerly and injected as [O.Option[T]].
|
// Option identifies this dependency as optional, it will be resolved eagerly and injected as [Option[T]].
|
||||||
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as [O.None[T]]
|
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as [O.None[T]]
|
||||||
Option() Dependency[O.Option[T]]
|
Option() Dependency[Option[T]]
|
||||||
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a [IOE.IOEither[error, T]]. This
|
// IOEither identifies this dependency as mandatory but it will be resolved lazily as a [IOResult[T]]. This
|
||||||
// value is memoized to make sure the dependency is a singleton.
|
// value is memoized to make sure the dependency is a singleton.
|
||||||
// If the dependency cannot be resolved, the resolution process fails
|
// If the dependency cannot be resolved, the resolution process fails
|
||||||
IOEither() Dependency[IOE.IOEither[error, T]]
|
IOEither() Dependency[IOResult[T]]
|
||||||
// IOOption identifies this dependency as optional but it will be resolved lazily as a [IOO.IOOption[T]]. This
|
// IOOption identifies this dependency as optional but it will be resolved lazily as a [IOOption[T]]. This
|
||||||
// value is memoized to make sure the dependency is a singleton.
|
// value is memoized to make sure the dependency is a singleton.
|
||||||
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value.
|
// If the dependency cannot be resolved, the resolution process continues and the dependency is represented as the none value.
|
||||||
IOOption() Dependency[IOO.IOOption[T]]
|
IOOption() Dependency[IOOption[T]]
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name that can have multiple implementations.
|
// MultiInjectionToken uniquely identifies a dependency by giving it an Id, Type and name that can have multiple implementations.
|
||||||
@@ -79,12 +76,12 @@ type tokenBase struct {
|
|||||||
name string
|
name string
|
||||||
id string
|
id string
|
||||||
flag int
|
flag int
|
||||||
providerFactory O.Option[DIE.ProviderFactory]
|
providerFactory Option[DIE.ProviderFactory]
|
||||||
}
|
}
|
||||||
|
|
||||||
type token[T any] struct {
|
type token[T any] struct {
|
||||||
base *tokenBase
|
base *tokenBase
|
||||||
toType func(val any) E.Either[error, T]
|
toType func(val any) Result[T]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *token[T]) Id() string {
|
func (t *token[T]) Id() string {
|
||||||
@@ -99,26 +96,26 @@ func (t *token[T]) String() string {
|
|||||||
return t.base.name
|
return t.base.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *token[T]) Unerase(val any) E.Either[error, T] {
|
func (t *token[T]) Unerase(val any) Result[T] {
|
||||||
return t.toType(val)
|
return t.toType(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *token[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
func (t *token[T]) ProviderFactory() Option[DIE.ProviderFactory] {
|
||||||
return t.base.providerFactory
|
return t.base.providerFactory
|
||||||
}
|
}
|
||||||
func makeTokenBase(name string, id string, typ int, providerFactory O.Option[DIE.ProviderFactory]) *tokenBase {
|
func makeTokenBase(name string, id string, typ int, providerFactory Option[DIE.ProviderFactory]) *tokenBase {
|
||||||
return &tokenBase{name, id, typ, providerFactory}
|
return &tokenBase{name, id, typ, providerFactory}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeToken[T any](name string, id string, typ int, unerase func(val any) E.Either[error, T], providerFactory O.Option[DIE.ProviderFactory]) Dependency[T] {
|
func makeToken[T any](name string, id string, typ int, unerase func(val any) Result[T], providerFactory Option[DIE.ProviderFactory]) Dependency[T] {
|
||||||
return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase}
|
return &token[T]{makeTokenBase(name, id, typ, providerFactory), unerase}
|
||||||
}
|
}
|
||||||
|
|
||||||
type injectionToken[T any] struct {
|
type injectionToken[T any] struct {
|
||||||
token[T]
|
token[T]
|
||||||
option Dependency[O.Option[T]]
|
option Dependency[Option[T]]
|
||||||
ioeither Dependency[IOE.IOEither[error, T]]
|
ioeither Dependency[IOResult[T]]
|
||||||
iooption Dependency[IOO.IOOption[T]]
|
iooption Dependency[IOOption[T]]
|
||||||
}
|
}
|
||||||
|
|
||||||
type multiInjectionToken[T any] struct {
|
type multiInjectionToken[T any] struct {
|
||||||
@@ -130,19 +127,19 @@ func (i *injectionToken[T]) Identity() Dependency[T] {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *injectionToken[T]) Option() Dependency[O.Option[T]] {
|
func (i *injectionToken[T]) Option() Dependency[Option[T]] {
|
||||||
return i.option
|
return i.option
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *injectionToken[T]) IOEither() Dependency[IOE.IOEither[error, T]] {
|
func (i *injectionToken[T]) IOEither() Dependency[IOResult[T]] {
|
||||||
return i.ioeither
|
return i.ioeither
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *injectionToken[T]) IOOption() Dependency[IOO.IOOption[T]] {
|
func (i *injectionToken[T]) IOOption() Dependency[IOOption[T]] {
|
||||||
return i.iooption
|
return i.iooption
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *injectionToken[T]) ProviderFactory() O.Option[DIE.ProviderFactory] {
|
func (i *injectionToken[T]) ProviderFactory() Option[DIE.ProviderFactory] {
|
||||||
return i.base.providerFactory
|
return i.base.providerFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,14 +152,14 @@ func (m *multiInjectionToken[T]) Item() InjectionToken[T] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// makeToken create a unique [InjectionToken] for a specific type
|
// makeToken create a unique [InjectionToken] for a specific type
|
||||||
func makeInjectionToken[T any](name string, providerFactory O.Option[DIE.ProviderFactory]) InjectionToken[T] {
|
func makeInjectionToken[T any](name string, providerFactory Option[DIE.ProviderFactory]) InjectionToken[T] {
|
||||||
id := genID()
|
id := genID()
|
||||||
toIdentity := toType[T]()
|
toIdentity := toType[T]()
|
||||||
return &injectionToken[T]{
|
return &injectionToken[T]{
|
||||||
token[T]{makeTokenBase(name, id, DIE.Identity, providerFactory), toIdentity},
|
token[T]{makeTokenBase(name, id, DIE.IDENTITY, providerFactory), toIdentity},
|
||||||
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", name), id, DIE.Option, toOptionType(toIdentity), providerFactory),
|
makeToken(fmt.Sprintf("Option[%s]", name), id, DIE.OPTION, toOptionType(toIdentity), providerFactory),
|
||||||
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEither, toIOEitherType(toIdentity), providerFactory),
|
makeToken(fmt.Sprintf("IOEither[%s]", name), id, DIE.IOEITHER, toIOEitherType(toIdentity), providerFactory),
|
||||||
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOption, toIOOptionType(toIdentity), providerFactory),
|
makeToken(fmt.Sprintf("IOOption[%s]", name), id, DIE.IOOPTION, toIOOptionType(toIdentity), providerFactory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,17 +184,17 @@ func MakeMultiToken[T any](name string) MultiInjectionToken[T] {
|
|||||||
providerFactory := O.None[DIE.ProviderFactory]()
|
providerFactory := O.None[DIE.ProviderFactory]()
|
||||||
// container
|
// container
|
||||||
container := &injectionToken[[]T]{
|
container := &injectionToken[[]T]{
|
||||||
token[[]T]{makeTokenBase(containerName, id, DIE.Multi|DIE.Identity, providerFactory), toContainer},
|
token[[]T]{makeTokenBase(containerName, id, DIE.MULTI|DIE.IDENTITY, providerFactory), toContainer},
|
||||||
makeToken[O.Option[[]T]](fmt.Sprintf("Option[%s]", containerName), id, DIE.Multi|DIE.Option, toOptionType(toContainer), providerFactory),
|
makeToken(fmt.Sprintf("Option[%s]", containerName), id, DIE.MULTI|DIE.OPTION, toOptionType(toContainer), providerFactory),
|
||||||
makeToken[IOE.IOEither[error, []T]](fmt.Sprintf("IOEither[%s]", containerName), id, DIE.Multi|DIE.IOEither, toIOEitherType(toContainer), providerFactory),
|
makeToken(fmt.Sprintf("IOEither[%s]", containerName), id, DIE.OPTION|DIE.IOEITHER, toIOEitherType(toContainer), providerFactory),
|
||||||
makeToken[IOO.IOOption[[]T]](fmt.Sprintf("IOOption[%s]", containerName), id, DIE.Multi|DIE.IOOption, toIOOptionType(toContainer), providerFactory),
|
makeToken(fmt.Sprintf("IOOption[%s]", containerName), id, DIE.OPTION|DIE.IOOPTION, toIOOptionType(toContainer), providerFactory),
|
||||||
}
|
}
|
||||||
// item
|
// item
|
||||||
item := &injectionToken[T]{
|
item := &injectionToken[T]{
|
||||||
token[T]{makeTokenBase(itemName, id, DIE.Item|DIE.Identity, providerFactory), toItem},
|
token[T]{makeTokenBase(itemName, id, DIE.ITEM|DIE.IDENTITY, providerFactory), toItem},
|
||||||
makeToken[O.Option[T]](fmt.Sprintf("Option[%s]", itemName), id, DIE.Item|DIE.Option, toOptionType(toItem), providerFactory),
|
makeToken(fmt.Sprintf("Option[%s]", itemName), id, DIE.ITEM|DIE.OPTION, toOptionType(toItem), providerFactory),
|
||||||
makeToken[IOE.IOEither[error, T]](fmt.Sprintf("IOEither[%s]", itemName), id, DIE.Item|DIE.IOEither, toIOEitherType(toItem), providerFactory),
|
makeToken(fmt.Sprintf("IOEither[%s]", itemName), id, DIE.ITEM|DIE.IOEITHER, toIOEitherType(toItem), providerFactory),
|
||||||
makeToken[IOO.IOOption[T]](fmt.Sprintf("IOOption[%s]", itemName), id, DIE.Item|DIE.IOOption, toIOOptionType(toItem), providerFactory),
|
makeToken(fmt.Sprintf("IOOption[%s]", itemName), id, DIE.ITEM|DIE.IOOPTION, toIOOptionType(toItem), providerFactory),
|
||||||
}
|
}
|
||||||
// returns the token
|
// returns the token
|
||||||
return &multiInjectionToken[T]{container, item}
|
return &multiInjectionToken[T]{container, item}
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ import (
|
|||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
IOO "github.com/IBM/fp-go/v2/iooption"
|
IOO "github.com/IBM/fp-go/v2/iooption"
|
||||||
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,9 +77,9 @@ func TestTokenUnerase(t *testing.T) {
|
|||||||
token := MakeToken[int]("IntToken")
|
token := MakeToken[int]("IntToken")
|
||||||
|
|
||||||
// Test successful unerase
|
// Test successful unerase
|
||||||
result := token.Unerase(42)
|
res := token.Unerase(42)
|
||||||
assert.True(t, E.IsRight(result))
|
assert.True(t, E.IsRight(res))
|
||||||
assert.Equal(t, E.Of[error](42), result)
|
assert.Equal(t, result.Of(42), res)
|
||||||
|
|
||||||
// Test failed unerase (wrong type)
|
// Test failed unerase (wrong type)
|
||||||
result2 := token.Unerase("not an int")
|
result2 := token.Unerase("not an int")
|
||||||
@@ -104,7 +106,7 @@ func TestTokenProviderFactory(t *testing.T) {
|
|||||||
assert.True(t, O.IsNone(token1.ProviderFactory()))
|
assert.True(t, O.IsNone(token1.ProviderFactory()))
|
||||||
|
|
||||||
// Token with default
|
// Token with default
|
||||||
token2 := MakeTokenWithDefault0("Token2", IOE.Of[error](42))
|
token2 := MakeTokenWithDefault0("Token2", ioresult.Of(42))
|
||||||
assert.True(t, O.IsSome(token2.ProviderFactory()))
|
assert.True(t, O.IsSome(token2.ProviderFactory()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,13 +150,13 @@ func TestOptionTokenUnerase(t *testing.T) {
|
|||||||
optionToken := token.Option()
|
optionToken := token.Option()
|
||||||
|
|
||||||
// Test successful unerase with Some
|
// Test successful unerase with Some
|
||||||
result := optionToken.Unerase(O.Of[any](42))
|
res := optionToken.Unerase(O.Of[any](42))
|
||||||
assert.True(t, E.IsRight(result))
|
assert.True(t, E.IsRight(res))
|
||||||
|
|
||||||
// Test successful unerase with None
|
// Test successful unerase with None
|
||||||
noneResult := optionToken.Unerase(O.None[any]())
|
noneResult := optionToken.Unerase(O.None[any]())
|
||||||
assert.True(t, E.IsRight(noneResult))
|
assert.True(t, E.IsRight(noneResult))
|
||||||
assert.Equal(t, E.Of[error](O.None[int]()), noneResult)
|
assert.Equal(t, result.Of(O.None[int]()), noneResult)
|
||||||
|
|
||||||
// Test failed unerase (wrong type)
|
// Test failed unerase (wrong type)
|
||||||
badResult := optionToken.Unerase(42) // Not an Option
|
badResult := optionToken.Unerase(42) // Not an Option
|
||||||
@@ -166,7 +168,7 @@ func TestIOEitherTokenUnerase(t *testing.T) {
|
|||||||
ioeitherToken := token.IOEither()
|
ioeitherToken := token.IOEither()
|
||||||
|
|
||||||
// Test successful unerase
|
// Test successful unerase
|
||||||
ioValue := IOE.Of[error](any(42))
|
ioValue := ioresult.Of(any(42))
|
||||||
result := ioeitherToken.Unerase(ioValue)
|
result := ioeitherToken.Unerase(ioValue)
|
||||||
assert.True(t, E.IsRight(result))
|
assert.True(t, E.IsRight(result))
|
||||||
|
|
||||||
@@ -222,7 +224,7 @@ func TestMultiTokenContainerUnerase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMakeTokenWithDefault(t *testing.T) {
|
func TestMakeTokenWithDefault(t *testing.T) {
|
||||||
factory := MakeProviderFactory0(IOE.Of[error](42))
|
factory := MakeProviderFactory0(ioresult.Of(42))
|
||||||
token := MakeTokenWithDefault[int]("TokenWithDefault", factory)
|
token := MakeTokenWithDefault[int]("TokenWithDefault", factory)
|
||||||
|
|
||||||
assert.NotNil(t, token)
|
assert.NotNil(t, token)
|
||||||
|
|||||||
15
v2/di/types.go
Normal file
15
v2/di/types.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package di
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/context/ioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/iooption"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Option[T any] = option.Option[T]
|
||||||
|
Result[T any] = result.Result[T]
|
||||||
|
IOResult[T any] = ioresult.IOResult[T]
|
||||||
|
IOOption[T any] = iooption.IOOption[T]
|
||||||
|
)
|
||||||
@@ -23,12 +23,13 @@ import (
|
|||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
IOO "github.com/IBM/fp-go/v2/iooption"
|
IOO "github.com/IBM/fp-go/v2/iooption"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
toOptionAny = toType[O.Option[any]]()
|
toOptionAny = toType[Option[any]]()
|
||||||
toIOEitherAny = toType[IOE.IOEither[error, any]]()
|
toIOEitherAny = toType[IOResult[any]]()
|
||||||
toIOOptionAny = toType[IOO.IOOption[any]]()
|
toIOOptionAny = toType[IOOption[any]]()
|
||||||
toArrayAny = toType[[]any]()
|
toArrayAny = toType[[]any]()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,45 +39,45 @@ func asDependency[T DIE.Dependency](t T) DIE.Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// toType converts an any to a T
|
// toType converts an any to a T
|
||||||
func toType[T any]() func(t any) E.Either[error, T] {
|
func toType[T any]() result.Kleisli[any, T] {
|
||||||
return E.ToType[T](errors.OnSome[any]("Value of type [%T] cannot be converted."))
|
return E.ToType[T](errors.OnSome[any]("Value of type [%T] cannot be converted."))
|
||||||
}
|
}
|
||||||
|
|
||||||
// toOptionType converts an any to an Option[any] and then to an Option[T]
|
// toOptionType converts an any to an Option[any] and then to an Option[T]
|
||||||
func toOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, O.Option[T]] {
|
func toOptionType[T any](item result.Kleisli[any, T]) result.Kleisli[any, Option[T]] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
toOptionAny,
|
toOptionAny,
|
||||||
E.Chain(O.Fold(
|
E.Chain(O.Fold(
|
||||||
F.Nullary2(O.None[T], E.Of[error, O.Option[T]]),
|
F.Nullary2(O.None[T], E.Of[error, Option[T]]),
|
||||||
F.Flow2(
|
F.Flow2(
|
||||||
item,
|
item,
|
||||||
E.Map[error](O.Of[T]),
|
result.Map(O.Of[T]),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// toIOEitherType converts an any to an IOEither[error, any] and then to an IOEither[error, T]
|
// toIOEitherType converts an any to an IOEither[error, any] and then to an IOEither[error, T]
|
||||||
func toIOEitherType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOE.IOEither[error, T]] {
|
func toIOEitherType[T any](item result.Kleisli[any, T]) result.Kleisli[any, IOResult[T]] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
toIOEitherAny,
|
toIOEitherAny,
|
||||||
E.Map[error](IOE.ChainEitherK(item)),
|
result.Map(IOE.ChainEitherK(item)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// toIOOptionType converts an any to an IOOption[any] and then to an IOOption[T]
|
// toIOOptionType converts an any to an IOOption[any] and then to an IOOption[T]
|
||||||
func toIOOptionType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, IOO.IOOption[T]] {
|
func toIOOptionType[T any](item result.Kleisli[any, T]) result.Kleisli[any, IOOption[T]] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
toIOOptionAny,
|
toIOOptionAny,
|
||||||
E.Map[error](IOO.ChainOptionK(F.Flow2(
|
result.Map(IOO.ChainOptionK(F.Flow2(
|
||||||
item,
|
item,
|
||||||
E.ToOption[error, T],
|
result.ToOption[T],
|
||||||
))),
|
))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// toArrayType converts an any to a []T
|
// toArrayType converts an any to a []T
|
||||||
func toArrayType[T any](item func(any) E.Either[error, T]) func(t any) E.Either[error, []T] {
|
func toArrayType[T any](item result.Kleisli[any, T]) result.Kleisli[any, []T] {
|
||||||
return F.Flow2(
|
return F.Flow2(
|
||||||
toArrayAny,
|
toArrayAny,
|
||||||
E.Chain(E.TraverseArray(item)),
|
E.Chain(E.TraverseArray(item)),
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ import (
|
|||||||
A "github.com/IBM/fp-go/v2/array"
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,13 +34,13 @@ var (
|
|||||||
|
|
||||||
func TestToType(t *testing.T) {
|
func TestToType(t *testing.T) {
|
||||||
// good cases
|
// good cases
|
||||||
assert.Equal(t, E.Of[error](10), toInt(any(10)))
|
assert.Equal(t, result.Of(10), toInt(any(10)))
|
||||||
assert.Equal(t, E.Of[error]("Carsten"), toString(any("Carsten")))
|
assert.Equal(t, result.Of("Carsten"), toString(any("Carsten")))
|
||||||
assert.Equal(t, E.Of[error](O.Of("Carsten")), toType[O.Option[string]]()(any(O.Of("Carsten"))))
|
assert.Equal(t, result.Of(O.Of("Carsten")), toType[Option[string]]()(any(O.Of("Carsten"))))
|
||||||
assert.Equal(t, E.Of[error](O.Of(any("Carsten"))), toType[O.Option[any]]()(any(O.Of(any("Carsten")))))
|
assert.Equal(t, result.Of(O.Of(any("Carsten"))), toType[Option[any]]()(any(O.Of(any("Carsten")))))
|
||||||
// failure
|
// failure
|
||||||
assert.False(t, E.IsRight(toInt(any("Carsten"))))
|
assert.False(t, E.IsRight(toInt(any("Carsten"))))
|
||||||
assert.False(t, E.IsRight(toType[O.Option[string]]()(O.Of(any("Carsten")))))
|
assert.False(t, E.IsRight(toType[Option[string]]()(O.Of(any("Carsten")))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestToOptionType(t *testing.T) {
|
func TestToOptionType(t *testing.T) {
|
||||||
@@ -47,17 +48,17 @@ func TestToOptionType(t *testing.T) {
|
|||||||
toOptInt := toOptionType(toInt)
|
toOptInt := toOptionType(toInt)
|
||||||
toOptString := toOptionType(toString)
|
toOptString := toOptionType(toString)
|
||||||
// good cases
|
// good cases
|
||||||
assert.Equal(t, E.Of[error](O.Of(10)), toOptInt(any(O.Of(any(10)))))
|
assert.Equal(t, result.Of(O.Of(10)), toOptInt(any(O.Of(any(10)))))
|
||||||
assert.Equal(t, E.Of[error](O.Of("Carsten")), toOptString(any(O.Of(any("Carsten")))))
|
assert.Equal(t, result.Of(O.Of("Carsten")), toOptString(any(O.Of(any("Carsten")))))
|
||||||
// bad cases
|
// bad cases
|
||||||
assert.False(t, E.IsRight(toOptInt(any(10))))
|
assert.False(t, E.IsRight(toOptInt(any(10))))
|
||||||
assert.False(t, E.IsRight(toOptInt(any(O.Of(10)))))
|
assert.False(t, E.IsRight(toOptInt(any(O.Of(10)))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeIOEither[T any](e E.Either[error, IOE.IOEither[error, T]]) E.Either[error, T] {
|
func invokeIOEither[T any](e Result[IOResult[T]]) Result[T] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
e,
|
e,
|
||||||
E.Chain(func(ioe IOE.IOEither[error, T]) E.Either[error, T] {
|
E.Chain(func(ioe IOResult[T]) Result[T] {
|
||||||
return ioe()
|
return ioe()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -68,11 +69,11 @@ func TestToIOEitherType(t *testing.T) {
|
|||||||
toIOEitherInt := toIOEitherType(toInt)
|
toIOEitherInt := toIOEitherType(toInt)
|
||||||
toIOEitherString := toIOEitherType(toString)
|
toIOEitherString := toIOEitherType(toString)
|
||||||
// good cases
|
// good cases
|
||||||
assert.Equal(t, E.Of[error](10), invokeIOEither(toIOEitherInt(any(IOE.Of[error](any(10))))))
|
assert.Equal(t, result.Of(10), invokeIOEither(toIOEitherInt(any(ioresult.Of(any(10))))))
|
||||||
assert.Equal(t, E.Of[error]("Carsten"), invokeIOEither(toIOEitherString(any(IOE.Of[error](any("Carsten"))))))
|
assert.Equal(t, result.Of("Carsten"), invokeIOEither(toIOEitherString(any(ioresult.Of(any("Carsten"))))))
|
||||||
// bad cases
|
// bad cases
|
||||||
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(IOE.Of[error](any(10)))))))
|
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(ioresult.Of(any(10)))))))
|
||||||
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(IOE.Of[error]("Carsten"))))))
|
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any(ioresult.Of("Carsten"))))))
|
||||||
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any("Carsten")))))
|
assert.False(t, E.IsRight(invokeIOEither(toIOEitherString(any("Carsten")))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,5 +81,5 @@ func TestToArrayType(t *testing.T) {
|
|||||||
// shortcuts
|
// shortcuts
|
||||||
toArrayString := toArrayType(toString)
|
toArrayString := toArrayType(toString)
|
||||||
// good cases
|
// good cases
|
||||||
assert.Equal(t, E.Of[error](A.From("a", "b")), toArrayString(any(A.From(any("a"), any("b")))))
|
assert.Equal(t, result.Of(A.From("a", "b")), toArrayString(any(A.From(any("a"), any("b")))))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,277 +0,0 @@
|
|||||||
# Either Benchmarks
|
|
||||||
|
|
||||||
This document describes the benchmark suite for the `either` package and how to interpret the results to identify performance bottlenecks.
|
|
||||||
|
|
||||||
## Running Benchmarks
|
|
||||||
|
|
||||||
To run all benchmarks:
|
|
||||||
```bash
|
|
||||||
cd either
|
|
||||||
go test -bench=. -benchmem
|
|
||||||
```
|
|
||||||
|
|
||||||
To run specific benchmarks:
|
|
||||||
```bash
|
|
||||||
go test -bench=BenchmarkMap -benchmem
|
|
||||||
go test -bench=BenchmarkChain -benchmem
|
|
||||||
```
|
|
||||||
|
|
||||||
To run with more iterations for stable results:
|
|
||||||
```bash
|
|
||||||
go test -bench=. -benchmem -benchtime=1000000x
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benchmark Categories
|
|
||||||
|
|
||||||
### 1. Core Constructors
|
|
||||||
- `BenchmarkLeft` - Creating Left values
|
|
||||||
- `BenchmarkRight` - Creating Right values
|
|
||||||
- `BenchmarkOf` - Creating Right values via Of
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Right construction is ~2x faster than Left (~0.45ns vs ~0.87ns)
|
|
||||||
- All constructors have zero allocations
|
|
||||||
- These are the fastest operations in the package
|
|
||||||
|
|
||||||
### 2. Predicates
|
|
||||||
- `BenchmarkIsLeft` - Testing if Either is Left
|
|
||||||
- `BenchmarkIsRight` - Testing if Either is Right
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Both predicates are extremely fast (~0.3ns)
|
|
||||||
- Zero allocations
|
|
||||||
- No performance difference between Left and Right checks
|
|
||||||
|
|
||||||
### 3. Fold Operations
|
|
||||||
- `BenchmarkMonadFold_Right/Left` - Direct fold with handlers
|
|
||||||
- `BenchmarkFold_Right/Left` - Curried fold
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Right path is ~10x faster than Left path (0.5ns vs 5-7ns)
|
|
||||||
- Curried version adds ~2ns overhead on Left path
|
|
||||||
- Zero allocations for both paths
|
|
||||||
- **Bottleneck:** Left path has higher overhead due to type assertions
|
|
||||||
|
|
||||||
### 4. Unwrap Operations
|
|
||||||
- `BenchmarkUnwrap_Right/Left` - Converting to (value, error) tuple
|
|
||||||
- `BenchmarkUnwrapError_Right/Left` - Specialized for error types
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Right path is ~10x faster than Left path
|
|
||||||
- Zero allocations
|
|
||||||
- Similar performance characteristics to Fold
|
|
||||||
|
|
||||||
### 5. Functor Operations (Map)
|
|
||||||
- `BenchmarkMonadMap_Right/Left` - Direct map
|
|
||||||
- `BenchmarkMap_Right/Left` - Curried map
|
|
||||||
- `BenchmarkMapLeft_Right/Left` - Mapping over Left channel
|
|
||||||
- `BenchmarkBiMap_Right/Left` - Mapping both channels
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Map operations: ~11-14ns, zero allocations
|
|
||||||
- MapLeft on Left: ~34ns with 1 allocation (16B)
|
|
||||||
- BiMap: ~29-39ns with 1 allocation (16B)
|
|
||||||
- **Bottleneck:** BiMap and MapLeft allocate closures
|
|
||||||
|
|
||||||
### 6. Monad Operations (Chain)
|
|
||||||
- `BenchmarkMonadChain_Right/Left` - Direct chain
|
|
||||||
- `BenchmarkChain_Right/Left` - Curried chain
|
|
||||||
- `BenchmarkChainFirst_Right/Left` - Chain preserving original value
|
|
||||||
- `BenchmarkFlatten_Right/Left` - Removing nesting
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Chain is very fast: 2-7ns, zero allocations
|
|
||||||
- **Bottleneck:** ChainFirst on Right: ~168ns with 5 allocations (120B)
|
|
||||||
- ChainFirst on Left short-circuits efficiently (~9ns)
|
|
||||||
- Curried Chain is faster than MonadChain
|
|
||||||
|
|
||||||
### 7. Applicative Operations (Ap)
|
|
||||||
- `BenchmarkMonadAp_RightRight/RightLeft/LeftRight` - Direct apply
|
|
||||||
- `BenchmarkAp_RightRight` - Curried apply
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Ap operations: 5-12ns, zero allocations
|
|
||||||
- Short-circuits efficiently when either operand is Left
|
|
||||||
- MonadAp slightly slower than curried Ap
|
|
||||||
|
|
||||||
### 8. Alternative Operations
|
|
||||||
- `BenchmarkAlt_RightRight/LeftRight` - Providing alternatives
|
|
||||||
- `BenchmarkOrElse_Right/Left` - Recovery from Left
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Very fast: 1.6-4.5ns, zero allocations
|
|
||||||
- Right path short-circuits without evaluating alternative
|
|
||||||
- Efficient error recovery mechanism
|
|
||||||
|
|
||||||
### 9. Conversion Operations
|
|
||||||
- `BenchmarkTryCatch_Success/Error` - Converting (value, error) tuples
|
|
||||||
- `BenchmarkTryCatchError_Success/Error` - Specialized for errors
|
|
||||||
- `BenchmarkSwap_Right/Left` - Swapping Left/Right
|
|
||||||
- `BenchmarkGetOrElse_Right/Left` - Extracting with default
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- All operations: 0.3-6ns, zero allocations
|
|
||||||
- Very efficient conversions
|
|
||||||
- GetOrElse on Right is extremely fast (~0.3ns)
|
|
||||||
|
|
||||||
### 10. Pipeline Operations
|
|
||||||
- `BenchmarkPipeline_Map_Right/Left` - Single Map in pipeline
|
|
||||||
- `BenchmarkPipeline_Chain_Right/Left` - Single Chain in pipeline
|
|
||||||
- `BenchmarkPipeline_Complex_Right/Left` - Multiple operations
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- **Major Bottleneck:** Pipeline operations allocate heavily
|
|
||||||
- Single Map: ~100ns with 4 allocations (96B)
|
|
||||||
- Complex pipeline: ~200-250ns with 8 allocations (192B)
|
|
||||||
- Chain in pipeline is much faster: 2-4.5ns, zero allocations
|
|
||||||
- **Recommendation:** Use direct function calls instead of F.Pipe for hot paths
|
|
||||||
|
|
||||||
### 11. Sequence Operations
|
|
||||||
- `BenchmarkMonadSequence2_RightRight/LeftRight` - Combining 2 Eithers
|
|
||||||
- `BenchmarkMonadSequence3_RightRightRight` - Combining 3 Eithers
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Sequence2: ~6ns, zero allocations
|
|
||||||
- Sequence3: ~9ns, zero allocations
|
|
||||||
- Efficient short-circuiting on Left
|
|
||||||
- Linear scaling with number of Eithers
|
|
||||||
|
|
||||||
### 12. Do-Notation Operations
|
|
||||||
- `BenchmarkDo` - Creating empty context
|
|
||||||
- `BenchmarkBind_Right` - Binding values to context
|
|
||||||
- `BenchmarkLet_Right` - Pure computations in context
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Do is extremely fast: ~0.4ns, zero allocations
|
|
||||||
- **Bottleneck:** Bind: ~130ns with 6 allocations (144B)
|
|
||||||
- Let is more efficient: ~23ns with 1 allocation (16B)
|
|
||||||
- **Recommendation:** Prefer Let over Bind when possible
|
|
||||||
|
|
||||||
### 13. String Formatting
|
|
||||||
- `BenchmarkString_Right/Left` - Converting to string
|
|
||||||
|
|
||||||
**Key Insights:**
|
|
||||||
- Right: ~69ns with 1 allocation (16B)
|
|
||||||
- Left: ~111ns with 1 allocation (48B)
|
|
||||||
- Only use for debugging, not in hot paths
|
|
||||||
|
|
||||||
## Performance Bottlenecks Summary
|
|
||||||
|
|
||||||
### Critical Bottlenecks (>100ns or multiple allocations)
|
|
||||||
|
|
||||||
1. **Pipeline operations with F.Pipe** (~100-250ns, 4-8 allocations)
|
|
||||||
- **Impact:** High - commonly used pattern
|
|
||||||
- **Mitigation:** Use direct function calls in hot paths
|
|
||||||
- **Example:**
|
|
||||||
```go
|
|
||||||
// Slow (100ns, 4 allocs)
|
|
||||||
result := F.Pipe1(either, Map[error](transform))
|
|
||||||
|
|
||||||
// Fast (12ns, 0 allocs)
|
|
||||||
result := Map[error](transform)(either)
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **ChainFirst on Right path** (~168ns, 5 allocations)
|
|
||||||
- **Impact:** Medium - used for side effects
|
|
||||||
- **Mitigation:** Avoid in hot paths, use direct Chain if side effect result not needed
|
|
||||||
|
|
||||||
3. **Bind in do-notation** (~130ns, 6 allocations)
|
|
||||||
- **Impact:** Medium - used in complex workflows
|
|
||||||
- **Mitigation:** Use Let for pure computations, minimize Bind calls
|
|
||||||
|
|
||||||
### Minor Bottlenecks (20-50ns or 1 allocation)
|
|
||||||
|
|
||||||
4. **BiMap operations** (~29-39ns, 1 allocation)
|
|
||||||
- **Impact:** Low - less commonly used
|
|
||||||
- **Mitigation:** Use Map or MapLeft separately if only one channel needs transformation
|
|
||||||
|
|
||||||
5. **MapLeft on Left path** (~34ns, 1 allocation)
|
|
||||||
- **Impact:** Low - error path typically not hot
|
|
||||||
- **Mitigation:** None needed unless in critical error handling
|
|
||||||
|
|
||||||
## Optimization Recommendations
|
|
||||||
|
|
||||||
### For Hot Paths
|
|
||||||
|
|
||||||
1. **Prefer direct function calls over pipelines:**
|
|
||||||
```go
|
|
||||||
// Instead of:
|
|
||||||
result := F.Pipe3(either, Map(f1), Chain(f2), Map(f3))
|
|
||||||
|
|
||||||
// Use:
|
|
||||||
result := Map(f3)(Chain(f2)(Map(f1)(either)))
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Use Chain instead of ChainFirst when possible:**
|
|
||||||
```go
|
|
||||||
// If you don't need the original value:
|
|
||||||
result := Chain(f)(either) // Fast
|
|
||||||
|
|
||||||
// Instead of:
|
|
||||||
result := ChainFirst(func(a A) Either[E, B] {
|
|
||||||
f(a)
|
|
||||||
return Right[error](a)
|
|
||||||
})(either) // Slow
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Prefer Let over Bind in do-notation:**
|
|
||||||
```go
|
|
||||||
// For pure computations:
|
|
||||||
result := Let(setter, pureFunc)(either) // 23ns
|
|
||||||
|
|
||||||
// Instead of:
|
|
||||||
result := Bind(setter, func(s S) Either[E, T] {
|
|
||||||
return Right[error](pureFunc(s))
|
|
||||||
})(either) // 130ns
|
|
||||||
```
|
|
||||||
|
|
||||||
### For Error Paths
|
|
||||||
|
|
||||||
- Left path operations are generally slower but acceptable since errors are exceptional
|
|
||||||
- Focus optimization on Right (success) path
|
|
||||||
- Use MapLeft and BiMap freely in error handling code
|
|
||||||
|
|
||||||
### Memory Considerations
|
|
||||||
|
|
||||||
- Most operations have zero allocations
|
|
||||||
- Avoid string formatting (String()) in production code
|
|
||||||
- Pipeline operations allocate - use sparingly in hot paths
|
|
||||||
|
|
||||||
## Comparative Analysis
|
|
||||||
|
|
||||||
### Fastest Operations (<5ns)
|
|
||||||
- Constructors (Left, Right, Of)
|
|
||||||
- Predicates (IsLeft, IsRight)
|
|
||||||
- GetOrElse on Right
|
|
||||||
- Chain (curried version)
|
|
||||||
- Alt/OrElse on Right
|
|
||||||
- Do
|
|
||||||
|
|
||||||
### Medium Speed (5-20ns)
|
|
||||||
- Fold operations
|
|
||||||
- Unwrap operations
|
|
||||||
- Map operations
|
|
||||||
- MonadChain
|
|
||||||
- Ap operations
|
|
||||||
- Sequence operations
|
|
||||||
- Let
|
|
||||||
|
|
||||||
### Slower Operations (>20ns)
|
|
||||||
- BiMap
|
|
||||||
- MapLeft on Left
|
|
||||||
- String formatting
|
|
||||||
- Pipeline operations
|
|
||||||
- Bind
|
|
||||||
- ChainFirst on Right
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
The `either` package is highly optimized with most operations completing in nanoseconds with zero allocations. The main performance considerations are:
|
|
||||||
|
|
||||||
1. Avoid F.Pipe in hot paths - use direct function calls
|
|
||||||
2. Prefer Let over Bind in do-notation
|
|
||||||
3. Use Chain instead of ChainFirst when the original value isn't needed
|
|
||||||
4. The Right (success) path is consistently faster than Left (error) path
|
|
||||||
5. Most operations have zero allocations, making them GC-friendly
|
|
||||||
|
|
||||||
For typical use cases, the performance is excellent. Only in extremely hot paths (millions of operations per second) should you consider the micro-optimizations suggested above.
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
2025/11/08 09:58:27 out: test
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
pkg: github.com/IBM/fp-go/v2/either
|
|
||||||
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
|
|
||||||
BenchmarkLeft-16 1000000000 0.8874 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRight-16 1000000000 0.5417 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkOf-16 1000000000 0.5751 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkIsLeft-16 1000000000 0.2564 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkIsRight-16 1000000000 0.2269 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadFold_Right-16 1000000000 0.2320 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadFold_Left-16 1000000000 0.2322 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFold_Right-16 1000000000 0.2258 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFold_Left-16 1000000000 0.2313 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrap_Right-16 1000000000 0.2263 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrap_Left-16 1000000000 0.2294 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrapError_Right-16 1000000000 0.2228 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrapError_Left-16 1000000000 0.2253 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadMap_Right-16 174464096 6.904 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadMap_Left-16 268986886 4.544 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMap_Right-16 183861918 7.383 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMap_Left-16 275134764 4.550 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMapLeft_Right-16 296297173 4.534 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMapLeft_Left-16 157772442 9.138 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBiMap_Right-16 29180962 42.42 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBiMap_Left-16 28203771 42.58 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadChain_Right-16 461887719 2.479 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadChain_Left-16 572442338 1.964 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkChain_Right-16 1000000000 0.8653 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkChain_Left-16 1000000000 0.7443 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkChainFirst_Right-16 10762824 126.3 ns/op 120 B/op 5 allocs/op
|
|
||||||
BenchmarkChainFirst_Left-16 253755582 4.394 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFlatten_Right-16 100000000 10.23 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFlatten_Left-16 100000000 10.09 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadAp_RightRight-16 176284840 6.734 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadAp_RightLeft-16 434471394 2.874 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadAp_LeftRight-16 451601320 2.795 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkAp_RightRight-16 177040815 6.786 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkAlt_RightRight-16 1000000000 0.8134 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkAlt_LeftRight-16 1000000000 0.8202 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkOrElse_Right-16 1000000000 0.8501 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkOrElse_Left-16 1000000000 0.6825 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatch_Success-16 565892258 2.091 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatch_Error-16 408437362 2.905 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatchError_Success-16 562500350 2.150 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatchError_Error-16 424754853 2.769 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkSwap_Right-16 1000000000 1.173 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkSwap_Left-16 995449963 1.162 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGetOrElse_Right-16 1000000000 0.2427 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGetOrElse_Left-16 1000000000 0.2457 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkPipeline_Map_Right-16 12972158 86.78 ns/op 96 B/op 4 allocs/op
|
|
||||||
BenchmarkPipeline_Map_Left-16 13715413 86.44 ns/op 96 B/op 4 allocs/op
|
|
||||||
BenchmarkPipeline_Chain_Right-16 1000000000 0.8321 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkPipeline_Chain_Left-16 1000000000 0.6849 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkPipeline_Complex_Right-16 6963496 173.1 ns/op 192 B/op 8 allocs/op
|
|
||||||
BenchmarkPipeline_Complex_Left-16 6503500 174.6 ns/op 192 B/op 8 allocs/op
|
|
||||||
BenchmarkMonadSequence2_RightRight-16 236820728 5.060 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadSequence2_LeftRight-16 545203750 2.215 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadSequence3_RightRightRight-16 120316371 9.657 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkDo-16 1000000000 0.2182 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBind_Right-16 8784483 137.3 ns/op 144 B/op 6 allocs/op
|
|
||||||
BenchmarkLet_Right-16 51356451 23.61 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkString_Right-16 15664588 79.59 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkString_Left-16 12226196 103.0 ns/op 48 B/op 1 allocs/op
|
|
||||||
PASS
|
|
||||||
ok github.com/IBM/fp-go/v2/either 66.778s
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
2025/11/08 09:02:49 out: test
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
pkg: github.com/IBM/fp-go/v2/either
|
|
||||||
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
|
|
||||||
BenchmarkLeft-16 71784931 16.52 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkRight-16 132961911 8.799 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkOf-16 132703154 8.894 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkIsLeft-16 1000000000 0.2188 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkIsRight-16 1000000000 0.2202 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadFold_Right-16 1000000000 0.2215 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadFold_Left-16 1000000000 0.2198 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFold_Right-16 1000000000 0.4467 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFold_Left-16 1000000000 0.2616 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrap_Right-16 1000000000 0.2937 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrap_Left-16 1000000000 0.3019 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrapError_Right-16 1000000000 0.5962 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrapError_Left-16 1000000000 0.2343 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadMap_Right-16 78786684 15.06 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadMap_Left-16 60553860 20.47 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkMap_Right-16 81438198 14.52 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkMap_Left-16 61249489 22.27 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkMapLeft_Right-16 100000000 11.54 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkMapLeft_Left-16 53107918 22.85 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkBiMap_Right-16 47468728 22.02 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkBiMap_Left-16 53815036 22.93 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadChain_Right-16 100000000 10.73 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadChain_Left-16 71138511 18.14 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkChain_Right-16 129499905 9.254 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkChain_Left-16 62561910 16.93 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkChainFirst_Right-16 9357235 134.4 ns/op 144 B/op 7 allocs/op
|
|
||||||
BenchmarkChainFirst_Left-16 46529842 21.52 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkFlatten_Right-16 353777130 3.336 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFlatten_Left-16 63614917 20.03 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadAp_RightRight-16 81210578 14.48 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadAp_RightLeft-16 63263110 19.65 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadAp_LeftRight-16 60824167 17.87 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkAp_RightRight-16 81860413 14.58 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkAlt_RightRight-16 130601511 8.955 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkAlt_LeftRight-16 136723171 9.062 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkOrElse_Right-16 130252060 9.187 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkOrElse_Left-16 137806992 9.325 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkTryCatch_Success-16 100000000 10.24 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkTryCatch_Error-16 61422231 19.29 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkTryCatchError_Success-16 100000000 10.06 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkTryCatchError_Error-16 68159355 18.74 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkSwap_Right-16 125292858 9.739 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkSwap_Left-16 64153282 18.13 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkGetOrElse_Right-16 1000000000 0.2167 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGetOrElse_Left-16 1000000000 0.2154 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkPipeline_Map_Right-16 13649640 88.69 ns/op 104 B/op 5 allocs/op
|
|
||||||
BenchmarkPipeline_Map_Left-16 11999940 101.6 ns/op 112 B/op 5 allocs/op
|
|
||||||
BenchmarkPipeline_Chain_Right-16 135232720 8.888 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkPipeline_Chain_Left-16 72481712 17.98 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkPipeline_Complex_Right-16 6314605 185.0 ns/op 216 B/op 11 allocs/op
|
|
||||||
BenchmarkPipeline_Complex_Left-16 5850564 217.1 ns/op 240 B/op 11 allocs/op
|
|
||||||
BenchmarkMonadSequence2_RightRight-16 85073667 14.56 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadSequence2_LeftRight-16 69183006 19.21 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadSequence3_RightRightRight-16 73083387 16.82 ns/op 8 B/op 1 allocs/op
|
|
||||||
BenchmarkDo-16 1000000000 0.2121 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBind_Right-16 8551692 142.5 ns/op 160 B/op 8 allocs/op
|
|
||||||
BenchmarkLet_Right-16 40338846 30.32 ns/op 24 B/op 2 allocs/op
|
|
||||||
BenchmarkString_Right-16 15758826 73.75 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkString_Left-16 12798620 94.71 ns/op 48 B/op 1 allocs/op
|
|
||||||
PASS
|
|
||||||
ok github.com/IBM/fp-go/v2/either 72.607s
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
2025/11/08 09:04:15 out: test
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
pkg: github.com/IBM/fp-go/v2/either
|
|
||||||
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
|
|
||||||
BenchmarkLeft-16 1000000000 0.8335 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkRight-16 1000000000 0.4256 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkOf-16 1000000000 0.4370 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkIsLeft-16 1000000000 0.2199 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkIsRight-16 1000000000 0.2181 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadFold_Right-16 1000000000 0.3209 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadFold_Left-16 289017271 4.176 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFold_Right-16 1000000000 0.4687 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFold_Left-16 274668618 4.669 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrap_Right-16 1000000000 0.3520 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrap_Left-16 273187717 4.226 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrapError_Right-16 1000000000 0.4503 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkUnwrapError_Left-16 279699219 4.285 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadMap_Right-16 152084527 7.855 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadMap_Left-16 181258614 6.711 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMap_Right-16 146735268 8.328 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMap_Left-16 136155859 8.829 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMapLeft_Right-16 254870900 4.687 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMapLeft_Left-16 44555998 27.87 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkBiMap_Right-16 53596072 22.82 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkBiMap_Left-16 44864508 26.88 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkMonadChain_Right-16 350417858 3.456 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadChain_Left-16 228175792 5.328 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkChain_Right-16 681885982 1.558 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkChain_Left-16 273064258 4.305 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkChainFirst_Right-16 11384614 107.1 ns/op 120 B/op 5 allocs/op
|
|
||||||
BenchmarkChainFirst_Left-16 145602547 8.177 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFlatten_Right-16 333052550 3.563 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkFlatten_Left-16 178190731 6.822 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadAp_RightRight-16 153308394 7.690 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadAp_RightLeft-16 187848016 6.593 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadAp_LeftRight-16 226528400 5.175 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkAp_RightRight-16 163718392 7.422 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkAlt_RightRight-16 773928914 1.603 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkAlt_LeftRight-16 296220595 4.034 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkOrElse_Right-16 772122764 1.587 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkOrElse_Left-16 295411228 4.018 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatch_Success-16 446405984 2.691 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatch_Error-16 445387840 2.750 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatchError_Success-16 442356684 2.682 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkTryCatchError_Error-16 441426070 2.801 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkSwap_Right-16 448856370 2.819 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkSwap_Left-16 213215637 5.602 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGetOrElse_Right-16 1000000000 0.2997 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkGetOrElse_Left-16 270844960 4.397 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkPipeline_Map_Right-16 13732585 83.72 ns/op 96 B/op 4 allocs/op
|
|
||||||
BenchmarkPipeline_Map_Left-16 15044474 84.31 ns/op 96 B/op 4 allocs/op
|
|
||||||
BenchmarkPipeline_Chain_Right-16 763123821 1.638 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkPipeline_Chain_Left-16 265623768 4.513 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkPipeline_Complex_Right-16 6963698 173.9 ns/op 192 B/op 8 allocs/op
|
|
||||||
BenchmarkPipeline_Complex_Left-16 6829795 178.2 ns/op 192 B/op 8 allocs/op
|
|
||||||
BenchmarkMonadSequence2_RightRight-16 188600779 6.451 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadSequence2_LeftRight-16 203297380 5.912 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkMonadSequence3_RightRightRight-16 127004353 9.377 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkDo-16 1000000000 0.2096 ns/op 0 B/op 0 allocs/op
|
|
||||||
BenchmarkBind_Right-16 9656920 124.5 ns/op 144 B/op 6 allocs/op
|
|
||||||
BenchmarkLet_Right-16 49086178 22.46 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkString_Right-16 16014156 71.13 ns/op 16 B/op 1 allocs/op
|
|
||||||
BenchmarkString_Left-16 11845627 94.86 ns/op 48 B/op 1 allocs/op
|
|
||||||
PASS
|
|
||||||
ok github.com/IBM/fp-go/v2/either 82.686s
|
|
||||||
@@ -58,8 +58,8 @@ func Do[E, S any](
|
|||||||
//go:inline
|
//go:inline
|
||||||
func Bind[E, S1, S2, T any](
|
func Bind[E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) Either[E, T],
|
f Kleisli[E, S1, T],
|
||||||
) func(Either[E, S1]) Either[E, S2] {
|
) Operator[E, S1, S2] {
|
||||||
return C.Bind(
|
return C.Bind(
|
||||||
Chain[E, S1, S2],
|
Chain[E, S1, S2],
|
||||||
Map[E, T, S2],
|
Map[E, T, S2],
|
||||||
@@ -88,7 +88,7 @@ func Bind[E, S1, S2, T any](
|
|||||||
func Let[E, S1, S2, T any](
|
func Let[E, S1, S2, T any](
|
||||||
key func(T) func(S1) S2,
|
key func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) func(Either[E, S1]) Either[E, S2] {
|
) Operator[E, S1, S2] {
|
||||||
return F.Let(
|
return F.Let(
|
||||||
Map[E, S1, S2],
|
Map[E, S1, S2],
|
||||||
key,
|
key,
|
||||||
@@ -115,7 +115,7 @@ func Let[E, S1, S2, T any](
|
|||||||
func LetTo[E, S1, S2, T any](
|
func LetTo[E, S1, S2, T any](
|
||||||
key func(T) func(S1) S2,
|
key func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) func(Either[E, S1]) Either[E, S2] {
|
) Operator[E, S1, S2] {
|
||||||
return F.LetTo(
|
return F.LetTo(
|
||||||
Map[E, S1, S2],
|
Map[E, S1, S2],
|
||||||
key,
|
key,
|
||||||
@@ -137,7 +137,7 @@ func LetTo[E, S1, S2, T any](
|
|||||||
//go:inline
|
//go:inline
|
||||||
func BindTo[E, S1, T any](
|
func BindTo[E, S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func(Either[E, T]) Either[E, S1] {
|
) Operator[E, T, S1] {
|
||||||
return C.BindTo(
|
return C.BindTo(
|
||||||
Map[E, T, S1],
|
Map[E, T, S1],
|
||||||
setter,
|
setter,
|
||||||
@@ -164,7 +164,7 @@ func BindTo[E, S1, T any](
|
|||||||
func ApS[E, S1, S2, T any](
|
func ApS[E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa Either[E, T],
|
fa Either[E, T],
|
||||||
) func(Either[E, S1]) Either[E, S2] {
|
) Operator[E, S1, S2] {
|
||||||
return A.ApS(
|
return A.ApS(
|
||||||
Ap[S2, E, T],
|
Ap[S2, E, T],
|
||||||
Map[E, S1, func(T) S2],
|
Map[E, S1, func(T) S2],
|
||||||
@@ -271,9 +271,9 @@ func ApSL[E, S, T any](
|
|||||||
//go:inline
|
//go:inline
|
||||||
func BindL[E, S, T any](
|
func BindL[E, S, T any](
|
||||||
lens Lens[S, T],
|
lens Lens[S, T],
|
||||||
f func(T) Either[E, T],
|
f Kleisli[E, T, T],
|
||||||
) Endomorphism[Either[E, S]] {
|
) Endomorphism[Either[E, S]] {
|
||||||
return Bind[E, S, S, T](lens.Set, function.Flow2(lens.Get, f))
|
return Bind(lens.Set, function.Flow2(lens.Get, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||||
@@ -323,7 +323,7 @@ func LetL[E, S, T any](
|
|||||||
lens Lens[S, T],
|
lens Lens[S, T],
|
||||||
f Endomorphism[T],
|
f Endomorphism[T],
|
||||||
) Endomorphism[Either[E, S]] {
|
) Endomorphism[Either[E, S]] {
|
||||||
return Let[E, S, S, T](lens.Set, function.Flow2(lens.Get, f))
|
return Let[E](lens.Set, function.Flow2(lens.Get, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
// LetToL attaches a constant value to a context using a lens-based setter.
|
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||||
@@ -371,5 +371,5 @@ func LetToL[E, S, T any](
|
|||||||
lens Lens[S, T],
|
lens Lens[S, T],
|
||||||
b T,
|
b T,
|
||||||
) Endomorphism[Either[E, S]] {
|
) Endomorphism[Either[E, S]] {
|
||||||
return LetTo[E, S, S, T](lens.Set, b)
|
return LetTo[E](lens.Set, b)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func MonadMap[E, A, B any](fa Either[E, A], f func(a A) B) Either[E, B] {
|
|||||||
//
|
//
|
||||||
// result := either.MonadBiMap(
|
// result := either.MonadBiMap(
|
||||||
// either.Left[int](errors.New("error")),
|
// either.Left[int](errors.New("error")),
|
||||||
// func(e error) string { return e.Error() },
|
// error.Error,
|
||||||
// func(n int) string { return fmt.Sprint(n) },
|
// func(n int) string { return fmt.Sprint(n) },
|
||||||
// ) // Left("error")
|
// ) // Left("error")
|
||||||
func MonadBiMap[E1, E2, A, B any](fa Either[E1, A], f func(E1) E2, g func(a A) B) Either[E2, B] {
|
func MonadBiMap[E1, E2, A, B any](fa Either[E1, A], f func(E1) E2, g func(a A) B) Either[E2, B] {
|
||||||
@@ -131,7 +131,7 @@ func MapTo[E, A, B any](b B) Operator[E, A, B] {
|
|||||||
//
|
//
|
||||||
// result := either.MonadMapLeft(
|
// result := either.MonadMapLeft(
|
||||||
// either.Left[int](errors.New("error")),
|
// either.Left[int](errors.New("error")),
|
||||||
// func(e error) string { return e.Error() },
|
// error.Error,
|
||||||
// ) // Left("error")
|
// ) // Left("error")
|
||||||
func MonadMapLeft[E1, A, E2 any](fa Either[E1, A], f func(E1) E2) Either[E2, A] {
|
func MonadMapLeft[E1, A, E2 any](fa Either[E1, A], f func(E1) E2) Either[E2, A] {
|
||||||
return MonadFold(fa, F.Flow2(f, Left[A, E2]), Right[E2, A])
|
return MonadFold(fa, F.Flow2(f, Left[A, E2]), Right[E2, A])
|
||||||
@@ -362,7 +362,7 @@ func Fold[E, A, B any](onLeft func(E) B, onRight func(A) B) func(Either[E, A]) B
|
|||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func UnwrapError[A any](ma Either[error, A]) (A, error) {
|
func UnwrapError[A any](ma Either[error, A]) (A, error) {
|
||||||
return Unwrap[error](ma)
|
return Unwrap(ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromPredicate creates an Either based on a predicate.
|
// FromPredicate creates an Either based on a predicate.
|
||||||
@@ -381,7 +381,7 @@ func FromPredicate[E, A any](pred func(A) bool, onFalse func(A) E) func(A) Eithe
|
|||||||
if pred(a) {
|
if pred(a) {
|
||||||
return Right[E](a)
|
return Right[E](a)
|
||||||
}
|
}
|
||||||
return Left[A, E](onFalse(a))
|
return Left[A](onFalse(a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
// return either.Right[error](0) // default value
|
||||||
// })
|
// })
|
||||||
// result := recover(either.Left[int](errors.New("fail"))) // Right(0)
|
// 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])
|
return Fold(onLeft, Of[E, A])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
benchErr = errors.New("benchmark error")
|
errBench = errors.New("benchmark error")
|
||||||
benchResult Either[error, int]
|
benchResult Either[error, int]
|
||||||
benchBool bool
|
benchBool bool
|
||||||
benchInt int
|
benchInt int
|
||||||
@@ -34,7 +34,7 @@ var (
|
|||||||
func BenchmarkLeft(b *testing.B) {
|
func BenchmarkLeft(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
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
|
// Benchmark predicates
|
||||||
func BenchmarkIsLeft(b *testing.B) {
|
func BenchmarkIsLeft(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -84,7 +84,7 @@ func BenchmarkMonadFold_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadFold_Left(b *testing.B) {
|
func BenchmarkMonadFold_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
onLeft := func(e error) int { return 0 }
|
onLeft := func(e error) int { return 0 }
|
||||||
onRight := func(a int) int { return a * 2 }
|
onRight := func(a int) int { return a * 2 }
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -108,7 +108,7 @@ func BenchmarkFold_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFold_Left(b *testing.B) {
|
func BenchmarkFold_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
folder := Fold(
|
folder := Fold(
|
||||||
func(e error) int { return 0 },
|
func(e error) int { return 0 },
|
||||||
func(a int) int { return a * 2 },
|
func(a int) int { return a * 2 },
|
||||||
@@ -131,7 +131,7 @@ func BenchmarkUnwrap_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkUnwrap_Left(b *testing.B) {
|
func BenchmarkUnwrap_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -149,7 +149,7 @@ func BenchmarkUnwrapError_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkUnwrapError_Left(b *testing.B) {
|
func BenchmarkUnwrapError_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -169,7 +169,7 @@ func BenchmarkMonadMap_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadMap_Left(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 }
|
mapper := func(a int) int { return a * 2 }
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -189,7 +189,7 @@ func BenchmarkMap_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMap_Left(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 })
|
mapper := Map[error](func(a int) int { return a * 2 })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -200,7 +200,7 @@ func BenchmarkMap_Left(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMapLeft_Right(b *testing.B) {
|
func BenchmarkMapLeft_Right(b *testing.B) {
|
||||||
right := Right[error](42)
|
right := Right[error](42)
|
||||||
mapper := MapLeft[int](func(e error) string { return e.Error() })
|
mapper := MapLeft[int](error.Error)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -209,8 +209,8 @@ func BenchmarkMapLeft_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMapLeft_Left(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() })
|
mapper := MapLeft[int](error.Error)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -221,7 +221,7 @@ func BenchmarkMapLeft_Left(b *testing.B) {
|
|||||||
func BenchmarkBiMap_Right(b *testing.B) {
|
func BenchmarkBiMap_Right(b *testing.B) {
|
||||||
right := Right[error](42)
|
right := Right[error](42)
|
||||||
mapper := BiMap(
|
mapper := BiMap(
|
||||||
func(e error) string { return e.Error() },
|
error.Error,
|
||||||
func(a int) string { return "value" },
|
func(a int) string { return "value" },
|
||||||
)
|
)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -232,9 +232,9 @@ func BenchmarkBiMap_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkBiMap_Left(b *testing.B) {
|
func BenchmarkBiMap_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
mapper := BiMap(
|
mapper := BiMap(
|
||||||
func(e error) string { return e.Error() },
|
error.Error,
|
||||||
func(a int) string { return "value" },
|
func(a int) string { return "value" },
|
||||||
)
|
)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -256,7 +256,7 @@ func BenchmarkMonadChain_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadChain_Left(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) }
|
chainer := func(a int) Either[error, int] { return Right[error](a * 2) }
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -267,7 +267,7 @@ func BenchmarkMonadChain_Left(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkChain_Right(b *testing.B) {
|
func BenchmarkChain_Right(b *testing.B) {
|
||||||
right := Right[error](42)
|
right := Right[error](42)
|
||||||
chainer := Chain[error](func(a int) Either[error, int] { return Right[error](a * 2) })
|
chainer := Chain(func(a int) Either[error, int] { return Right[error](a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -276,8 +276,8 @@ func BenchmarkChain_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChain_Left(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) })
|
chainer := Chain(func(a int) Either[error, int] { return Right[error](a * 2) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -287,7 +287,7 @@ func BenchmarkChain_Left(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkChainFirst_Right(b *testing.B) {
|
func BenchmarkChainFirst_Right(b *testing.B) {
|
||||||
right := Right[error](42)
|
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.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -296,8 +296,8 @@ func BenchmarkChainFirst_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChainFirst_Left(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") })
|
chainer := ChainFirst(func(a int) Either[error, string] { return Right[error]("logged") })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -315,7 +315,7 @@ func BenchmarkFlatten_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFlatten_Left(b *testing.B) {
|
func BenchmarkFlatten_Left(b *testing.B) {
|
||||||
nested := Left[Either[error, int]](benchErr)
|
nested := Left[Either[error, int]](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -336,7 +336,7 @@ func BenchmarkMonadAp_RightRight(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkMonadAp_RightLeft(b *testing.B) {
|
func BenchmarkMonadAp_RightLeft(b *testing.B) {
|
||||||
fab := Right[error](func(a int) int { return a * 2 })
|
fab := Right[error](func(a int) int { return a * 2 })
|
||||||
fa := Left[int](benchErr)
|
fa := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -345,7 +345,7 @@ func BenchmarkMonadAp_RightLeft(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadAp_LeftRight(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)
|
fa := Right[error](42)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
@@ -357,7 +357,7 @@ func BenchmarkMonadAp_LeftRight(b *testing.B) {
|
|||||||
func BenchmarkAp_RightRight(b *testing.B) {
|
func BenchmarkAp_RightRight(b *testing.B) {
|
||||||
fab := Right[error](func(a int) int { return a * 2 })
|
fab := Right[error](func(a int) int { return a * 2 })
|
||||||
fa := Right[error](42)
|
fa := Right[error](42)
|
||||||
ap := Ap[int, error, int](fa)
|
ap := Ap[int](fa)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -368,7 +368,7 @@ func BenchmarkAp_RightRight(b *testing.B) {
|
|||||||
// Benchmark alternative operations
|
// Benchmark alternative operations
|
||||||
func BenchmarkAlt_RightRight(b *testing.B) {
|
func BenchmarkAlt_RightRight(b *testing.B) {
|
||||||
right := Right[error](42)
|
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.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -377,8 +377,8 @@ func BenchmarkAlt_RightRight(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkAlt_LeftRight(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) })
|
alternative := Alt(func() Either[error, int] { return Right[error](99) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -388,7 +388,7 @@ func BenchmarkAlt_LeftRight(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkOrElse_Right(b *testing.B) {
|
func BenchmarkOrElse_Right(b *testing.B) {
|
||||||
right := Right[error](42)
|
right := Right[error](42)
|
||||||
recover := OrElse[error](func(e error) Either[error, int] { return Right[error](0) })
|
recover := OrElse(func(e error) Either[error, int] { return Right[error](0) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -397,8 +397,8 @@ func BenchmarkOrElse_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkOrElse_Left(b *testing.B) {
|
func BenchmarkOrElse_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
recover := OrElse[error](func(e error) Either[error, int] { return Right[error](0) })
|
recover := OrElse(func(e error) Either[error, int] { return Right[error](0) })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -419,7 +419,7 @@ func BenchmarkTryCatch_Error(b *testing.B) {
|
|||||||
onThrow := func(err error) error { return err }
|
onThrow := func(err error) error { return err }
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
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) {
|
func BenchmarkTryCatchError_Error(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
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) {
|
func BenchmarkSwap_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -457,7 +457,7 @@ func BenchmarkSwap_Left(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkGetOrElse_Right(b *testing.B) {
|
func BenchmarkGetOrElse_Right(b *testing.B) {
|
||||||
right := Right[error](42)
|
right := Right[error](42)
|
||||||
getter := GetOrElse[error](func(e error) int { return 0 })
|
getter := GetOrElse(func(e error) int { return 0 })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -466,8 +466,8 @@ func BenchmarkGetOrElse_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkGetOrElse_Left(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 })
|
getter := GetOrElse(func(e error) int { return 0 })
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -489,7 +489,7 @@ func BenchmarkPipeline_Map_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPipeline_Map_Left(b *testing.B) {
|
func BenchmarkPipeline_Map_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -507,19 +507,19 @@ func BenchmarkPipeline_Chain_Right(b *testing.B) {
|
|||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchResult = F.Pipe1(
|
benchResult = F.Pipe1(
|
||||||
right,
|
right,
|
||||||
Chain[error](func(x int) Either[error, int] { return Right[error](x * 2) }),
|
Chain(func(x int) Either[error, int] { return Right[error](x * 2) }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPipeline_Chain_Left(b *testing.B) {
|
func BenchmarkPipeline_Chain_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchResult = F.Pipe1(
|
benchResult = F.Pipe1(
|
||||||
left,
|
left,
|
||||||
Chain[error](func(x int) Either[error, int] { return Right[error](x * 2) }),
|
Chain(func(x int) Either[error, int] { return Right[error](x * 2) }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,21 +532,21 @@ func BenchmarkPipeline_Complex_Right(b *testing.B) {
|
|||||||
benchResult = F.Pipe3(
|
benchResult = F.Pipe3(
|
||||||
right,
|
right,
|
||||||
Map[error](func(x int) int { return x * 2 }),
|
Map[error](func(x int) int { return x * 2 }),
|
||||||
Chain[error](func(x int) Either[error, int] { return Right[error](x + 1) }),
|
Chain(func(x int) Either[error, int] { return Right[error](x + 1) }),
|
||||||
Map[error](func(x int) int { return x * 2 }),
|
Map[error](func(x int) int { return x * 2 }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPipeline_Complex_Left(b *testing.B) {
|
func BenchmarkPipeline_Complex_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchResult = F.Pipe3(
|
benchResult = F.Pipe3(
|
||||||
left,
|
left,
|
||||||
Map[error](func(x int) int { return x * 2 }),
|
Map[error](func(x int) int { return x * 2 }),
|
||||||
Chain[error](func(x int) Either[error, int] { return Right[error](x + 1) }),
|
Chain(func(x int) Either[error, int] { return Right[error](x + 1) }),
|
||||||
Map[error](func(x int) int { return x * 2 }),
|
Map[error](func(x int) int { return x * 2 }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -565,7 +565,7 @@ func BenchmarkMonadSequence2_RightRight(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMonadSequence2_LeftRight(b *testing.B) {
|
func BenchmarkMonadSequence2_LeftRight(b *testing.B) {
|
||||||
e1 := Left[int](benchErr)
|
e1 := Left[int](errBench)
|
||||||
e2 := Right[error](20)
|
e2 := Right[error](20)
|
||||||
f := func(a, b int) Either[error, int] { return Right[error](a + b) }
|
f := func(a, b int) Either[error, int] { return Right[error](a + b) }
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@@ -599,7 +599,7 @@ func BenchmarkDo(b *testing.B) {
|
|||||||
func BenchmarkBind_Right(b *testing.B) {
|
func BenchmarkBind_Right(b *testing.B) {
|
||||||
type State struct{ value int }
|
type State struct{ value int }
|
||||||
initial := Do[error](State{})
|
initial := Do[error](State{})
|
||||||
binder := Bind[error, State, State](
|
binder := Bind(
|
||||||
func(v int) func(State) State {
|
func(v int) func(State) State {
|
||||||
return func(s State) State { return State{value: v} }
|
return func(s State) State { return State{value: v} }
|
||||||
},
|
},
|
||||||
@@ -617,7 +617,7 @@ func BenchmarkBind_Right(b *testing.B) {
|
|||||||
func BenchmarkLet_Right(b *testing.B) {
|
func BenchmarkLet_Right(b *testing.B) {
|
||||||
type State struct{ value int }
|
type State struct{ value int }
|
||||||
initial := Right[error](State{value: 10})
|
initial := Right[error](State{value: 10})
|
||||||
letter := Let[error, State, State](
|
letter := Let[error](
|
||||||
func(v int) func(State) State {
|
func(v int) func(State) State {
|
||||||
return func(s State) State { return State{value: s.value + v} }
|
return func(s State) State { return State{value: s.value + v} }
|
||||||
},
|
},
|
||||||
@@ -641,12 +641,10 @@ func BenchmarkString_Right(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkString_Left(b *testing.B) {
|
func BenchmarkString_Left(b *testing.B) {
|
||||||
left := Left[int](benchErr)
|
left := Left[int](errBench)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
benchString = left.String()
|
benchString = left.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Made with Bob
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ import (
|
|||||||
|
|
||||||
// Test BiMap
|
// Test BiMap
|
||||||
func TestBiMap(t *testing.T) {
|
func TestBiMap(t *testing.T) {
|
||||||
errToStr := func(e error) string { return e.Error() }
|
errToStr := error.Error
|
||||||
intToStr := func(i int) string { return strconv.Itoa(i) }
|
intToStr := strconv.Itoa
|
||||||
|
|
||||||
// Test Right case
|
// Test Right case
|
||||||
result := BiMap(errToStr, intToStr)(Right[error](42))
|
result := BiMap(errToStr, intToStr)(Right[error](42))
|
||||||
@@ -43,8 +43,8 @@ func TestBiMap(t *testing.T) {
|
|||||||
|
|
||||||
// Test MonadBiMap
|
// Test MonadBiMap
|
||||||
func TestMonadBiMap(t *testing.T) {
|
func TestMonadBiMap(t *testing.T) {
|
||||||
errToStr := func(e error) string { return e.Error() }
|
errToStr := error.Error
|
||||||
intToStr := func(i int) string { return strconv.Itoa(i) }
|
intToStr := strconv.Itoa
|
||||||
|
|
||||||
result := MonadBiMap(Right[error](42), errToStr, intToStr)
|
result := MonadBiMap(Right[error](42), errToStr, intToStr)
|
||||||
assert.Equal(t, Right[string]("42"), result)
|
assert.Equal(t, Right[string]("42"), result)
|
||||||
@@ -55,7 +55,7 @@ func TestMonadBiMap(t *testing.T) {
|
|||||||
|
|
||||||
// Test MapLeft
|
// Test MapLeft
|
||||||
func TestMapLeft(t *testing.T) {
|
func TestMapLeft(t *testing.T) {
|
||||||
errToStr := func(e error) string { return e.Error() }
|
errToStr := error.Error
|
||||||
|
|
||||||
result := MapLeft[int](errToStr)(Left[int](errors.New("error")))
|
result := MapLeft[int](errToStr)(Left[int](errors.New("error")))
|
||||||
assert.Equal(t, Left[int]("error"), result)
|
assert.Equal(t, Left[int]("error"), result)
|
||||||
@@ -66,7 +66,7 @@ func TestMapLeft(t *testing.T) {
|
|||||||
|
|
||||||
// Test MonadMapLeft
|
// Test MonadMapLeft
|
||||||
func TestMonadMapLeft(t *testing.T) {
|
func TestMonadMapLeft(t *testing.T) {
|
||||||
errToStr := func(e error) string { return e.Error() }
|
errToStr := error.Error
|
||||||
|
|
||||||
result := MonadMapLeft(Left[int](errors.New("error")), errToStr)
|
result := MonadMapLeft(Left[int](errors.New("error")), errToStr)
|
||||||
assert.Equal(t, Left[int]("error"), result)
|
assert.Equal(t, Left[int]("error"), result)
|
||||||
@@ -341,7 +341,7 @@ func TestTraverseRecordWithIndex(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input := map[string]string{"a": "1"}
|
input := map[string]string{"a": "1"}
|
||||||
result := TraverseRecordWithIndex[string](validate)(input)
|
result := TraverseRecordWithIndex(validate)(input)
|
||||||
expected := Right[error](map[string]string{"a": "a:1"})
|
expected := Right[error](map[string]string{"a": "a:1"})
|
||||||
assert.Equal(t, expected, result)
|
assert.Equal(t, expected, result)
|
||||||
}
|
}
|
||||||
@@ -658,7 +658,7 @@ func TestAlternativeMonoid(t *testing.T) {
|
|||||||
// Test AltMonoid
|
// Test AltMonoid
|
||||||
func TestAltMonoid(t *testing.T) {
|
func TestAltMonoid(t *testing.T) {
|
||||||
zero := func() Either[error, int] { return Left[int](errors.New("empty")) }
|
zero := func() Either[error, int] { return Left[int](errors.New("empty")) }
|
||||||
m := AltMonoid[error, int](zero)
|
m := AltMonoid(zero)
|
||||||
|
|
||||||
result := m.Concat(Left[int](errors.New("err1")), Right[error](42))
|
result := m.Concat(Left[int](errors.New("err1")), Right[error](42))
|
||||||
assert.Equal(t, Right[error](42), result)
|
assert.Equal(t, Right[error](42), result)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func TestMapEither(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, F.Pipe1(Right[error]("abc"), Map[error](utils.StringLen)), Right[error](3))
|
assert.Equal(t, F.Pipe1(Right[error]("abc"), Map[error](utils.StringLen)), Right[error](3))
|
||||||
|
|
||||||
val2 := F.Pipe1(Left[string, string]("s"), Map[string](utils.StringLen))
|
val2 := F.Pipe1(Left[string]("s"), Map[string](utils.StringLen))
|
||||||
exp2 := Left[int]("s")
|
exp2 := Left[int]("s")
|
||||||
|
|
||||||
assert.Equal(t, val2, exp2)
|
assert.Equal(t, val2, exp2)
|
||||||
@@ -69,15 +69,15 @@ func TestReduce(t *testing.T) {
|
|||||||
s := S.Semigroup()
|
s := S.Semigroup()
|
||||||
|
|
||||||
assert.Equal(t, "foobar", F.Pipe1(Right[string]("bar"), Reduce[string](s.Concat, "foo")))
|
assert.Equal(t, "foobar", F.Pipe1(Right[string]("bar"), Reduce[string](s.Concat, "foo")))
|
||||||
assert.Equal(t, "foo", F.Pipe1(Left[string, string]("bar"), Reduce[string](s.Concat, "foo")))
|
assert.Equal(t, "foo", F.Pipe1(Left[string]("bar"), Reduce[string](s.Concat, "foo")))
|
||||||
|
|
||||||
}
|
}
|
||||||
func TestAp(t *testing.T) {
|
func TestAp(t *testing.T) {
|
||||||
f := S.Size
|
f := S.Size
|
||||||
|
|
||||||
assert.Equal(t, Right[string](3), F.Pipe1(Right[string](f), Ap[int, string, string](Right[string]("abc"))))
|
assert.Equal(t, Right[string](3), F.Pipe1(Right[string](f), Ap[int](Right[string]("abc"))))
|
||||||
assert.Equal(t, Left[int]("maError"), F.Pipe1(Right[string](f), Ap[int, string, string](Left[string, string]("maError"))))
|
assert.Equal(t, Left[int]("maError"), F.Pipe1(Right[string](f), Ap[int](Left[string]("maError"))))
|
||||||
assert.Equal(t, Left[int]("mabError"), F.Pipe1(Left[func(string) int]("mabError"), Ap[int, string, string](Left[string, 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) {
|
func TestAlt(t *testing.T) {
|
||||||
@@ -91,7 +91,7 @@ func TestChainFirst(t *testing.T) {
|
|||||||
f := F.Flow2(S.Size, Right[string, int])
|
f := F.Flow2(S.Size, Right[string, int])
|
||||||
|
|
||||||
assert.Equal(t, Right[string]("abc"), F.Pipe1(Right[string]("abc"), ChainFirst(f)))
|
assert.Equal(t, Right[string]("abc"), F.Pipe1(Right[string]("abc"), ChainFirst(f)))
|
||||||
assert.Equal(t, Left[string, string]("maError"), F.Pipe1(Left[string, string]("maError"), ChainFirst(f)))
|
assert.Equal(t, Left[string]("maError"), F.Pipe1(Left[string]("maError"), ChainFirst(f)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChainOptionK(t *testing.T) {
|
func TestChainOptionK(t *testing.T) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user