1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-07 23:03:15 +02:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Dr. Carsten Leue
1af6501cd8 fix: add bind variations
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-11 12:42:14 +01:00
Dr. Carsten Leue
600521b220 fix: refactor
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-11 11:01:49 +01:00
Dr. Carsten Leue
62fcd186a3 fix: introcuce readeioresult
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-10 18:44:14 +01:00
Dr. Carsten Leue
2db7e83651 fix: introduce IOResult
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-10 16:26:52 +01:00
Dr. Carsten Leue
8d92df83ad fix: introduce Result type for convenience
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-10 12:08:50 +01:00
Dr. Carsten Leue
0c742b81e6 fix: better performance for either
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-08 10:01:14 +01:00
Dr. Carsten Leue
a1d6c94b15 fix: run benchmarks
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-08 09:31:48 +01:00
Dr. Carsten Leue
e4e28a6556 fix: more Kleisli simplified types
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-07 17:53:47 +01:00
Dr. Carsten Leue
7e7cc06f11 fix: add more Kleisli definitions
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-07 17:42:54 +01:00
Dr. Carsten Leue
54d5dbd04a fix: more tests for iso and prism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-07 17:31:27 +01:00
Dr. Carsten Leue
51adce0c95 fix: better package import
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-07 16:15:16 +01:00
252 changed files with 22883 additions and 4173 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@ fp-go.exe
fp-go
main.exe
build/
.idea
.idea
*.exe

View File

@@ -21,8 +21,8 @@ import (
type (
either struct {
isLeft bool
value any
isLeft bool
}
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
@@ -73,12 +73,12 @@ func IsRight[E, A any](val Either[E, A]) bool {
// Left creates a new instance of an [Either] representing the left value.
func Left[A, E any](value E) Either[E, A] {
return Either[E, A]{true, value}
return Either[E, A]{value, true}
}
// Right creates a new instance of an [Either] representing the right value.
func Right[E, A any](value A) Either[E, A] {
return Either[E, A]{false, value}
return Either[E, A]{value, false}
}
// MonadFold extracts the values from an [Either] by invoking the [onLeft] callback or the [onRight] callback depending on the case
@@ -94,8 +94,7 @@ func Unwrap[E, A any](ma Either[E, A]) (A, E) {
if ma.isLeft {
var a A
return a, ma.value.(E)
} else {
var e E
return ma.value.(A), e
}
var e E
return ma.value.(A), e
}

View File

@@ -269,7 +269,7 @@ import (
func process() IOET.IOEither[error, string] {
return IOEG.Map[error, int, string](
func(x int) string { return fmt.Sprintf("%d", x) },
strconv.Itoa,
)(fetchData())
}
```
@@ -285,7 +285,7 @@ type IOEither[A any] = ioeither.IOEither[error, A]
func process() IOEither[string] {
return ioeither.Map(
func(x int) string { return fmt.Sprintf("%d", x) },
strconv.Itoa,
)(fetchData())
}
```

View File

@@ -82,7 +82,7 @@ func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
//
//go:inline
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.
@@ -278,7 +278,7 @@ func Of[A any](a A) []A {
//
//go:inline
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.
@@ -291,7 +291,7 @@ func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
//
//go:inline
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.
@@ -314,14 +314,14 @@ func Ap[B, A any](fa []A) func([]func(A) B) []B {
//
//go:inline
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.
//
//go:inline
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.
@@ -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.
func Intercalate[A any](m M.Monoid[A]) func(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
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.
@@ -527,7 +527,7 @@ func MonadFlap[B, A any](fab []func(A) B, a A) []B {
//
//go:inline
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.

View File

@@ -97,7 +97,7 @@ func TestAp(t *testing.T) {
utils.Double,
utils.Triple,
},
Ap[int, int]([]int{1, 2, 3}),
Ap[int]([]int{1, 2, 3}),
),
)
}

View File

@@ -34,7 +34,7 @@ import (
func Do[S any](
empty 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.
@@ -58,7 +58,7 @@ func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) []T,
) 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.
@@ -80,7 +80,7 @@ func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) 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.
@@ -102,7 +102,7 @@ func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) 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.
@@ -121,7 +121,7 @@ func LetTo[S1, S2, T any](
func BindTo[S1, T any](
setter 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
@@ -144,5 +144,5 @@ func ApS[S1, S2, T any](
setter func(T) func(S1) S2,
fa []T,
) func([]S1) []S2 {
return G.ApS[[]S1, []S2, []T, S1, S2, T](setter, fa)
return G.ApS[[]S1, []S2](setter, fa)
}

View File

@@ -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 {
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 {
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 {
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 {
return func(as GA) B {
return MonadReduceRightWithIndex[GA](as, f, initial)
return MonadReduceRightWithIndex(as, f, initial)
}
}

View File

@@ -22,19 +22,19 @@ import (
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 {
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 {
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 {
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 {
return Ap[GB, GAB, GA, B, A](fa)
return Ap[GB, GAB](fa)
}
// Monad implements the monadic operations for an array

View File

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

View File

@@ -94,5 +94,5 @@ func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T {
//
//go:inline
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)
}

View File

@@ -18,7 +18,7 @@ import (
//
//go:inline
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

View File

@@ -36,7 +36,7 @@ import (
//
//go:inline
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).
@@ -79,5 +79,5 @@ func Zip[A, B any](fb []B) func([]A) []T.Tuple2[A, B] {
//
//go:inline
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)
}

View File

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

View File

@@ -35,5 +35,6 @@ func Commands() []*C.Command {
IOCommand(),
IOOptionCommand(),
DICommand(),
LensCommand(),
}
}

524
v2/cli/lens.go Normal file
View File

@@ -0,0 +1,524 @@
// 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 cli
import (
"bytes"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"reflect"
"strings"
"text/template"
C "github.com/urfave/cli/v2"
)
const (
keyLensDir = "dir"
keyVerbose = "verbose"
lensAnnotation = "fp-go:Lens"
)
var (
flagLensDir = &C.StringFlag{
Name: keyLensDir,
Value: ".",
Usage: "Directory to scan for Go files",
}
flagVerbose = &C.BoolFlag{
Name: keyVerbose,
Aliases: []string{"v"},
Value: false,
Usage: "Enable verbose output",
}
)
// structInfo holds information about a struct that needs lens generation
type structInfo struct {
Name string
Fields []fieldInfo
Imports map[string]string // package path -> alias
}
// fieldInfo holds information about a struct field
type fieldInfo struct {
Name string
TypeName string
BaseType string // TypeName without leading * for pointer types
IsOptional bool // true if field is a pointer or has json omitempty tag
}
// templateData holds data for template rendering
type templateData struct {
PackageName string
Structs []structInfo
}
const lensStructTemplate = `
// {{.Name}}Lenses provides lenses for accessing fields of {{.Name}}
type {{.Name}}Lenses struct {
{{- range .Fields}}
{{.Name}} {{if .IsOptional}}LO.LensO[{{$.Name}}, {{.TypeName}}]{{else}}L.Lens[{{$.Name}}, {{.TypeName}}]{{end}}
{{- end}}
}
// {{.Name}}RefLenses provides lenses for accessing fields of {{.Name}} via a reference to {{.Name}}
type {{.Name}}RefLenses struct {
{{- range .Fields}}
{{.Name}} {{if .IsOptional}}LO.LensO[*{{$.Name}}, {{.TypeName}}]{{else}}L.Lens[*{{$.Name}}, {{.TypeName}}]{{end}}
{{- end}}
}
`
const lensConstructorTemplate = `
// Make{{.Name}}Lenses creates a new {{.Name}}Lenses with lenses for all fields
func Make{{.Name}}Lenses() {{.Name}}Lenses {
{{- range .Fields}}
{{- if .IsOptional}}
iso{{.Name}} := I.FromZero[{{.TypeName}}]()
{{- end}}
{{- end}}
return {{.Name}}Lenses{
{{- range .Fields}}
{{- if .IsOptional}}
{{.Name}}: L.MakeLens(
func(s {{$.Name}}) O.Option[{{.TypeName}}] { return iso{{.Name}}.Get(s.{{.Name}}) },
func(s {{$.Name}}, v O.Option[{{.TypeName}}]) {{$.Name}} { s.{{.Name}} = iso{{.Name}}.ReverseGet(v); return s },
),
{{- else}}
{{.Name}}: L.MakeLens(
func(s {{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
func(s {{$.Name}}, v {{.TypeName}}) {{$.Name}} { s.{{.Name}} = v; return s },
),
{{- end}}
{{- end}}
}
}
// Make{{.Name}}RefLenses creates a new {{.Name}}RefLenses with lenses for all fields
func Make{{.Name}}RefLenses() {{.Name}}RefLenses {
{{- range .Fields}}
{{- if .IsOptional}}
iso{{.Name}} := I.FromZero[{{.TypeName}}]()
{{- end}}
{{- end}}
return {{.Name}}RefLenses{
{{- range .Fields}}
{{- if .IsOptional}}
{{.Name}}: L.MakeLensRef(
func(s *{{$.Name}}) O.Option[{{.TypeName}}] { return iso{{.Name}}.Get(s.{{.Name}}) },
func(s *{{$.Name}}, v O.Option[{{.TypeName}}]) *{{$.Name}} { s.{{.Name}} = iso{{.Name}}.ReverseGet(v); return s },
),
{{- else}}
{{.Name}}: L.MakeLensRef(
func(s *{{$.Name}}) {{.TypeName}} { return s.{{.Name}} },
func(s *{{$.Name}}, v {{.TypeName}}) *{{$.Name}} { s.{{.Name}} = v; return s },
),
{{- end}}
{{- end}}
}
}
`
var (
structTmpl *template.Template
constructorTmpl *template.Template
)
func init() {
var err error
structTmpl, err = template.New("struct").Parse(lensStructTemplate)
if err != nil {
panic(err)
}
constructorTmpl, err = template.New("constructor").Parse(lensConstructorTemplate)
if err != nil {
panic(err)
}
}
// hasLensAnnotation checks if a comment group contains the lens annotation
func hasLensAnnotation(doc *ast.CommentGroup) bool {
if doc == nil {
return false
}
for _, comment := range doc.List {
if strings.Contains(comment.Text, lensAnnotation) {
return true
}
}
return false
}
// getTypeName extracts the type name from a field type expression
func getTypeName(expr ast.Expr) string {
switch t := expr.(type) {
case *ast.Ident:
return t.Name
case *ast.StarExpr:
return "*" + getTypeName(t.X)
case *ast.ArrayType:
return "[]" + getTypeName(t.Elt)
case *ast.MapType:
return "map[" + getTypeName(t.Key) + "]" + getTypeName(t.Value)
case *ast.SelectorExpr:
return getTypeName(t.X) + "." + t.Sel.Name
case *ast.InterfaceType:
return "interface{}"
case *ast.IndexExpr:
// Generic type with single type parameter (Go 1.18+)
// e.g., Option[string]
return getTypeName(t.X) + "[" + getTypeName(t.Index) + "]"
case *ast.IndexListExpr:
// Generic type with multiple type parameters (Go 1.18+)
// e.g., Map[string, int]
var params []string
for _, index := range t.Indices {
params = append(params, getTypeName(index))
}
return getTypeName(t.X) + "[" + strings.Join(params, ", ") + "]"
default:
return "any"
}
}
// extractImports extracts package imports from a type expression
// Returns a map of package path -> package name
func extractImports(expr ast.Expr, imports map[string]string) {
switch t := expr.(type) {
case *ast.StarExpr:
extractImports(t.X, imports)
case *ast.ArrayType:
extractImports(t.Elt, imports)
case *ast.MapType:
extractImports(t.Key, imports)
extractImports(t.Value, imports)
case *ast.SelectorExpr:
// This is a qualified identifier like "option.Option"
if ident, ok := t.X.(*ast.Ident); ok {
// ident.Name is the package name (e.g., "option")
// We need to track this for import resolution
imports[ident.Name] = ident.Name
}
case *ast.IndexExpr:
// Generic type with single type parameter
extractImports(t.X, imports)
extractImports(t.Index, imports)
case *ast.IndexListExpr:
// Generic type with multiple type parameters
extractImports(t.X, imports)
for _, index := range t.Indices {
extractImports(index, imports)
}
}
}
// hasOmitEmpty checks if a struct tag contains json omitempty
func hasOmitEmpty(tag *ast.BasicLit) bool {
if tag == nil {
return false
}
// Parse the struct tag
tagValue := strings.Trim(tag.Value, "`")
structTag := reflect.StructTag(tagValue)
jsonTag := structTag.Get("json")
// Check if omitempty is present
parts := strings.Split(jsonTag, ",")
for _, part := range parts {
if strings.TrimSpace(part) == "omitempty" {
return true
}
}
return false
}
// isPointerType checks if a type expression is a pointer
func isPointerType(expr ast.Expr) bool {
_, ok := expr.(*ast.StarExpr)
return ok
}
// parseFile parses a Go file and extracts structs with lens annotations
func parseFile(filename string) ([]structInfo, string, error) {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
return nil, "", err
}
var structs []structInfo
packageName := node.Name.Name
// Build import map: package name -> import path
fileImports := make(map[string]string)
for _, imp := range node.Imports {
path := strings.Trim(imp.Path.Value, `"`)
var name string
if imp.Name != nil {
name = imp.Name.Name
} else {
// Extract package name from path (last component)
parts := strings.Split(path, "/")
name = parts[len(parts)-1]
}
fileImports[name] = path
}
// First pass: collect all GenDecls with their doc comments
declMap := make(map[*ast.TypeSpec]*ast.CommentGroup)
ast.Inspect(node, func(n ast.Node) bool {
if gd, ok := n.(*ast.GenDecl); ok {
for _, spec := range gd.Specs {
if ts, ok := spec.(*ast.TypeSpec); ok {
declMap[ts] = gd.Doc
}
}
}
return true
})
// Second pass: process type specs
ast.Inspect(node, func(n ast.Node) bool {
// Look for type declarations
typeSpec, ok := n.(*ast.TypeSpec)
if !ok {
return true
}
// Check if it's a struct type
structType, ok := typeSpec.Type.(*ast.StructType)
if !ok {
return true
}
// Get the doc comment from our map
doc := declMap[typeSpec]
if !hasLensAnnotation(doc) {
return true
}
// Extract field information and collect imports
var fields []fieldInfo
structImports := make(map[string]string)
for _, field := range structType.Fields.List {
if len(field.Names) == 0 {
// Embedded field, skip for now
continue
}
for _, name := range field.Names {
// Only export lenses for exported fields
if name.IsExported() {
typeName := getTypeName(field.Type)
isOptional := false
baseType := typeName
// Check if field is optional:
// 1. Pointer types are always optional
// 2. Non-pointer types with json omitempty tag are optional
if isPointerType(field.Type) {
isOptional = true
// Strip leading * for base type
baseType = strings.TrimPrefix(typeName, "*")
} else if hasOmitEmpty(field.Tag) {
// Non-pointer type with omitempty is also optional
isOptional = true
}
// Extract imports from this field's type
fieldImports := make(map[string]string)
extractImports(field.Type, fieldImports)
// Resolve package names to full import paths
for pkgName := range fieldImports {
if importPath, ok := fileImports[pkgName]; ok {
structImports[importPath] = pkgName
}
}
fields = append(fields, fieldInfo{
Name: name.Name,
TypeName: typeName,
BaseType: baseType,
IsOptional: isOptional,
})
}
}
}
if len(fields) > 0 {
structs = append(structs, structInfo{
Name: typeSpec.Name.Name,
Fields: fields,
Imports: structImports,
})
}
return true
})
return structs, packageName, nil
}
// generateLensHelpers scans a directory for Go files and generates lens code
func generateLensHelpers(dir, filename string, verbose bool) error {
// Get absolute path
absDir, err := filepath.Abs(dir)
if err != nil {
return err
}
if verbose {
log.Printf("Scanning directory: %s", absDir)
}
// Find all Go files in the directory
files, err := filepath.Glob(filepath.Join(absDir, "*.go"))
if err != nil {
return err
}
if verbose {
log.Printf("Found %d Go files", len(files))
}
// Parse all files and collect structs
var allStructs []structInfo
var packageName string
for _, file := range files {
// Skip generated files and test files
if strings.HasSuffix(file, "_test.go") || strings.Contains(file, "gen.go") {
if verbose {
log.Printf("Skipping file: %s", filepath.Base(file))
}
continue
}
if verbose {
log.Printf("Parsing file: %s", filepath.Base(file))
}
structs, pkg, err := parseFile(file)
if err != nil {
log.Printf("Warning: failed to parse %s: %v", file, err)
continue
}
if verbose && len(structs) > 0 {
log.Printf("Found %d annotated struct(s) in %s", len(structs), filepath.Base(file))
for _, s := range structs {
log.Printf(" - %s (%d fields)", s.Name, len(s.Fields))
}
}
if packageName == "" {
packageName = pkg
}
allStructs = append(allStructs, structs...)
}
if len(allStructs) == 0 {
log.Printf("No structs with %s annotation found in %s", lensAnnotation, absDir)
return nil
}
// Collect all unique imports from all structs
allImports := make(map[string]string) // import path -> alias
for _, s := range allStructs {
for importPath, alias := range s.Imports {
allImports[importPath] = alias
}
}
// Create output file
outPath := filepath.Join(absDir, filename)
f, err := os.Create(filepath.Clean(outPath))
if err != nil {
return err
}
defer f.Close()
log.Printf("Generating lens code in [%s] for package [%s] with [%d] structs ...", outPath, packageName, len(allStructs))
// Write header
writePackage(f, packageName)
// Write imports
f.WriteString("import (\n")
// Standard fp-go imports always needed
f.WriteString("\tL \"github.com/IBM/fp-go/v2/optics/lens\"\n")
f.WriteString("\tLO \"github.com/IBM/fp-go/v2/optics/lens/option\"\n")
f.WriteString("\tO \"github.com/IBM/fp-go/v2/option\"\n")
f.WriteString("\tI \"github.com/IBM/fp-go/v2/optics/iso/option\"\n")
// Add additional imports collected from field types
for importPath, alias := range allImports {
f.WriteString("\t" + alias + " \"" + importPath + "\"\n")
}
f.WriteString(")\n")
// Generate lens code for each struct using templates
for _, s := range allStructs {
var buf bytes.Buffer
// Generate struct type
if err := structTmpl.Execute(&buf, s); err != nil {
return err
}
// Generate constructor
if err := constructorTmpl.Execute(&buf, s); err != nil {
return err
}
// Write to file
if _, err := f.Write(buf.Bytes()); err != nil {
return err
}
}
return nil
}
// LensCommand creates the CLI command for lens generation
func LensCommand() *C.Command {
return &C.Command{
Name: "lens",
Usage: "generate lens code for annotated structs",
Description: "Scans Go files for structs annotated with 'fp-go:Lens' and generates lens types. Pointer types and non-pointer types with json omitempty tag generate LensO (optional lens).",
Flags: []C.Flag{
flagLensDir,
flagFilename,
flagVerbose,
},
Action: func(ctx *C.Context) error {
return generateLensHelpers(
ctx.String(keyLensDir),
ctx.String(keyFilename),
ctx.Bool(keyVerbose),
)
},
}
}

503
v2/cli/lens_test.go Normal file
View File

@@ -0,0 +1,503 @@
// 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 cli
import (
"bytes"
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHasLensAnnotation(t *testing.T) {
tests := []struct {
name string
comment string
expected bool
}{
{
name: "has annotation",
comment: "// fp-go:Lens",
expected: true,
},
{
name: "has annotation with other text",
comment: "// This is a struct with fp-go:Lens annotation",
expected: true,
},
{
name: "no annotation",
comment: "// This is just a regular comment",
expected: false,
},
{
name: "nil comment",
comment: "",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var doc *ast.CommentGroup
if tt.comment != "" {
doc = &ast.CommentGroup{
List: []*ast.Comment{
{Text: tt.comment},
},
}
}
result := hasLensAnnotation(doc)
assert.Equal(t, tt.expected, result)
})
}
}
func TestGetTypeName(t *testing.T) {
tests := []struct {
name string
code string
expected string
}{
{
name: "simple type",
code: "type T struct { F string }",
expected: "string",
},
{
name: "pointer type",
code: "type T struct { F *string }",
expected: "*string",
},
{
name: "slice type",
code: "type T struct { F []int }",
expected: "[]int",
},
{
name: "map type",
code: "type T struct { F map[string]int }",
expected: "map[string]int",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "", "package test\n"+tt.code, 0)
require.NoError(t, err)
var fieldType ast.Expr
ast.Inspect(file, func(n ast.Node) bool {
if field, ok := n.(*ast.Field); ok && len(field.Names) > 0 {
fieldType = field.Type
return false
}
return true
})
require.NotNil(t, fieldType)
result := getTypeName(fieldType)
assert.Equal(t, tt.expected, result)
})
}
}
func TestIsPointerType(t *testing.T) {
tests := []struct {
name string
code string
expected bool
}{
{
name: "pointer type",
code: "type T struct { F *string }",
expected: true,
},
{
name: "non-pointer type",
code: "type T struct { F string }",
expected: false,
},
{
name: "slice type",
code: "type T struct { F []string }",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "", "package test\n"+tt.code, 0)
require.NoError(t, err)
var fieldType ast.Expr
ast.Inspect(file, func(n ast.Node) bool {
if field, ok := n.(*ast.Field); ok && len(field.Names) > 0 {
fieldType = field.Type
return false
}
return true
})
require.NotNil(t, fieldType)
result := isPointerType(fieldType)
assert.Equal(t, tt.expected, result)
})
}
}
func TestHasOmitEmpty(t *testing.T) {
tests := []struct {
name string
tag string
expected bool
}{
{
name: "has omitempty",
tag: "`json:\"field,omitempty\"`",
expected: true,
},
{
name: "has omitempty with other options",
tag: "`json:\"field,omitempty,string\"`",
expected: true,
},
{
name: "no omitempty",
tag: "`json:\"field\"`",
expected: false,
},
{
name: "no tag",
tag: "",
expected: false,
},
{
name: "different tag",
tag: "`xml:\"field\"`",
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var tag *ast.BasicLit
if tt.tag != "" {
tag = &ast.BasicLit{
Value: tt.tag,
}
}
result := hasOmitEmpty(tag)
assert.Equal(t, tt.expected, result)
})
}
}
func TestParseFile(t *testing.T) {
// Create a temporary test file
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.go")
testCode := `package testpkg
// fp-go:Lens
type Person struct {
Name string
Age int
Phone *string
}
// fp-go:Lens
type Address struct {
Street string
City string
}
// Not annotated
type Other struct {
Field string
}
`
err := os.WriteFile(testFile, []byte(testCode), 0644)
require.NoError(t, err)
// Parse the file
structs, pkg, err := parseFile(testFile)
require.NoError(t, err)
// Verify results
assert.Equal(t, "testpkg", pkg)
assert.Len(t, structs, 2)
// Check Person struct
person := structs[0]
assert.Equal(t, "Person", person.Name)
assert.Len(t, person.Fields, 3)
assert.Equal(t, "Name", person.Fields[0].Name)
assert.Equal(t, "string", person.Fields[0].TypeName)
assert.False(t, person.Fields[0].IsOptional)
assert.Equal(t, "Age", person.Fields[1].Name)
assert.Equal(t, "int", person.Fields[1].TypeName)
assert.False(t, person.Fields[1].IsOptional)
assert.Equal(t, "Phone", person.Fields[2].Name)
assert.Equal(t, "*string", person.Fields[2].TypeName)
assert.True(t, person.Fields[2].IsOptional)
// Check Address struct
address := structs[1]
assert.Equal(t, "Address", address.Name)
assert.Len(t, address.Fields, 2)
assert.Equal(t, "Street", address.Fields[0].Name)
assert.Equal(t, "City", address.Fields[1].Name)
}
func TestParseFileWithOmitEmpty(t *testing.T) {
// Create a temporary test file
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "test.go")
testCode := `package testpkg
// fp-go:Lens
type Config struct {
Name string
Value string ` + "`json:\"value,omitempty\"`" + `
Count int ` + "`json:\",omitempty\"`" + `
Optional *string ` + "`json:\"optional,omitempty\"`" + `
Required int ` + "`json:\"required\"`" + `
}
`
err := os.WriteFile(testFile, []byte(testCode), 0644)
require.NoError(t, err)
// Parse the file
structs, pkg, err := parseFile(testFile)
require.NoError(t, err)
// Verify results
assert.Equal(t, "testpkg", pkg)
assert.Len(t, structs, 1)
// Check Config struct
config := structs[0]
assert.Equal(t, "Config", config.Name)
assert.Len(t, config.Fields, 5)
// Name - no tag, not optional
assert.Equal(t, "Name", config.Fields[0].Name)
assert.Equal(t, "string", config.Fields[0].TypeName)
assert.False(t, config.Fields[0].IsOptional)
// Value - has omitempty, should be optional
assert.Equal(t, "Value", config.Fields[1].Name)
assert.Equal(t, "string", config.Fields[1].TypeName)
assert.True(t, config.Fields[1].IsOptional, "Value field with omitempty should be optional")
// Count - has omitempty (no field name in tag), should be optional
assert.Equal(t, "Count", config.Fields[2].Name)
assert.Equal(t, "int", config.Fields[2].TypeName)
assert.True(t, config.Fields[2].IsOptional, "Count field with omitempty should be optional")
// Optional - pointer with omitempty, should be optional
assert.Equal(t, "Optional", config.Fields[3].Name)
assert.Equal(t, "*string", config.Fields[3].TypeName)
assert.True(t, config.Fields[3].IsOptional)
// Required - has json tag but no omitempty, not optional
assert.Equal(t, "Required", config.Fields[4].Name)
assert.Equal(t, "int", config.Fields[4].TypeName)
assert.False(t, config.Fields[4].IsOptional, "Required field without omitempty should not be optional")
}
func TestGenerateLensHelpers(t *testing.T) {
// Create a temporary directory with test files
tmpDir := t.TempDir()
testCode := `package testpkg
// fp-go:Lens
type TestStruct struct {
Name string
Value *int
}
`
testFile := filepath.Join(tmpDir, "test.go")
err := os.WriteFile(testFile, []byte(testCode), 0644)
require.NoError(t, err)
// Generate lens code
outputFile := "gen.go"
err = generateLensHelpers(tmpDir, outputFile, false)
require.NoError(t, err)
// Verify the generated file exists
genPath := filepath.Join(tmpDir, outputFile)
_, err = os.Stat(genPath)
require.NoError(t, err)
// Read and verify the generated content
content, err := os.ReadFile(genPath)
require.NoError(t, err)
contentStr := string(content)
// Check for expected content
assert.Contains(t, contentStr, "package testpkg")
assert.Contains(t, contentStr, "Code generated by go generate")
assert.Contains(t, contentStr, "TestStructLens")
assert.Contains(t, contentStr, "MakeTestStructLens")
assert.Contains(t, contentStr, "L.Lens[TestStruct, string]")
assert.Contains(t, contentStr, "LO.LensO[TestStruct, *int]")
assert.Contains(t, contentStr, "I.FromZero")
}
func TestGenerateLensHelpersNoAnnotations(t *testing.T) {
// Create a temporary directory with test files
tmpDir := t.TempDir()
testCode := `package testpkg
// No annotation
type TestStruct struct {
Name string
}
`
testFile := filepath.Join(tmpDir, "test.go")
err := os.WriteFile(testFile, []byte(testCode), 0644)
require.NoError(t, err)
// Generate lens code (should not create file)
outputFile := "gen.go"
err = generateLensHelpers(tmpDir, outputFile, false)
require.NoError(t, err)
// Verify the generated file does not exist
genPath := filepath.Join(tmpDir, outputFile)
_, err = os.Stat(genPath)
assert.True(t, os.IsNotExist(err))
}
func TestLensTemplates(t *testing.T) {
s := structInfo{
Name: "TestStruct",
Fields: []fieldInfo{
{Name: "Name", TypeName: "string", IsOptional: false},
{Name: "Value", TypeName: "*int", IsOptional: true},
},
}
// Test struct template
var structBuf bytes.Buffer
err := structTmpl.Execute(&structBuf, s)
require.NoError(t, err)
structStr := structBuf.String()
assert.Contains(t, structStr, "type TestStructLenses struct")
assert.Contains(t, structStr, "Name L.Lens[TestStruct, string]")
assert.Contains(t, structStr, "Value LO.LensO[TestStruct, *int]")
// Test constructor template
var constructorBuf bytes.Buffer
err = constructorTmpl.Execute(&constructorBuf, s)
require.NoError(t, err)
constructorStr := constructorBuf.String()
assert.Contains(t, constructorStr, "func MakeTestStructLenses() TestStructLenses")
assert.Contains(t, constructorStr, "return TestStructLenses{")
assert.Contains(t, constructorStr, "Name: L.MakeLens(")
assert.Contains(t, constructorStr, "Value: L.MakeLens(")
assert.Contains(t, constructorStr, "I.FromZero")
}
func TestLensTemplatesWithOmitEmpty(t *testing.T) {
s := structInfo{
Name: "ConfigStruct",
Fields: []fieldInfo{
{Name: "Name", TypeName: "string", IsOptional: false},
{Name: "Value", TypeName: "string", IsOptional: true}, // non-pointer with omitempty
{Name: "Count", TypeName: "int", IsOptional: true}, // non-pointer with omitempty
{Name: "Pointer", TypeName: "*string", IsOptional: true}, // pointer
},
}
// Test struct template
var structBuf bytes.Buffer
err := structTmpl.Execute(&structBuf, s)
require.NoError(t, err)
structStr := structBuf.String()
assert.Contains(t, structStr, "type ConfigStructLenses struct")
assert.Contains(t, structStr, "Name L.Lens[ConfigStruct, string]")
assert.Contains(t, structStr, "Value LO.LensO[ConfigStruct, string]", "non-pointer with omitempty should use LensO")
assert.Contains(t, structStr, "Count LO.LensO[ConfigStruct, int]", "non-pointer with omitempty should use LensO")
assert.Contains(t, structStr, "Pointer LO.LensO[ConfigStruct, *string]")
// Test constructor template
var constructorBuf bytes.Buffer
err = constructorTmpl.Execute(&constructorBuf, s)
require.NoError(t, err)
constructorStr := constructorBuf.String()
assert.Contains(t, constructorStr, "func MakeConfigStructLenses() ConfigStructLenses")
assert.Contains(t, constructorStr, "isoValue := I.FromZero[string]()")
assert.Contains(t, constructorStr, "isoCount := I.FromZero[int]()")
assert.Contains(t, constructorStr, "isoPointer := I.FromZero[*string]()")
}
func TestLensCommandFlags(t *testing.T) {
cmd := LensCommand()
assert.Equal(t, "lens", cmd.Name)
assert.Equal(t, "generate lens code for annotated structs", cmd.Usage)
assert.Contains(t, strings.ToLower(cmd.Description), "fp-go:lens")
assert.Contains(t, strings.ToLower(cmd.Description), "lenso")
// Check flags
assert.Len(t, cmd.Flags, 3)
var hasDir, hasFilename, hasVerbose bool
for _, flag := range cmd.Flags {
switch flag.Names()[0] {
case "dir":
hasDir = true
case "filename":
hasFilename = true
case "verbose":
hasVerbose = true
}
}
assert.True(t, hasDir, "should have dir flag")
assert.True(t, hasFilename, "should have filename flag")
assert.True(t, hasVerbose, "should have verbose flag")
}

View File

@@ -27,7 +27,7 @@ import (
func TestMap(t *testing.T) {
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) {

View File

@@ -13,20 +13,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package ioeither
package ioresult
import (
"context"
"github.com/IBM/fp-go/v2/either"
IOE "github.com/IBM/fp-go/v2/ioeither"
"github.com/IBM/fp-go/v2/result"
)
// 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] {
return func() either.Either[error, A] {
func WithContext[A any](ctx context.Context, ma IOResult[A]) IOResult[A] {
return func() Result[A] {
if err := context.Cause(ctx); err != nil {
return either.Left[A](err)
return result.Left[A](err)
}
return ma()
}

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

View File

@@ -1,341 +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 (
"github.com/IBM/fp-go/v2/internal/apply"
"github.com/IBM/fp-go/v2/internal/chain"
"github.com/IBM/fp-go/v2/internal/functor"
L "github.com/IBM/fp-go/v2/optics/lens"
)
// Do creates an empty context of type [S] to be used with the [Bind] operation.
// This is the starting point for do-notation style composition.
//
// Example:
//
// type State struct {
// User User
// Config Config
// }
// result := readerioeither.Do(State{})
//
//go:inline
func Do[S any](
empty S,
) ReaderIOEither[S] {
return Of(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.ReaderIOEither[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.ReaderIOEither[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 chain.Bind(
Chain[S1, S2],
Map[T, S2],
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 functor.Let(
Map[S1, S2],
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 functor.LetTo(
Map[S1, S2],
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 chain.BindTo(
Map[T, S1],
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 ReaderIOEither[T],
) Operator[S1, S2] {
return apply.ApS(
Ap[S2, T],
Map[S1, func(T) S2],
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 ReaderIOEither[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 ReaderIOEither 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.ReaderIOEither[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 Bind[S, S, T](lens.Set, func(s S) ReaderIOEither[T] {
return f(lens.Get(s))
})
}
// 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 ReaderIOEither).
//
// 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 Let[S, S, T](lens.Set, func(s S) T {
return f(lens.Get(s))
})
}
// 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 LetTo[S, S, T](lens.Set, b)
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,374 @@
# ReaderIOResult Benchmarks
This document describes the benchmark suite for the `context/readerioeither` package and how to interpret the results to identify performance bottlenecks.
## Running Benchmarks
To run all benchmarks:
```bash
cd context/readerioeither
go test -bench=. -benchmem
```
To run specific benchmarks:
```bash
go test -bench=BenchmarkMap -benchmem
go test -bench=BenchmarkChain -benchmem
go test -bench=BenchmarkApPar -benchmem
```
To run with more iterations for stable results:
```bash
go test -bench=. -benchmem -benchtime=100000x
```
## Benchmark Categories
### 1. Core Constructors
- `BenchmarkLeft` - Creating Left (error) values (~64ns, 2 allocs)
- `BenchmarkRight` - Creating Right (success) values (~64ns, 2 allocs)
- `BenchmarkOf` - Creating Right values via Of (~47ns, 2 allocs)
**Key Insights:**
- All constructors allocate 2 times (64B total)
- `Of` is slightly faster than `Right` due to inlining
- Construction is very fast, suitable for hot paths
### 2. Conversion Operations
- `BenchmarkFromEither_Right/Left` - Converting Either to ReaderIOResult (~70ns, 2 allocs)
- `BenchmarkFromIO` - Converting IO to ReaderIOResult (~78ns, 3 allocs)
- `BenchmarkFromIOEither_Right/Left` - Converting IOEither (~23ns, 1 alloc)
**Key Insights:**
- FromIOEither is the fastest conversion (~23ns)
- FromIO has an extra allocation due to wrapping
- All conversions are lightweight
### 3. Execution Operations
- `BenchmarkExecute_Right` - Executing Right computation (~37ns, 1 alloc)
- `BenchmarkExecute_Left` - Executing Left computation (~48ns, 1 alloc)
- `BenchmarkExecute_WithContext` - Executing with context (~42ns, 1 alloc)
**Key Insights:**
- Execution is very fast with minimal allocations
- Left path is slightly slower due to error handling
- Context overhead is minimal (~5ns)
### 4. Functor Operations (Map)
- `BenchmarkMonadMap_Right/Left` - Direct map (~135ns, 5 allocs)
- `BenchmarkMap_Right/Left` - Curried map (~24ns, 1 alloc)
- `BenchmarkMapTo_Right` - Replacing with constant (~69ns, 1 alloc)
**Key Insights:**
- **Bottleneck:** MonadMap has 5 allocations (128B)
- Curried Map is ~5x faster with fewer allocations
- **Recommendation:** Use curried `Map` instead of `MonadMap`
### 5. Monad Operations (Chain)
- `BenchmarkMonadChain_Right/Left` - Direct chain (~190ns, 6 allocs)
- `BenchmarkChain_Right/Left` - Curried chain (~28ns, 1 alloc)
- `BenchmarkChainFirst_Right/Left` - Chain preserving original (~27ns, 1 alloc)
- `BenchmarkFlatten_Right/Left` - Removing nesting (~147ns, 7 allocs)
**Key Insights:**
- **Bottleneck:** MonadChain has 6 allocations (160B)
- Curried Chain is ~7x faster
- ChainFirst is as fast as Chain
- **Bottleneck:** Flatten has 7 allocations
- **Recommendation:** Use curried `Chain` instead of `MonadChain`
### 6. Applicative Operations (Ap)
- `BenchmarkMonadApSeq_RightRight` - Sequential apply (~281ns, 8 allocs)
- `BenchmarkMonadApPar_RightRight` - Parallel apply (~49ns, 3 allocs)
- `BenchmarkExecuteApSeq_RightRight` - Executing sequential (~1403ns, 8 allocs)
- `BenchmarkExecuteApPar_RightRight` - Executing parallel (~5606ns, 61 allocs)
**Key Insights:**
- **Major Bottleneck:** Parallel execution has 61 allocations (1896B)
- Construction of ApPar is fast (~49ns), but execution is expensive
- Sequential execution is faster for simple operations (~1.4μs vs ~5.6μs)
- **Recommendation:** Use ApSeq for simple operations, ApPar only for truly independent, expensive computations
- Parallel overhead includes context management and goroutine coordination
### 7. Alternative Operations
- `BenchmarkAlt_RightRight/LeftRight` - Providing alternatives (~210-344ns, 6 allocs)
- `BenchmarkOrElse_Right/Left` - Recovery from Left (~40-52ns, 2 allocs)
**Key Insights:**
- Alt has significant overhead (6 allocations)
- OrElse is much more efficient for error recovery
- **Recommendation:** Prefer OrElse over Alt when possible
### 8. Chain Operations with Different Types
- `BenchmarkChainEitherK_Right/Left` - Chaining Either (~25ns, 1 alloc)
- `BenchmarkChainIOK_Right/Left` - Chaining IO (~55ns, 1 alloc)
- `BenchmarkChainIOEitherK_Right/Left` - Chaining IOEither (~53ns, 1 alloc)
**Key Insights:**
- All chain-K operations are efficient
- ChainEitherK is fastest (pure transformation)
- ChainIOK and ChainIOEitherK have similar performance
### 9. Context Operations
- `BenchmarkAsk` - Accessing context (~52ns, 3 allocs)
- `BenchmarkDefer` - Lazy generation (~34ns, 1 alloc)
- `BenchmarkMemoize` - Caching results (~82ns, 4 allocs)
**Key Insights:**
- Ask has 3 allocations for context wrapping
- Defer is lightweight
- Memoize has overhead but pays off for expensive computations
### 10. Delay Operations
- `BenchmarkDelay_Construction` - Creating delayed computation (~19ns, 1 alloc)
- `BenchmarkTimer_Construction` - Creating timer (~92ns, 3 allocs)
**Key Insights:**
- Delay construction is very cheap
- Timer has additional overhead for time operations
### 11. TryCatch Operations
- `BenchmarkTryCatch_Success/Error` - Creating TryCatch (~33ns, 1 alloc)
- `BenchmarkExecuteTryCatch_Success/Error` - Executing TryCatch (~3ns, 0 allocs)
**Key Insights:**
- TryCatch construction is cheap
- Execution is extremely fast with zero allocations
- Excellent for wrapping Go error-returning functions
### 12. Pipeline Operations
- `BenchmarkPipeline_Map_Right/Left` - Single Map in pipeline (~200-306ns, 9 allocs)
- `BenchmarkPipeline_Chain_Right/Left` - Single Chain in pipeline (~155-217ns, 7 allocs)
- `BenchmarkPipeline_Complex_Right/Left` - Multiple operations (~777-1039ns, 25 allocs)
- `BenchmarkExecutePipeline_Complex_Right` - Executing complex pipeline (~533ns, 10 allocs)
**Key Insights:**
- **Major Bottleneck:** Pipeline operations allocate heavily
- Single Map: ~200ns with 9 allocations (224B)
- Complex pipeline: ~900ns with 25 allocations (640B)
- **Recommendation:** Avoid F.Pipe in hot paths, use direct function calls
### 13. Do-Notation Operations
- `BenchmarkDo` - Creating empty context (~45ns, 2 allocs)
- `BenchmarkBind_Right` - Binding values (~25ns, 1 alloc)
- `BenchmarkLet_Right` - Pure computations (~23ns, 1 alloc)
- `BenchmarkApS_Right` - Applicative binding (~99ns, 4 allocs)
**Key Insights:**
- Do-notation operations are efficient
- Bind and Let are very fast
- ApS has more overhead (4 allocations)
- Much better than either package's Bind (~130ns vs ~25ns)
### 14. Traverse Operations
- `BenchmarkTraverseArray_Empty` - Empty array (~689ns, 13 allocs)
- `BenchmarkTraverseArray_Small` - 3 elements (~1971ns, 37 allocs)
- `BenchmarkTraverseArray_Medium` - 10 elements (~4386ns, 93 allocs)
- `BenchmarkTraverseArraySeq_Small` - Sequential (~1885ns, 52 allocs)
- `BenchmarkTraverseArrayPar_Small` - Parallel (~1362ns, 37 allocs)
- `BenchmarkExecuteTraverseArraySeq_Small` - Executing sequential (~1080ns, 34 allocs)
- `BenchmarkExecuteTraverseArrayPar_Small` - Executing parallel (~18560ns, 202 allocs)
**Key Insights:**
- **Bottleneck:** Traverse operations allocate per element
- Empty array still has 13 allocations (overhead)
- Parallel construction is faster but execution is much slower
- **Major Bottleneck:** Parallel execution: ~18.5μs with 202 allocations
- Sequential execution is ~17x faster for small arrays
- **Recommendation:** Use sequential traverse for small collections, parallel only for large, expensive operations
### 15. Record Operations
- `BenchmarkTraverseRecord_Small` - 3 entries (~1444ns, 55 allocs)
- `BenchmarkSequenceRecord_Small` - 3 entries (~1073ns, 54 allocs)
**Key Insights:**
- Record operations have high allocation overhead
- Similar performance to array traversal
- Allocations scale with map size
### 16. Resource Management
- `BenchmarkWithResource_Success` - Creating resource wrapper (~193ns, 8 allocs)
- `BenchmarkExecuteWithResource_Success` - Executing with resource (varies)
- `BenchmarkExecuteWithResource_ErrorInBody` - Error handling (varies)
**Key Insights:**
- Resource management has 8 allocations for safety
- Ensures proper cleanup even on errors
- Overhead is acceptable for resource safety guarantees
### 17. Context Cancellation
- `BenchmarkExecute_CanceledContext` - Executing with canceled context
- `BenchmarkExecuteApPar_CanceledContext` - Parallel with canceled context
**Key Insights:**
- Cancellation is handled efficiently
- Minimal overhead for checking cancellation
- ApPar respects cancellation properly
## Performance Bottlenecks Summary
### Critical Bottlenecks (>100ns or >5 allocations)
1. **Pipeline operations with F.Pipe** (~200-1000ns, 9-25 allocations)
- **Impact:** High - commonly used pattern
- **Mitigation:** Use direct function calls in hot paths
- **Example:**
```go
// Slow (200ns, 9 allocs)
result := F.Pipe1(rioe, Map[int](transform))
// Fast (24ns, 1 alloc)
result := Map[int](transform)(rioe)
```
2. **MonadMap and MonadChain** (~135-207ns, 5-6 allocations)
- **Impact:** High - fundamental operations
- **Mitigation:** Use curried versions (Map, Chain)
- **Speedup:** 5-7x faster
3. **Parallel applicative execution** (~5.6μs, 61 allocations)
- **Impact:** High when used
- **Mitigation:** Use ApSeq for simple operations
- **Note:** Only use ApPar for truly independent, expensive computations
4. **Parallel traverse execution** (~18.5μs, 202 allocations)
- **Impact:** High for collections
- **Mitigation:** Use sequential traverse for small collections
- **Threshold:** Consider parallel only for >100 elements with expensive operations
5. **Alt operations** (~210-344ns, 6 allocations)
- **Impact:** Medium
- **Mitigation:** Use OrElse for error recovery (40-52ns, 2 allocs)
### Minor Bottlenecks (50-100ns or 3-4 allocations)
6. **Flatten operations** (~147ns, 7 allocations)
- **Impact:** Low - less commonly used
- **Mitigation:** Avoid unnecessary nesting
7. **Memoize** (~82ns, 4 allocations)
- **Impact:** Low - overhead pays off for expensive computations
- **Mitigation:** Only use for computations >1μs
8. **ApS in do-notation** (~99ns, 4 allocations)
- **Impact:** Low
- **Mitigation:** Use Let or Bind when possible
## Optimization Recommendations
### For Hot Paths
1. **Use curried functions over Monad* versions:**
```go
// Instead of:
result := MonadMap(rioe, transform) // 135ns, 5 allocs
// Use:
result := Map[int](transform)(rioe) // 24ns, 1 alloc
```
2. **Avoid F.Pipe in performance-critical code:**
```go
// Instead of:
result := F.Pipe3(rioe, Map(f1), Chain(f2), Map(f3)) // 1000ns, 25 allocs
// Use:
result := Map(f3)(Chain(f2)(Map(f1)(rioe))) // Much faster
```
3. **Use sequential operations for small collections:**
```go
// For arrays with <10 elements:
result := TraverseArraySeq(f)(arr) // 1.9μs, 52 allocs
// Instead of:
result := TraverseArrayPar(f)(arr) // 18.5μs, 202 allocs (when executed)
```
4. **Prefer OrElse over Alt for error recovery:**
```go
// Instead of:
result := Alt(alternative)(rioe) // 210-344ns, 6 allocs
// Use:
result := OrElse(recover)(rioe) // 40-52ns, 2 allocs
```
### For Context Operations
- Context operations are generally efficient
- Ask has 3 allocations but is necessary for context access
- Cancellation checking is fast and should be used liberally
### For Resource Management
- WithResource overhead (8 allocations) is acceptable for safety
- Always use for resources that need cleanup
- The RAII pattern prevents resource leaks
### Memory Considerations
- Most operations have 1-2 allocations
- Monad* versions have 5-8 allocations
- Pipeline operations allocate heavily
- Parallel operations have significant allocation overhead
- Traverse operations allocate per element
## Comparative Analysis
### Fastest Operations (<50ns, <2 allocations)
- Constructors (Left, Right, Of)
- Execution (Execute_Right/Left)
- Curried operations (Map, Chain, ChainFirst)
- TryCatch execution
- Chain-K operations
- Let and Bind in do-notation
### Medium Speed (50-200ns, 2-8 allocations)
- Conversion operations
- MonadMap, MonadChain
- Flatten
- Memoize
- Resource management construction
- Traverse construction
### Slower Operations (>200ns or >8 allocations)
- Pipeline operations
- Alt operations
- Traverse operations (especially parallel)
- Applicative operations (especially parallel execution)
## Parallel vs Sequential Trade-offs
### When to Use Sequential (ApSeq, TraverseArraySeq)
- Small collections (<10 elements)
- Fast operations (<1μs per element)
- When allocations matter
- Default choice for most use cases
### When to Use Parallel (ApPar, TraverseArrayPar)
- Large collections (>100 elements)
- Expensive operations (>10μs per element)
- Independent computations
- When latency matters more than throughput
- I/O-bound operations
### Parallel Overhead
- Construction: ~50ns, 3 allocs
- Execution: +4-17μs, +40-160 allocs
- Context management and goroutine coordination
- Only worthwhile for truly expensive operations
## Conclusion
The `context/readerioeither` package is well-optimized with most operations completing in nanoseconds with minimal allocations. Key recommendations:
1. Use curried functions (Map, Chain) instead of Monad* versions (5-7x faster)
2. Avoid F.Pipe in hot paths (5-10x slower)
3. Use sequential operations by default; parallel only for expensive, independent computations
4. Prefer OrElse over Alt for error recovery (5x faster)
5. TryCatch is excellent for wrapping Go functions (near-zero execution cost)
6. Context operations are efficient; use liberally
7. Resource management overhead is acceptable for safety guarantees
For typical use cases, the performance is excellent. Only in extremely hot paths (millions of operations per second) should you consider the micro-optimizations suggested above.

View File

@@ -0,0 +1,672 @@
// 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[context.Context](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[context.Context](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])
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])
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])
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])
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])
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])
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])
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])
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])
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])
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])
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])
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
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
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
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
func ApReaderS[S1, S2, T any](
setter func(T) func(S1) S2,
fa Reader[context.Context, T],
) Operator[S1, S2] {
return ApS(setter, FromReader[T](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
func ApReaderIOS[S1, S2, T any](
setter func(T) func(S1) S2,
fa ReaderIO[T],
) Operator[S1, S2] {
return ApS(setter, FromReaderIO[T](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
func ApEitherS[S1, S2, T any](
setter func(T) func(S1) S2,
fa Result[T],
) Operator[S1, S2] {
return ApS(setter, FromEither[T](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
func ApResultS[S1, S2, T any](
setter func(T) func(S1) S2,
fa Result[T],
) Operator[S1, S2] {
return ApS(setter, FromResult[T](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
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
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
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
func ApReaderSL[S, T any](
lens L.Lens[S, T],
fa Reader[context.Context, T],
) Operator[S, S] {
return ApSL(lens, FromReader[T](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
func ApReaderIOSL[S, T any](
lens L.Lens[S, T],
fa ReaderIO[T],
) Operator[S, S] {
return ApSL(lens, FromReaderIO[T](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
func ApEitherSL[S, T any](
lens L.Lens[S, T],
fa Result[T],
) Operator[S, S] {
return ApSL(lens, FromEither[T](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
func ApResultSL[S, T any](
lens L.Lens[S, T],
fa Result[T],
) Operator[S, S] {
return ApSL(lens, FromResult[T](fa))
}

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
@@ -26,11 +26,11 @@ import (
"github.com/stretchr/testify/assert"
)
func getLastName(s utils.Initial) ReaderIOEither[string] {
func getLastName(s utils.Initial) ReaderIOResult[string] {
return Of("Doe")
}
func getGivenName(s utils.WithLastName) ReaderIOEither[string] {
func getGivenName(s utils.WithLastName) ReaderIOResult[string] {
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
return Of(s.Independent + "-dependent")
}

View File

@@ -13,13 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
"github.com/IBM/fp-go/v2/internal/bracket"
"github.com/IBM/fp-go/v2/readerio"
RIOR "github.com/IBM/fp-go/v2/readerioresult"
)
// 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[
A, B, ANY any](
acquire ReaderIOEither[A],
acquire ReaderIOResult[A],
use Kleisli[A, B],
release func(A, Either[B]) ReaderIOEither[ANY],
) ReaderIOEither[B] {
return bracket.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY], Either[B], A, B](
readerio.Of[context.Context, Either[B]],
MonadChain[A, B],
readerio.MonadChain[context.Context, Either[B], Either[B]],
MonadChain[ANY, B],
acquire,
use,
release,
)
release func(A, Either[B]) ReaderIOResult[ANY],
) ReaderIOResult[B] {
return RIOR.Bracket(acquire, use, release)
}

View File

@@ -13,26 +13,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"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"
)
// 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
// without executing the wrapped computation.
//
// This is useful for adding cancellation awareness to computations that might not check the context themselves.
//
// 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.
func WithContext[A any](ma ReaderIOEither[A]) ReaderIOEither[A] {
// Returns a ReaderIOResult that checks for cancellation before executing.
func WithContext[A any](ma ReaderIOResult[A]) ReaderIOResult[A] {
return func(ctx context.Context) IOEither[A] {
if err := context.Cause(ctx); err != nil {
return ioeither.Left[A](err)

View 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

View File

@@ -13,13 +13,13 @@
// See the License for the specific language governing permissions and
// 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
// for typical Go applications where context-aware, effectful computations with error handling are needed.
//
// # Core Concept
//
// ReaderIOEither[A] represents a computation that:
// ReaderIOResult[A] represents a computation that:
// - Depends on a [context.Context] (Reader aspect)
// - Performs side effects (IO aspect)
// - Can fail with an [error] (Either aspect)
@@ -27,7 +27,7 @@
//
// 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:
// - Reader: Dependency injection via context
@@ -50,7 +50,7 @@
// - [Left]: Create failed computations
// - [FromEither], [FromIO], [FromIOEither]: Convert from other types
// - [TryCatch]: Wrap error-returning functions
// - [Eitherize0-10]: Convert standard Go functions to ReaderIOEither
// - [Eitherize0-10]: Convert standard Go functions to ReaderIOResult
//
// Transformation:
// - [Map]: Transform success values
@@ -90,15 +90,15 @@
// import (
// "context"
// "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"
// )
//
// // 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(
// 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 func() (User, error) {
// return userService.Get(ctx, id)
@@ -138,8 +138,8 @@
// openFile("data.txt"),
// closeFile,
// ),
// func(use func(func(*os.File) RIOE.ReaderIOEither[string]) RIOE.ReaderIOEither[string]) RIOE.ReaderIOEither[string] {
// return use(func(file *os.File) 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.ReaderIOResult[string] {
// return readContent(file)
// })
// },
@@ -166,4 +166,4 @@
// result := computation(ctx)() // Returns Left with cancellation error
//
//go:generate go run ../.. contextreaderioeither --count 10 --filename gen.go
package readerioeither
package readerioresult

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
@@ -22,14 +22,14 @@ import (
RIOE "github.com/IBM/fp-go/v2/readerioeither"
)
// Eq implements the equals predicate for values contained in the [ReaderIOEither] monad.
// It creates an equality checker that can compare two ReaderIOEither values by executing them
// Eq implements the equals predicate for values contained in the [ReaderIOResult] monad.
// 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.
//
// Parameters:
// - 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:
//
@@ -41,6 +41,6 @@ import (
// equal := eqRIE(ctx).Equals(Right[int](42), Right[int](42)) // true
//
//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)
}

View File

@@ -18,7 +18,7 @@ package exec
import (
"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"
F "github.com/IBM/fp-go/v2/function"
GE "github.com/IBM/fp-go/v2/internal/exec"
@@ -30,7 +30,7 @@ var (
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 IOE.TryCatchError(func() (exec.CommandOutput, error) {
return GE.Exec(ctx, name, args, in)

View File

@@ -20,7 +20,7 @@ import (
"io"
"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"
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/file"
@@ -44,7 +44,7 @@ var (
)
// 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(
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
func ReadFile(path string) RIOE.ReaderIOEither[[]byte] {
return RIOE.WithResource[[]byte](Open(path), Close[*os.File])(func(r *os.File) RIOE.ReaderIOEither[[]byte] {
func ReadFile(path string) RIOE.ReaderIOResult[[]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() ET.Either[error, []byte] {
return file.ReadAll(ctx, r)

View File

@@ -19,7 +19,7 @@ import (
"context"
"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"
"github.com/IBM/fp-go/v2/io"
J "github.com/IBM/fp-go/v2/json"

View File

@@ -18,7 +18,7 @@ package file
import (
"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"
IO "github.com/IBM/fp-go/v2/io"
IOF "github.com/IBM/fp-go/v2/io/file"
@@ -38,7 +38,7 @@ var (
)
// 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(
IOEF.CreateTemp(dir, pattern),
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
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)
}

View File

@@ -20,7 +20,7 @@ import (
"os"
"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"
F "github.com/IBM/fp-go/v2/function"
"github.com/stretchr/testify/assert"
@@ -35,7 +35,7 @@ func TestWithTempFile(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(
f,
onWriteAll[*os.File]([]byte("Carsten")),

View File

@@ -19,12 +19,12 @@ import (
"context"
"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"
)
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOEither[[]byte] {
return func(w W) RIOE.ReaderIOEither[[]byte] {
func onWriteAll[W io.Writer](data []byte) func(w W) RIOE.ReaderIOResult[[]byte] {
return func(w W) RIOE.ReaderIOResult[[]byte] {
return F.Pipe1(
RIOE.TryCatch(func(_ context.Context) 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
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)
return func(onCreate RIOE.ReaderIOEither[W]) RIOE.ReaderIOEither[[]byte] {
return func(onCreate RIOE.ReaderIOResult[W]) RIOE.ReaderIOResult[[]byte] {
return RIOE.WithResource[[]byte](
onCreate,
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
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](
acquire,
Close[W])

View File

@@ -14,12 +14,12 @@
// limitations under the License.
// 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
// and context support.
//
// 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
// - Automatic header management including Content-Length
// - Support for requests with and without bodies
@@ -31,7 +31,7 @@
// import (
// "context"
// 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(
@@ -51,8 +51,8 @@ import (
"net/http"
"strconv"
RIOE "github.com/IBM/fp-go/v2/context/readerioeither"
RIOEH "github.com/IBM/fp-go/v2/context/readerioeither/http"
RIOE "github.com/IBM/fp-go/v2/context/readerioresult"
RIOEH "github.com/IBM/fp-go/v2/context/readerioresult/http"
E "github.com/IBM/fp-go/v2/either"
F "github.com/IBM/fp-go/v2/function"
R "github.com/IBM/fp-go/v2/http/builder"
@@ -61,7 +61,7 @@ import (
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
// Content-Length for requests with bodies.
//
@@ -86,14 +86,14 @@ import (
// - builder: A pointer to an http/builder.Builder containing request configuration
//
// 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
//
// Example with body:
//
// import (
// 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(
@@ -116,7 +116,7 @@ import (
// result := requester(context.Background())()
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 func() (*http.Request, error) {
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 func() (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, url, nil)
@@ -144,8 +144,8 @@ func Requester(builder *R.Builder) RIOEH.Requester {
return F.Pipe5(
builder.GetBody(),
O.Fold(LZ.Of(E.Of[error](withoutBody)), E.Map[error](withBody)),
E.Ap[func(string) RIOE.ReaderIOEither[*http.Request]](builder.GetTargetURL()),
E.Flap[error, RIOE.ReaderIOEither[*http.Request]](builder.GetMethod()),
E.Ap[func(string) RIOE.ReaderIOResult[*http.Request]](builder.GetTargetURL()),
E.Flap[error, RIOE.ReaderIOResult[*http.Request]](builder.GetMethod()),
E.GetOrElse(RIOE.Left[*http.Request]),
RIOE.Map(func(req *http.Request) *http.Request {
req.Header = H.Monoid.Concat(req.Header, builder.GetHeaders())

View File

@@ -21,7 +21,7 @@ import (
"net/url"
"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"
F "github.com/IBM/fp-go/v2/function"
R "github.com/IBM/fp-go/v2/http/builder"

View 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

View 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

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// 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,
// and response parsing capabilities. The package follows functional programming principles
// to ensure type-safe, testable, and maintainable HTTP operations.
@@ -36,7 +36,7 @@ import (
"net/http"
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"
H "github.com/IBM/fp-go/v2/http"
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
// or an HTTP request. This allows for composable request building with proper
// 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.
// 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 {
// 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,
// when executed with a context, performs the HTTP request and returns either
// an error or the HTTP response.
@@ -65,8 +65,8 @@ type (
// - req: A Requester that builds the HTTP request
//
// Returns:
// - A ReaderIOEither that produces either an error or an *http.Response
Do(Requester) RIOE.ReaderIOEither[*http.Response]
// - A ReaderIOResult that produces either an error or an *http.Response
Do(Requester) RIOE.ReaderIOResult[*http.Response]
}
// client is the internal implementation of the Client interface.
@@ -108,7 +108,7 @@ var (
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(
req,
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.
// 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:
// - 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
//
// 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)
//
// Example:
@@ -158,8 +158,8 @@ func MakeClient(httpClient *http.Client) Client {
// request := MakeGetRequest("https://api.example.com/data")
// fullResp := ReadFullResponse(client)(request)
// result := fullResp(context.Background())()
func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullResponse] {
return func(req Requester) RIOE.ReaderIOEither[H.FullResponse] {
func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOResult[H.FullResponse] {
return func(req Requester) RIOE.ReaderIOResult[H.FullResponse] {
return F.Flow3(
client.Do(req),
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
//
// 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
//
// Example:
@@ -195,7 +195,7 @@ func ReadFullResponse(client Client) func(Requester) RIOE.ReaderIOEither[H.FullR
// request := MakeGetRequest("https://api.example.com/data")
// readBytes := ReadAll(client)
// 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(
ReadFullResponse(client),
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
//
// 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
//
// Example:
@@ -219,7 +219,7 @@ func ReadAll(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
// request := MakeGetRequest("https://api.example.com/text")
// readText := ReadText(client)
// 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(
ReadAll(client),
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
// but will be removed in a future version. The capitalized version follows Go naming
// 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)
}
@@ -242,7 +242,7 @@ func ReadJson[A any](client Client) func(Requester) RIOE.ReaderIOEither[A] {
// 3. Reads the response body as bytes
//
// 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(
ReadFullResponse(client),
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
//
// 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
//
// Example:
@@ -278,7 +278,7 @@ func readJSON(client Client) func(Requester) RIOE.ReaderIOEither[[]byte] {
// request := MakeGetRequest("https://api.example.com/user/1")
// readUser := ReadJSON[User](client)
// 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(
readJSON(client),
RIOE.ChainEitherK(J.Unmarshal[A]),

View File

@@ -22,7 +22,7 @@ import (
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"
"github.com/IBM/fp-go/v2/errors"
F "github.com/IBM/fp-go/v2/function"
@@ -66,7 +66,7 @@ func (b simpleRequestBuilder) WithHeader(key, value string) simpleRequestBuilder
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 IOE.TryCatchError(func() (*H.Request, error) {
req, err := H.NewRequestWithContext(ctx, b.method, b.url, nil)

View File

@@ -13,96 +13,75 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
"github.com/IBM/fp-go/v2/monoid"
RIOR "github.com/IBM/fp-go/v2/readerioresult"
)
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).
//
// 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.
//
// Parameters:
// - 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] {
return monoid.ApplicativeMonoid(
Of[A],
MonadMap[A, func(A) A],
MonadAp[A, A],
m,
)
return RIOR.ApplicativeMonoid[context.Context](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.
//
// Parameters:
// - 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] {
return monoid.ApplicativeMonoid(
Of[A],
MonadMap[A, func(A) A],
MonadApSeq[A, A],
m,
)
return RIOR.ApplicativeMonoidSeq[context.Context](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.
//
// Parameters:
// - 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] {
return monoid.ApplicativeMonoid(
Of[A],
MonadMap[A, func(A) A],
MonadApPar[A, A],
m,
)
return RIOR.ApplicativeMonoidPar[context.Context](m)
}
// AlternativeMonoid is the alternative [Monoid] for [ReaderIOEither].
// This combines ReaderIOEither values using the alternative semantics,
// AlternativeMonoid is the alternative [Monoid] for [ReaderIOResult].
// This combines ReaderIOResult values using the alternative semantics,
// where the second value is only evaluated if the first fails.
//
// Parameters:
// - 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] {
return monoid.AlternativeMonoid(
Of[A],
MonadMap[A, func(A) A],
MonadAp[A, A],
MonadAlt[A],
m,
)
return RIOR.AlternativeMonoid[context.Context](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,
// and combination uses the Alt operation (try first, fallback to second on failure).
//
// Parameters:
// - zero: Lazy computation that provides the empty/identity value
//
// Returns a Monoid for ReaderIOEither[A] with Alt-based combination.
func AltMonoid[A any](zero Lazy[ReaderIOEither[A]]) Monoid[A] {
return monoid.AltMonoid(
zero,
MonadAlt[A],
)
// Returns a Monoid for ReaderIOResult[A] with Alt-based combination.
func AltMonoid[A any](zero Lazy[ReaderIOResult[A]]) Monoid[A] {
return RIOR.AltMonoid[context.Context](zero)
}

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
@@ -24,7 +24,8 @@ import (
"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"
"github.com/IBM/fp-go/v2/ioresult"
RIOR "github.com/IBM/fp-go/v2/readerioresult"
)
const (
@@ -32,194 +33,207 @@ const (
useParallel = true
)
// FromEither converts an [Either] into a [ReaderIOEither].
// 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 ReaderIOEither
// - e: The Either value to lift into ReaderIOResult
//
// Returns a ReaderIOEither that produces the given Either value.
// Returns a ReaderIOResult that produces the given Either value.
//
//go:inline
func FromEither[A any](e Either[A]) ReaderIOEither[A] {
return readerioeither.FromEither[context.Context](e)
func FromEither[A any](e Either[A]) ReaderIOResult[A] {
return RIOR.FromEither[context.Context](e)
}
// Left creates a [ReaderIOEither] that represents a failed computation with the given error.
// 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 ReaderIOEither that always fails with the given error.
func Left[A any](l error) ReaderIOEither[A] {
return readerioeither.Left[context.Context, A](l)
// 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 [ReaderIOEither] that represents a successful computation with the given value.
// Right creates a [ReaderIOResult] that represents a successful computation with the given value.
//
// Parameters:
// - r: The success value
//
// Returns a ReaderIOEither that always succeeds with the given value.
// Returns a ReaderIOResult that always succeeds with the given value.
//
//go:inline
func Right[A any](r A) ReaderIOEither[A] {
return readerioeither.Right[context.Context, error](r)
func Right[A any](r A) ReaderIOResult[A] {
return RIOR.Right[context.Context, A](r)
}
// MonadMap transforms the success value of a [ReaderIOEither] using the provided function.
// MonadMap transforms the success value of a [ReaderIOResult] using the provided function.
// If the computation fails, the error is propagated unchanged.
//
// Parameters:
// - fa: The ReaderIOEither to transform
// - fa: The ReaderIOResult to transform
// - f: The transformation function
//
// Returns a new ReaderIOEither with the transformed value.
// Returns a new ReaderIOResult 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)
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 [ReaderIOEither] using the provided function.
// 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 ReaderIOEither.
// Returns a function that transforms a ReaderIOResult.
//
//go:inline
func Map[A, B any](f func(A) B) Operator[A, B] {
return readerioeither.Map[context.Context, error](f)
return RIOR.Map[context.Context](f)
}
// MonadMapTo replaces the success value of a [ReaderIOEither] with a constant value.
// MonadMapTo replaces the success value of a [ReaderIOResult] with a constant value.
// If the computation fails, the error is propagated unchanged.
//
// Parameters:
// - fa: The ReaderIOEither to transform
// - fa: The ReaderIOResult to transform
// - b: The constant value to use
//
// Returns a new ReaderIOEither with the constant value.
// Returns a new ReaderIOResult with the constant value.
//
//go:inline
func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] {
return readerioeither.MonadMapTo(fa, b)
func MonadMapTo[A, B any](fa ReaderIOResult[A], b B) ReaderIOResult[B] {
return RIOR.MonadMapTo(fa, b)
}
// MapTo replaces the success value of a [ReaderIOEither] with a constant value.
// 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 ReaderIOEither.
// Returns a function that transforms a ReaderIOResult.
//
//go:inline
func MapTo[A, B any](b B) Operator[A, B] {
return readerioeither.MapTo[context.Context, error, A](b)
return RIOR.MapTo[context.Context, A](b)
}
// MonadChain sequences two [ReaderIOEither] computations, where the second depends on the result of the first.
// 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 ReaderIOEither
// - f: Function that produces the second ReaderIOEither based on the first's result
// - ma: The first ReaderIOResult
// - f: Function that produces the second ReaderIOResult based on the first's result
//
// Returns a new ReaderIOEither representing the sequenced computation.
// Returns a new ReaderIOResult 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)
func MonadChain[A, B any](ma ReaderIOResult[A], f Kleisli[A, B]) ReaderIOResult[B] {
return RIOR.MonadChain(ma, f)
}
// Chain sequences two [ReaderIOEither] computations, where the second depends on the result of the first.
// 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 ReaderIOEither based on the first's result
// - f: Function that produces the second ReaderIOResult based on the first's result
//
// Returns a function that sequences ReaderIOEither computations.
// Returns a function that sequences ReaderIOResult computations.
//
//go:inline
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
return readerioeither.Chain(f)
return RIOR.Chain(f)
}
// MonadChainFirst sequences two [ReaderIOEither] computations but returns the result of the first.
// 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 ReaderIOEither
// - f: Function that produces the second ReaderIOEither
// - ma: The first ReaderIOResult
// - f: Function that produces the second ReaderIOResult
//
// Returns a ReaderIOEither with the result of the first computation.
// Returns a ReaderIOResult 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)
func MonadChainFirst[A, B any](ma ReaderIOResult[A], f Kleisli[A, B]) ReaderIOResult[A] {
return RIOR.MonadChainFirst(ma, f)
}
// ChainFirst sequences two [ReaderIOEither] computations but returns the result of the first.
// 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 ReaderIOEither
// - f: Function that produces the second ReaderIOResult
//
// Returns a function that sequences ReaderIOEither computations.
// Returns a function that sequences ReaderIOResult computations.
//
//go:inline
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
return readerioeither.ChainFirst(f)
return RIOR.ChainFirst(f)
}
// Of creates a [ReaderIOEither] that always succeeds with the given value.
// 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 ReaderIOEither that always succeeds with the given value.
// Returns a ReaderIOResult 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 Of[A any](a A) ReaderIOResult[A] {
return RIOR.Of[context.Context](a)
}
func withCancelCauseFunc[A any](cancel context.CancelCauseFunc, ma IOEither[A]) IOEither[A] {
func withCancelCauseFunc[A any](cancel context.CancelCauseFunc, ma IOResult[A]) IOResult[A] {
return function.Pipe3(
ma,
ioeither.Swap[error, A],
ioresult.Swap[A],
ioeither.ChainFirstIOK[A](func(err error) func() any {
return io.FromImpure(func() { cancel(err) })
}),
ioeither.Swap[A, error],
ioeither.Swap[A],
)
}
// MonadApPar implements parallel applicative application for [ReaderIOEither].
// 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: ReaderIOEither containing a function
// - fa: ReaderIOEither containing a value
// - fab: ReaderIOResult containing a function
// - fa: ReaderIOResult 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] {
// 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) IOEither[B] {
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() Either[B] {
return func() Result[B] {
// quick check for cancellation
if err := context.Cause(ctx); err != nil {
return either.Left[B](err)
@@ -232,21 +246,21 @@ func MonadApPar[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) R
fabIOE := withCancelCauseFunc(cancelSub, cfab(ctxSub))
faIOE := withCancelCauseFunc(cancelSub, cfa(ctxSub))
return ioeither.MonadApPar(fabIOE, faIOE)()
return ioresult.MonadApPar(fabIOE, faIOE)()
}
}
}
// MonadAp implements applicative application for [ReaderIOEither].
// 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: ReaderIOEither containing a function
// - fa: ReaderIOEither containing a value
// - fab: ReaderIOResult containing a function
// - fa: ReaderIOResult 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] {
// 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)
@@ -254,111 +268,111 @@ func MonadAp[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) Read
return MonadApSeq(fab, fa)
}
// MonadApSeq implements sequential applicative application for [ReaderIOEither].
// MonadApSeq implements sequential applicative application for [ReaderIOResult].
// It executes the function computation first, then the value computation.
//
// Parameters:
// - fab: ReaderIOEither containing a function
// - fa: ReaderIOEither containing a value
// - fab: ReaderIOResult containing a function
// - fa: ReaderIOResult containing a value
//
// Returns a ReaderIOEither with the function applied to the value.
// Returns a ReaderIOResult 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)
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 [ReaderIOEither] to a value wrapped in a ReaderIOEither.
// 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: ReaderIOEither containing a value
// - fa: ReaderIOResult containing a value
//
// Returns a function that applies a ReaderIOEither function to the value.
// Returns a function that applies a ReaderIOResult function to the value.
//
//go:inline
func Ap[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
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 [ReaderIOEither] to a value sequentially.
// ApSeq applies a function wrapped in a [ReaderIOResult] to a value sequentially.
// This is the curried version of [MonadApSeq].
//
// Parameters:
// - fa: ReaderIOEither containing a value
// - fa: ReaderIOResult containing a value
//
// Returns a function that applies a ReaderIOEither function to the value sequentially.
// Returns a function that applies a ReaderIOResult function to the value sequentially.
//
//go:inline
func ApSeq[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
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 [ReaderIOEither] to a value in parallel.
// ApPar applies a function wrapped in a [ReaderIOResult] to a value in parallel.
// This is the curried version of [MonadApPar].
//
// Parameters:
// - fa: ReaderIOEither containing a value
// - fa: ReaderIOResult containing a value
//
// Returns a function that applies a ReaderIOEither function to the value in parallel.
// Returns a function that applies a ReaderIOResult function to the value in parallel.
//
//go:inline
func ApPar[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
func ApPar[B, A any](fa ReaderIOResult[A]) Operator[func(A) B, B] {
return function.Bind2nd(MonadApPar[B, A], fa)
}
// FromPredicate creates a [ReaderIOEither] from a predicate function.
// 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 ReaderIOEither based on the predicate.
// 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 readerioeither.FromPredicate[context.Context](pred, onFalse)
return RIOR.FromPredicate[context.Context](pred, onFalse)
}
// OrElse provides an alternative [ReaderIOEither] computation if the first one fails.
// 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 ReaderIOEither from the error
// - 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 readerioeither.OrElse[context.Context](onLeft)
return RIOR.OrElse(onLeft)
}
// Ask returns a [ReaderIOEither] that provides access to the context.
// Ask returns a [ReaderIOResult] that provides access to the context.
// This is useful for accessing the [context.Context] within a computation.
//
// Returns a ReaderIOEither that produces the context.
// Returns a ReaderIOResult that produces the context.
//
//go:inline
func Ask() ReaderIOEither[context.Context] {
return readerioeither.Ask[context.Context, error]()
func Ask() ReaderIOResult[context.Context] {
return RIOR.Ask[context.Context]()
}
// MonadChainEitherK chains a function that returns an [Either] into a [ReaderIOEither] computation.
// This is useful for integrating pure Either-returning functions into ReaderIOEither workflows.
// 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 ReaderIOEither to chain from
// - ma: The ReaderIOResult to chain from
// - f: Function that produces an Either
//
// Returns a new ReaderIOEither with the chained computation.
// Returns a new ReaderIOResult 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)
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 [ReaderIOEither] computation.
// ChainEitherK chains a function that returns an [Either] into a [ReaderIOResult] computation.
// This is the curried version of [MonadChainEitherK].
//
// Parameters:
@@ -368,21 +382,21 @@ func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) Read
//
//go:inline
func ChainEitherK[A, B any](f func(A) Either[B]) Operator[A, B] {
return readerioeither.ChainEitherK[context.Context](f)
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 ReaderIOEither to chain from
// - ma: The ReaderIOResult to chain from
// - f: Function that produces an Either
//
// Returns a ReaderIOEither with the original value if both computations succeed.
// Returns a ReaderIOResult 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)
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.
@@ -395,68 +409,83 @@ func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B])
//
//go:inline
func ChainFirstEitherK[A, B any](f func(A) Either[B]) Operator[A, A] {
return readerioeither.ChainFirstEitherK[context.Context](f)
return RIOR.ChainFirstEitherK[context.Context](f)
}
// ChainOptionK chains a function that returns an [Option] into a [ReaderIOEither] computation.
// 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 ReaderIOEither.
// 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 readerioeither.ChainOptionK[context.Context, A, B](onNone)
return RIOR.ChainOptionK[context.Context, A, B](onNone)
}
// FromIOEither converts an [IOEither] into a [ReaderIOEither].
// FromIOEither converts an [IOResult] into a [ReaderIOResult].
// The resulting computation ignores the context.
//
// Parameters:
// - t: The IOEither to convert
// - t: The IOResult to convert
//
// Returns a ReaderIOEither that executes the IOEither.
// Returns a ReaderIOResult that executes the IOResult.
//
//go:inline
func FromIOEither[A any](t ioeither.IOEither[error, A]) ReaderIOEither[A] {
return readerioeither.FromIOEither[context.Context](t)
func FromIOEither[A any](t IOResult[A]) ReaderIOResult[A] {
return RIOR.FromIOEither[context.Context](t)
}
// FromIO converts an [IO] into a [ReaderIOEither].
//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 ReaderIOEither that executes the IO and wraps the result in Right.
// Returns a ReaderIOResult 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)
func FromIO[A any](t IO[A]) ReaderIOResult[A] {
return RIOR.FromIO[context.Context](t)
}
// FromLazy converts a [Lazy] computation into a [ReaderIOEither].
//go:inline
func FromReader[A any](t Reader[context.Context, A]) ReaderIOResult[A] {
return RIOR.FromReader[context.Context](t)
}
//go:inline
func FromReaderIO[A any](t ReaderIO[A]) ReaderIOResult[A] {
return RIOR.FromReaderIO[context.Context](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 ReaderIOEither that executes the Lazy computation and wraps the result in Right.
// Returns a ReaderIOResult 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)
func FromLazy[A any](t Lazy[A]) ReaderIOResult[A] {
return RIOR.FromIO[context.Context](t)
}
// Never returns a [ReaderIOEither] that blocks indefinitely until the context is canceled.
// 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 ReaderIOEither that waits for context cancellation and returns the cancellation error.
func Never[A any]() ReaderIOEither[A] {
return func(ctx context.Context) IOEither[A] {
// 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))
@@ -464,21 +493,21 @@ func Never[A any]() ReaderIOEither[A] {
}
}
// MonadChainIOK chains a function that returns an [IO] into a [ReaderIOEither] computation.
// 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 ReaderIOEither to chain from
// - ma: The ReaderIOResult to chain from
// - f: Function that produces an IO
//
// Returns a new ReaderIOEither with the chained IO computation.
// Returns a new ReaderIOResult 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)
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 [ReaderIOEither] computation.
// ChainIOK chains a function that returns an [IO] into a [ReaderIOResult] computation.
// This is the curried version of [MonadChainIOK].
//
// Parameters:
@@ -488,21 +517,21 @@ func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEith
//
//go:inline
func ChainIOK[A, B any](f func(A) IO[B]) Operator[A, B] {
return readerioeither.ChainIOK[context.Context, error](f)
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 ReaderIOEither to chain from
// - ma: The ReaderIOResult to chain from
// - f: Function that produces an IO
//
// Returns a ReaderIOEither with the original value after executing the IO.
// Returns a ReaderIOResult 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)
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.
@@ -515,20 +544,20 @@ func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderI
//
//go:inline
func ChainFirstIOK[A, B any](f func(A) IO[B]) Operator[A, A] {
return readerioeither.ChainFirstIOK[context.Context, error](f)
return RIOR.ChainFirstIOK[context.Context](f)
}
// ChainIOEitherK chains a function that returns an [IOEither] into a [ReaderIOEither] computation.
// This is useful for integrating IOEither-returning functions into ReaderIOEither workflows.
// 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 IOEither
// - f: Function that produces an IOResult
//
// Returns a function that chains the IOEither-returning function.
// Returns a function that chains the IOResult-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)
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.
@@ -537,10 +566,10 @@ func ChainIOEitherK[A, B any](f func(A) ioeither.IOEither[error, B]) Operator[A,
// Parameters:
// - delay: The duration to wait before executing the computation
//
// Returns a function that delays a ReaderIOEither computation.
// Returns a function that delays a ReaderIOResult 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(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)
@@ -563,8 +592,8 @@ func Delay[A any](delay time.Duration) Operator[A, A] {
// 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] {
// 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],
@@ -572,149 +601,149 @@ func Timer(delay time.Duration) ReaderIOEither[time.Time] {
)
}
// Defer creates a [ReaderIOEither] by lazily generating a new computation each time it's executed.
// 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 ReaderIOEither
// - gen: Lazy generator function that produces a ReaderIOResult
//
// Returns a ReaderIOEither that generates a fresh computation on each execution.
// Returns a ReaderIOResult that generates a fresh computation on each execution.
//
//go:inline
func Defer[A any](gen Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
return readerioeither.Defer(gen)
func Defer[A any](gen Lazy[ReaderIOResult[A]]) ReaderIOResult[A] {
return RIOR.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.
// 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, error)
// - f: Function that takes a context and returns a function producing (value)
//
// Returns a ReaderIOEither that wraps the error-returning function.
// Returns a ReaderIOResult 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)
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOResult[A] {
return RIOR.TryCatch(f, errors.IdentityError)
}
// MonadAlt provides an alternative [ReaderIOEither] if the first one fails.
// MonadAlt provides an alternative [ReaderIOResult] 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
// - first: The primary ReaderIOResult to try
// - second: Lazy alternative ReaderIOResult to use if first fails
//
// Returns a ReaderIOEither that tries the first, then the second if first fails.
// Returns a ReaderIOResult 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)
func MonadAlt[A any](first ReaderIOResult[A], second Lazy[ReaderIOResult[A]]) ReaderIOResult[A] {
return RIOR.MonadAlt(first, second)
}
// Alt provides an alternative [ReaderIOEither] if the first one fails.
// Alt provides an alternative [ReaderIOResult] if the first one fails.
// This is the curried version of [MonadAlt].
//
// Parameters:
// - second: Lazy alternative ReaderIOEither to use if first fails
// - second: Lazy alternative ReaderIOResult 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)
func Alt[A any](second Lazy[ReaderIOResult[A]]) Operator[A, A] {
return RIOR.Alt(second)
}
// Memoize computes the value of the provided [ReaderIOEither] monad lazily but exactly once.
// 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 ReaderIOEither to memoize
// - rdr: The ReaderIOResult to memoize
//
// Returns a ReaderIOEither that caches its result after the first execution.
// Returns a ReaderIOResult that caches its result after the first execution.
//
//go:inline
func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
return readerioeither.Memoize(rdr)
func Memoize[A any](rdr ReaderIOResult[A]) ReaderIOResult[A] {
return RIOR.Memoize(rdr)
}
// Flatten converts a nested [ReaderIOEither] into a flat [ReaderIOEither].
// Flatten converts a nested [ReaderIOResult] into a flat [ReaderIOResult].
// This is equivalent to [MonadChain] with the identity function.
//
// Parameters:
// - rdr: The nested ReaderIOEither to flatten
// - rdr: The nested ReaderIOResult to flatten
//
// Returns a flattened ReaderIOEither.
// Returns a flattened ReaderIOResult.
//
//go:inline
func Flatten[A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
return readerioeither.Flatten(rdr)
func Flatten[A any](rdr ReaderIOResult[ReaderIOResult[A]]) ReaderIOResult[A] {
return RIOR.Flatten(rdr)
}
// MonadFlap applies a value to a function wrapped in a [ReaderIOEither].
// MonadFlap applies a value to a function wrapped in a [ReaderIOResult].
// This is the reverse of [MonadAp], useful in certain composition scenarios.
//
// Parameters:
// - fab: ReaderIOEither containing a function
// - fab: ReaderIOResult containing a function
// - a: The value to apply to the function
//
// Returns a ReaderIOEither with the function applied to the value.
// Returns a ReaderIOResult 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)
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 [ReaderIOEither].
// 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 ReaderIOEither 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 readerioeither.Flap[context.Context, error, B](a)
return RIOR.Flap[context.Context, 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.
// 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 ReaderIOEither into a new ReaderIOEither.
// 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 readerioeither.Fold(onLeft, onRight)
return RIOR.Fold(onLeft, onRight)
}
// GetOrElse extracts the value from a [ReaderIOEither], providing a default via a function if it fails.
// 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 ReaderIOEither to a ReaderIO.
// Returns a function that converts a ReaderIOResult to a ReaderIO.
//
//go:inline
func GetOrElse[A any](onLeft func(error) ReaderIO[A]) func(ReaderIOEither[A]) ReaderIO[A] {
return readerioeither.GetOrElse(onLeft)
func GetOrElse[A any](onLeft func(error) ReaderIO[A]) func(ReaderIOResult[A]) ReaderIO[A] {
return RIOR.GetOrElse(onLeft)
}
// OrLeft transforms the error of a [ReaderIOEither] using the provided function.
// 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 ReaderIOEither.
// 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 readerioeither.OrLeft[A](onLeft)
return RIOR.OrLeft[A](onLeft)
}

View File

@@ -0,0 +1,887 @@
// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioresult
import (
"context"
"errors"
"testing"
"time"
E "github.com/IBM/fp-go/v2/either"
F "github.com/IBM/fp-go/v2/function"
IOE "github.com/IBM/fp-go/v2/ioeither"
)
var (
benchErr = errors.New("benchmark error")
benchCtx = context.Background()
benchResult Either[int]
benchRIOE ReaderIOResult[int]
benchInt int
)
// Benchmark core constructors
func BenchmarkLeft(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Left[int](benchErr)
}
}
func BenchmarkRight(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Right(42)
}
}
func BenchmarkOf(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Of(42)
}
}
func BenchmarkFromEither_Right(b *testing.B) {
either := E.Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = FromEither(either)
}
}
func BenchmarkFromEither_Left(b *testing.B) {
either := E.Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = FromEither(either)
}
}
func BenchmarkFromIO(b *testing.B) {
io := func() int { return 42 }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = FromIO(io)
}
}
func BenchmarkFromIOEither_Right(b *testing.B) {
ioe := IOE.Of[error](42)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = FromIOEither(ioe)
}
}
func BenchmarkFromIOEither_Left(b *testing.B) {
ioe := IOE.Left[int](benchErr)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = FromIOEither(ioe)
}
}
// Benchmark execution
func BenchmarkExecute_Right(b *testing.B) {
rioe := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
func BenchmarkExecute_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
func BenchmarkExecute_WithContext(b *testing.B) {
rioe := Right(42)
ctx, cancel := context.WithCancel(benchCtx)
defer cancel()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(ctx)()
}
}
// Benchmark functor operations
func BenchmarkMonadMap_Right(b *testing.B) {
rioe := Right(42)
mapper := func(a int) int { return a * 2 }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadMap(rioe, mapper)
}
}
func BenchmarkMonadMap_Left(b *testing.B) {
rioe := Left[int](benchErr)
mapper := func(a int) int { return a * 2 }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadMap(rioe, mapper)
}
}
func BenchmarkMap_Right(b *testing.B) {
rioe := Right(42)
mapper := Map(func(a int) int { return a * 2 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = mapper(rioe)
}
}
func BenchmarkMap_Left(b *testing.B) {
rioe := Left[int](benchErr)
mapper := Map(func(a int) int { return a * 2 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = mapper(rioe)
}
}
func BenchmarkMapTo_Right(b *testing.B) {
rioe := Right(42)
mapper := MapTo[int](99)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = mapper(rioe)
}
}
// Benchmark monad operations
func BenchmarkMonadChain_Right(b *testing.B) {
rioe := Right(42)
chainer := func(a int) ReaderIOResult[int] { return Right(a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadChain(rioe, chainer)
}
}
func BenchmarkMonadChain_Left(b *testing.B) {
rioe := Left[int](benchErr)
chainer := func(a int) ReaderIOResult[int] { return Right(a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadChain(rioe, chainer)
}
}
func BenchmarkChain_Right(b *testing.B) {
rioe := Right(42)
chainer := Chain(func(a int) ReaderIOResult[int] { return Right(a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChain_Left(b *testing.B) {
rioe := Left[int](benchErr)
chainer := Chain(func(a int) ReaderIOResult[int] { return Right(a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChainFirst_Right(b *testing.B) {
rioe := Right(42)
chainer := ChainFirst(func(a int) ReaderIOResult[string] { return Right("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChainFirst_Left(b *testing.B) {
rioe := Left[int](benchErr)
chainer := ChainFirst(func(a int) ReaderIOResult[string] { return Right("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkFlatten_Right(b *testing.B) {
nested := Right(Right(42))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Flatten(nested)
}
}
func BenchmarkFlatten_Left(b *testing.B) {
nested := Left[ReaderIOResult[int]](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Flatten(nested)
}
}
// Benchmark applicative operations
func BenchmarkMonadApSeq_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadApSeq(fab, fa)
}
}
func BenchmarkMonadApSeq_RightLeft(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fa := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadApSeq(fab, fa)
}
}
func BenchmarkMonadApSeq_LeftRight(b *testing.B) {
fab := Left[func(int) int](benchErr)
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadApSeq(fab, fa)
}
}
func BenchmarkMonadApPar_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadApPar(fab, fa)
}
}
func BenchmarkMonadApPar_RightLeft(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fa := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadApPar(fab, fa)
}
}
func BenchmarkMonadApPar_LeftRight(b *testing.B) {
fab := Left[func(int) int](benchErr)
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = MonadApPar(fab, fa)
}
}
// Benchmark execution of applicative operations
func BenchmarkExecuteApSeq_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fa := Right(42)
rioe := MonadApSeq(fab, fa)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
func BenchmarkExecuteApPar_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fa := Right(42)
rioe := MonadApPar(fab, fa)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
// Benchmark alternative operations
func BenchmarkAlt_RightRight(b *testing.B) {
rioe := Right(42)
alternative := Alt(func() ReaderIOResult[int] { return Right(99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = alternative(rioe)
}
}
func BenchmarkAlt_LeftRight(b *testing.B) {
rioe := Left[int](benchErr)
alternative := Alt(func() ReaderIOResult[int] { return Right(99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = alternative(rioe)
}
}
func BenchmarkOrElse_Right(b *testing.B) {
rioe := Right(42)
recover := OrElse(func(e error) ReaderIOResult[int] { return Right(0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = recover(rioe)
}
}
func BenchmarkOrElse_Left(b *testing.B) {
rioe := Left[int](benchErr)
recover := OrElse(func(e error) ReaderIOResult[int] { return Right(0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = recover(rioe)
}
}
// Benchmark chain operations with different types
func BenchmarkChainEitherK_Right(b *testing.B) {
rioe := Right(42)
chainer := ChainEitherK(func(a int) Either[int] { return E.Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChainEitherK_Left(b *testing.B) {
rioe := Left[int](benchErr)
chainer := ChainEitherK(func(a int) Either[int] { return E.Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChainIOK_Right(b *testing.B) {
rioe := Right(42)
chainer := ChainIOK(func(a int) func() int { return func() int { return a * 2 } })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChainIOK_Left(b *testing.B) {
rioe := Left[int](benchErr)
chainer := ChainIOK(func(a int) func() int { return func() int { return a * 2 } })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChainIOEitherK_Right(b *testing.B) {
rioe := Right(42)
chainer := ChainIOEitherK(func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
func BenchmarkChainIOEitherK_Left(b *testing.B) {
rioe := Left[int](benchErr)
chainer := ChainIOEitherK(func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = chainer(rioe)
}
}
// Benchmark context operations
func BenchmarkAsk(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = Ask()
}
}
func BenchmarkDefer(b *testing.B) {
gen := func() ReaderIOResult[int] { return Right(42) }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Defer(gen)
}
}
func BenchmarkMemoize(b *testing.B) {
rioe := Right(42)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Memoize(rioe)
}
}
// Benchmark delay operations
func BenchmarkDelay_Construction(b *testing.B) {
rioe := Right(42)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = Delay[int](time.Millisecond)(rioe)
}
}
func BenchmarkTimer_Construction(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = Timer(time.Millisecond)
}
}
// Benchmark TryCatch
func BenchmarkTryCatch_Success(b *testing.B) {
f := func(ctx context.Context) func() (int, error) {
return func() (int, error) { return 42, nil }
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = TryCatch(f)
}
}
func BenchmarkTryCatch_Error(b *testing.B) {
f := func(ctx context.Context) func() (int, error) {
return func() (int, error) { return 0, benchErr }
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = TryCatch(f)
}
}
func BenchmarkExecuteTryCatch_Success(b *testing.B) {
f := func(ctx context.Context) func() (int, error) {
return func() (int, error) { return 42, nil }
}
rioe := TryCatch(f)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
func BenchmarkExecuteTryCatch_Error(b *testing.B) {
f := func(ctx context.Context) func() (int, error) {
return func() (int, error) { return 0, benchErr }
}
rioe := TryCatch(f)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
// Benchmark pipeline operations
func BenchmarkPipeline_Map_Right(b *testing.B) {
rioe := Right(21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = F.Pipe1(
rioe,
Map(func(x int) int { return x * 2 }),
)
}
}
func BenchmarkPipeline_Map_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = F.Pipe1(
rioe,
Map(func(x int) int { return x * 2 }),
)
}
}
func BenchmarkPipeline_Chain_Right(b *testing.B) {
rioe := Right(21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = F.Pipe1(
rioe,
Chain(func(x int) ReaderIOResult[int] { return Right[int](x * 2) }),
)
}
}
func BenchmarkPipeline_Chain_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = F.Pipe1(
rioe,
Chain(func(x int) ReaderIOResult[int] { return Right[int](x * 2) }),
)
}
}
func BenchmarkPipeline_Complex_Right(b *testing.B) {
rioe := Right(10)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = F.Pipe3(
rioe,
Map(func(x int) int { return x * 2 }),
Chain(func(x int) ReaderIOResult[int] { return Right[int](x + 1) }),
Map(func(x int) int { return x * 2 }),
)
}
}
func BenchmarkPipeline_Complex_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchRIOE = F.Pipe3(
rioe,
Map(func(x int) int { return x * 2 }),
Chain(func(x int) ReaderIOResult[int] { return Right[int](x + 1) }),
Map(func(x int) int { return x * 2 }),
)
}
}
func BenchmarkExecutePipeline_Complex_Right(b *testing.B) {
rioe := F.Pipe3(
Right(10),
Map(func(x int) int { return x * 2 }),
Chain(func(x int) ReaderIOResult[int] { return Right[int](x + 1) }),
Map(func(x int) int { return x * 2 }),
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
// Benchmark do-notation operations
func BenchmarkDo(b *testing.B) {
type State struct{ value int }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = Do(State{})
}
}
func BenchmarkBind_Right(b *testing.B) {
type State struct{ value int }
initial := Do(State{})
binder := Bind(
func(v int) func(State) State {
return func(s State) State { return State{value: v} }
},
func(s State) ReaderIOResult[int] {
return Right(42)
},
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = binder(initial)
}
}
func BenchmarkLet_Right(b *testing.B) {
type State struct{ value int }
initial := Right(State{value: 10})
letter := Let(
func(v int) func(State) State {
return func(s State) State { return State{value: s.value + v} }
},
func(s State) int { return 32 },
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = letter(initial)
}
}
func BenchmarkApS_Right(b *testing.B) {
type State struct{ value int }
initial := Right(State{value: 10})
aps := ApS(
func(v int) func(State) State {
return func(s State) State { return State{value: v} }
},
Right(42),
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = aps(initial)
}
}
// Benchmark traverse operations
func BenchmarkTraverseArray_Empty(b *testing.B) {
arr := []int{}
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = traverser(arr)
}
}
func BenchmarkTraverseArray_Small(b *testing.B) {
arr := []int{1, 2, 3}
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = traverser(arr)
}
}
func BenchmarkTraverseArray_Medium(b *testing.B) {
arr := make([]int, 10)
for i := range arr {
arr[i] = i
}
traverser := TraverseArray(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = traverser(arr)
}
}
func BenchmarkTraverseArraySeq_Small(b *testing.B) {
arr := []int{1, 2, 3}
traverser := TraverseArraySeq(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = traverser(arr)
}
}
func BenchmarkTraverseArrayPar_Small(b *testing.B) {
arr := []int{1, 2, 3}
traverser := TraverseArrayPar(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = traverser(arr)
}
}
func BenchmarkSequenceArray_Small(b *testing.B) {
arr := []ReaderIOResult[int]{
Right(1),
Right(2),
Right(3),
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = SequenceArray(arr)
}
}
func BenchmarkExecuteTraverseArray_Small(b *testing.B) {
arr := []int{1, 2, 3}
rioe := TraverseArray(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})(arr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = rioe(benchCtx)()
}
}
func BenchmarkExecuteTraverseArraySeq_Small(b *testing.B) {
arr := []int{1, 2, 3}
rioe := TraverseArraySeq(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})(arr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = rioe(benchCtx)()
}
}
func BenchmarkExecuteTraverseArrayPar_Small(b *testing.B) {
arr := []int{1, 2, 3}
rioe := TraverseArrayPar(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})(arr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = rioe(benchCtx)()
}
}
// Benchmark record operations
func BenchmarkTraverseRecord_Small(b *testing.B) {
rec := map[string]int{"a": 1, "b": 2, "c": 3}
traverser := TraverseRecord[string](func(x int) ReaderIOResult[int] {
return Right(x * 2)
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = traverser(rec)
}
}
func BenchmarkSequenceRecord_Small(b *testing.B) {
rec := map[string]ReaderIOResult[int]{
"a": Right(1),
"b": Right(2),
"c": Right(3),
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = SequenceRecord(rec)
}
}
// Benchmark resource management
func BenchmarkWithResource_Success(b *testing.B) {
acquire := Right(42)
release := func(int) ReaderIOResult[int] { return Right(0) }
body := func(x int) ReaderIOResult[int] { return Right(x * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = WithResource[int](acquire, release)(body)
}
}
func BenchmarkExecuteWithResource_Success(b *testing.B) {
acquire := Right(42)
release := func(int) ReaderIOResult[int] { return Right(0) }
body := func(x int) ReaderIOResult[int] { return Right(x * 2) }
rioe := WithResource[int](acquire, release)(body)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
func BenchmarkExecuteWithResource_ErrorInBody(b *testing.B) {
acquire := Right(42)
release := func(int) ReaderIOResult[int] { return Right(0) }
body := func(x int) ReaderIOResult[int] { return Left[int](benchErr) }
rioe := WithResource[int](acquire, release)(body)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(benchCtx)()
}
}
// Benchmark context cancellation
func BenchmarkExecute_CanceledContext(b *testing.B) {
rioe := Right(42)
ctx, cancel := context.WithCancel(benchCtx)
cancel() // Cancel immediately
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(ctx)()
}
}
func BenchmarkExecuteApPar_CanceledContext(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fa := Right(42)
rioe := MonadApPar(fab, fa)
ctx, cancel := context.WithCancel(benchCtx)
cancel() // Cancel immediately
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = rioe(ctx)()
}
}
// Made with Bob

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
@@ -93,20 +93,20 @@ func TestMonadChain(t *testing.T) {
ctx := t.Context()
// Test with Right
result := MonadChain(Right(42), func(x int) ReaderIOEither[int] {
result := MonadChain(Right(42), func(x int) ReaderIOResult[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] {
result = MonadChain(Left[int](err), func(x int) ReaderIOResult[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] {
result = MonadChain(Right(42), func(x int) ReaderIOResult[int] {
return Left[int](errors.New("chain error"))
})(ctx)()
assert.True(t, E.IsLeft(result))
@@ -116,20 +116,20 @@ func TestMonadChainFirst(t *testing.T) {
ctx := t.Context()
// Test with Right
result := MonadChainFirst(Right(42), func(x int) ReaderIOEither[string] {
result := MonadChainFirst(Right(42), func(x int) ReaderIOResult[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] {
result = MonadChainFirst(Left[int](err), func(x int) ReaderIOResult[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] {
result = MonadChainFirst(Right(42), func(x int) ReaderIOResult[string] {
return Left[string](errors.New("chain error"))
})(ctx)()
assert.True(t, E.IsLeft(result))
@@ -331,7 +331,7 @@ func TestDefer(t *testing.T) {
ctx := t.Context()
count := 0
gen := func() ReaderIOEither[int] {
gen := func() ReaderIOResult[int] {
count++
return Right(count)
}
@@ -372,14 +372,14 @@ func TestMonadAlt(t *testing.T) {
ctx := t.Context()
// Test with Right (alternative not called)
result := MonadAlt(Right(42), func() ReaderIOEither[int] {
result := MonadAlt(Right(42), func() ReaderIOResult[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] {
result = MonadAlt(Left[int](err), func() ReaderIOResult[int] {
return Right(99)
})(ctx)()
assert.Equal(t, E.Right[error](99), result)
@@ -446,7 +446,7 @@ func TestSequenceArray(t *testing.T) {
ctx := t.Context()
// Test with all Right
arr := []ReaderIOEither[int]{Right(1), Right(2), Right(3)}
arr := []ReaderIOResult[int]{Right(1), Right(2), Right(3)}
result := SequenceArray(arr)(ctx)()
assert.True(t, E.IsRight(result))
vals, _ := E.Unwrap(result)
@@ -454,7 +454,7 @@ func TestSequenceArray(t *testing.T) {
// Test with one Left
err := errors.New("test error")
arr = []ReaderIOEither[int]{Right(1), Left[int](err), Right(3)}
arr = []ReaderIOResult[int]{Right(1), Left[int](err), Right(3)}
result = SequenceArray(arr)(ctx)()
assert.True(t, E.IsLeft(result))
}
@@ -464,7 +464,7 @@ func TestTraverseArray(t *testing.T) {
// Test transformation
arr := []int{1, 2, 3}
result := TraverseArray(func(x int) ReaderIOEither[int] {
result := TraverseArray(func(x int) ReaderIOResult[int] {
return Right(x * 2)
})(arr)(ctx)()
assert.True(t, E.IsRight(result))
@@ -476,7 +476,7 @@ func TestSequenceRecord(t *testing.T) {
ctx := t.Context()
// Test with all Right
rec := map[string]ReaderIOEither[int]{
rec := map[string]ReaderIOResult[int]{
"a": Right(1),
"b": Right(2),
}
@@ -492,7 +492,7 @@ func TestTraverseRecord(t *testing.T) {
// Test transformation
rec := map[string]int{"a": 1, "b": 2}
result := TraverseRecord[string](func(x int) ReaderIOEither[int] {
result := TraverseRecord[string](func(x int) ReaderIOResult[int] {
return Right(x * 2)
})(rec)(ctx)()
assert.True(t, E.IsRight(result))

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
@@ -143,7 +143,7 @@ func TestCanceledApply(t *testing.T) {
applied := F.Pipe1(
fct,
Ap[string, string](errValue),
Ap[string](errValue),
)
res := applied(context.Background())()
@@ -156,7 +156,7 @@ func TestRegularApply(t *testing.T) {
applied := F.Pipe1(
fct,
Ap[string, string](value),
Ap[string](value),
)
res := applied(context.Background())()
@@ -171,14 +171,14 @@ func TestWithResourceNoErrors(t *testing.T) {
return countAcquire
})
release := func(int) ReaderIOEither[int] {
release := func(int) ReaderIOResult[int] {
return FromLazy(func() int {
countRelease++
return countRelease
})
}
body := func(int) ReaderIOEither[int] {
body := func(int) ReaderIOResult[int] {
return FromLazy(func() int {
countBody++
return countBody
@@ -203,7 +203,7 @@ func TestWithResourceErrorInBody(t *testing.T) {
return countAcquire
})
release := func(int) ReaderIOEither[int] {
release := func(int) ReaderIOResult[int] {
return FromLazy(func() int {
countRelease++
return countRelease
@@ -211,7 +211,7 @@ func TestWithResourceErrorInBody(t *testing.T) {
}
err := fmt.Errorf("error in body")
body := func(int) ReaderIOEither[int] {
body := func(int) ReaderIOResult[int] {
return Left[int](err)
}
@@ -231,14 +231,14 @@ func TestWithResourceErrorInAcquire(t *testing.T) {
err := fmt.Errorf("error in acquire")
acquire := Left[int](err)
release := func(int) ReaderIOEither[int] {
release := func(int) ReaderIOResult[int] {
return FromLazy(func() int {
countRelease++
return countRelease
})
}
body := func(int) ReaderIOEither[int] {
body := func(int) ReaderIOResult[int] {
return FromLazy(func() int {
countBody++
return countBody
@@ -264,11 +264,11 @@ func TestWithResourceErrorInRelease(t *testing.T) {
})
err := fmt.Errorf("error in release")
release := func(int) ReaderIOEither[int] {
release := func(int) ReaderIOResult[int] {
return Left[int](err)
}
body := func(int) ReaderIOEither[int] {
body := func(int) ReaderIOResult[int] {
return FromLazy(func() int {
countBody++
return countBody

View File

@@ -13,13 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
"github.com/IBM/fp-go/v2/function"
RIE "github.com/IBM/fp-go/v2/readerioeither"
RIOR "github.com/IBM/fp-go/v2/readerioresult"
)
// 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)
//
// Parameters:
// - onCreate: ReaderIOEither that creates the resource
// - onCreate: ReaderIOResult that creates 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:
//
// file := WithResource(
// 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 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 func() (string, error) {
// 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] {
return function.Flow2(
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),
)
func WithResource[A, R, ANY any](onCreate ReaderIOResult[R], onRelease Kleisli[R, ANY]) Kleisli[Kleisli[R, A], A] {
return RIOR.WithResource[A](onCreate, onRelease)
}

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
@@ -37,7 +37,7 @@ var (
)
)
func closeFile(f *os.File) ReaderIOEither[string] {
func closeFile(f *os.File) ReaderIOResult[string] {
return F.Pipe1(
TryCatch(func(_ context.Context) func() (string, error) {
return func() (string, error) {
@@ -52,7 +52,7 @@ func ExampleWithResource() {
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(
TryCatch(func(_ context.Context) func() ([]byte, error) {
return func() ([]byte, error) {

View File

@@ -13,21 +13,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"github.com/IBM/fp-go/v2/semigroup"
)
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.
// 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.
//
// Returns a Semigroup for ReaderIOEither[A] with Alt-based combination.
// Returns a Semigroup for ReaderIOResult[A] with Alt-based combination.
//
// Example:
//

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
@@ -29,9 +29,9 @@ import (
// The lock parameter should return a CancelFunc that releases the lock when called.
//
// 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:
//
@@ -43,9 +43,9 @@ import (
// }
// })
// 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(
function.Constant1[context.CancelFunc, ReaderIOEither[A]],
function.Constant1[context.CancelFunc, ReaderIOResult[A]],
WithResource[A](lock, function.Flow2(
io.FromImpure[context.CancelFunc],
FromIO[any],

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"github.com/IBM/fp-go/v2/function"
@@ -21,13 +21,13 @@ import (
"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).
//
// 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] {
return array.Traverse[[]A](
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.
//
// 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.
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []B] {
// Returns a function that transforms an array into a ReaderIOResult of an array.
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
return array.TraverseWithIndex[[]A](
Of[[]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.
//
// Parameters:
// - ma: Array of ReaderIOEither values
// - ma: Array of ReaderIOResult values
//
// Returns a ReaderIOEither containing an array of values.
func SequenceArray[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
return TraverseArray(function.Identity[ReaderIOEither[A]])(ma)
// Returns a ReaderIOResult containing an array of values.
func SequenceArray[A any](ma []ReaderIOResult[A]) ReaderIOResult[[]A] {
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:
// - 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] {
return record.Traverse[map[K]A](
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.
//
// 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.
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, map[K]B] {
// Returns a function that transforms a map into a ReaderIOResult of a map.
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](
Of[map[K]B],
Map[map[K]B, func(B) map[K]B],
@@ -99,25 +99,25 @@ 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:
// - ma: Map of ReaderIOEither values
// - ma: Map of ReaderIOResult values
//
// Returns a ReaderIOEither containing a map of values.
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
return TraverseRecord[K](function.Identity[ReaderIOEither[A]])(ma)
// Returns a ReaderIOResult containing a map of values.
func SequenceRecord[K comparable, A any](ma map[K]ReaderIOResult[A]) ReaderIOResult[map[K]A] {
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.
//
// Parameters:
// - 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.
func MonadTraverseArraySeq[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B] {
// Returns a ReaderIOResult containing an array of transformed values.
func MonadTraverseArraySeq[A, B any](as []A, f Kleisli[A, B]) ReaderIOResult[[]B] {
return array.MonadTraverse[[]A](
Of[[]B],
Map[[]B, func(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.
//
// 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] {
return array.Traverse[[]A](
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]]
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []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) ReaderIOResult[B]) Kleisli[[]A, []B] {
return array.TraverseWithIndex[[]A](
Of[[]B],
Map[[]B, func(B) []B],
@@ -155,19 +155,19 @@ 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.
//
// Parameters:
// - ma: Array of ReaderIOEither values
// - ma: Array of ReaderIOResult values
//
// Returns a ReaderIOEither containing an array of values.
func SequenceArraySeq[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
return MonadTraverseArraySeq(ma, function.Identity[ReaderIOEither[A]])
// Returns a ReaderIOResult containing an array of values.
func SequenceArraySeq[A any](ma []ReaderIOResult[A]) ReaderIOResult[[]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]]
func MonadTraverseRecordSeq[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) 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]) ReaderIOResult[map[K]B] {
return record.MonadTraverse[map[K]A](
Of[map[K]B],
Map[map[K]B, func(B) map[K]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] {
return record.Traverse[map[K]A](
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]]
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, 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) ReaderIOResult[B]) Kleisli[map[K]A, map[K]B] {
return record.TraverseWithIndex[map[K]A](
Of[map[K]B],
Map[map[K]B, func(B) map[K]B],
@@ -200,19 +200,19 @@ func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEit
}
// 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] {
return MonadTraverseRecordSeq(ma, function.Identity[ReaderIOEither[A]])
func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOResult[A]) ReaderIOResult[map[K]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.
//
// Parameters:
// - 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.
func MonadTraverseArrayPar[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B] {
// Returns a ReaderIOResult containing an array of transformed values.
func MonadTraverseArrayPar[A, B any](as []A, f Kleisli[A, B]) ReaderIOResult[[]B] {
return array.MonadTraverse[[]A](
Of[[]B],
Map[[]B, func(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.
//
// 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] {
return array.Traverse[[]A](
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]]
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []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) ReaderIOResult[B]) Kleisli[[]A, []B] {
return array.TraverseWithIndex[[]A](
Of[[]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.
//
// Parameters:
// - ma: Array of ReaderIOEither values
// - ma: Array of ReaderIOResult values
//
// Returns a ReaderIOEither containing an array of values.
func SequenceArrayPar[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
return MonadTraverseArrayPar(ma, function.Identity[ReaderIOEither[A]])
// Returns a ReaderIOResult containing an array of values.
func SequenceArrayPar[A any](ma []ReaderIOResult[A]) ReaderIOResult[[]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] {
return record.Traverse[map[K]A](
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]]
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, 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) ReaderIOResult[B]) Kleisli[map[K]A, map[K]B] {
return record.TraverseWithIndex[map[K]A](
Of[map[K]B],
Map[map[K]B, func(B) map[K]B],
@@ -283,8 +283,8 @@ 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]]
func MonadTraverseRecordPar[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) 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]) ReaderIOResult[map[K]B] {
return record.MonadTraverse[map[K]A](
Of[map[K]B],
Map[map[K]B, func(B) map[K]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.
//
// Parameters:
// - ma: Map of ReaderIOEither values
// - ma: Map of ReaderIOResult values
//
// Returns a ReaderIOEither containing a map of values.
func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEither[map[K]A] {
return MonadTraverseRecordPar(ma, function.Identity[ReaderIOEither[A]])
// Returns a ReaderIOResult containing a map of values.
func SequenceRecordPar[K comparable, A any](ma map[K]ReaderIOResult[A]) ReaderIOResult[map[K]A] {
return MonadTraverseRecordPar(ma, function.Identity[ReaderIOResult[A]])
}

View File

@@ -13,11 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readerioeither
package readerioresult
import (
"context"
"github.com/IBM/fp-go/v2/context/ioresult"
"github.com/IBM/fp-go/v2/either"
"github.com/IBM/fp-go/v2/io"
"github.com/IBM/fp-go/v2/ioeither"
@@ -25,7 +26,8 @@ import (
"github.com/IBM/fp-go/v2/option"
"github.com/IBM/fp-go/v2/reader"
"github.com/IBM/fp-go/v2/readerio"
"github.com/IBM/fp-go/v2/readerioeither"
RIOR "github.com/IBM/fp-go/v2/readerioresult"
"github.com/IBM/fp-go/v2/result"
)
type (
@@ -40,6 +42,8 @@ type (
// Either[A] is equivalent to Either[error, A] from the either package.
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.
// The computation is not executed until explicitly invoked.
Lazy[A any] = lazy.Lazy[A]
@@ -56,6 +60,8 @@ type (
// IOEither[A] is equivalent to func() Either[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.
// This is used for dependency injection and accessing shared context.
//
@@ -68,21 +74,21 @@ type (
// ReaderIO[A] is equivalent to func(context.Context) func() 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)
// - Performs side effects (IO aspect)
// - Can fail with an [error] (Either aspect)
// - 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]
// - Error type fixed to [error]
//
// 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:
// func fetchUser(id string) ReaderIOEither[User] {
// func fetchUser(id string) ReaderIOResult[User] {
// return func(ctx context.Context) func() Either[error, User] {
// return func() Either[error, User] {
// user, err := userService.Get(ctx, id)
@@ -97,14 +103,14 @@ type (
// The computation is executed by providing a context and then invoking the result:
// ctx := context.Background()
// 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.
//
// Operator[A, B] is equivalent to Kleisli[ReaderIOEither[A], B]
// Operator[A, B] is equivalent to Kleisli[ReaderIOResult[A], B]
//
// Example usage:
// // Define a reusable transformation
@@ -112,5 +118,5 @@ type (
//
// // Apply the transformation
// result := toUpper(computation)
Operator[A, B any] = Kleisli[ReaderIOEither[A], B]
Operator[A, B any] = Kleisli[ReaderIOResult[A], B]
)

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
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
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)
}
// 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)
}

View File

@@ -13,11 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
import (
"context"
F "github.com/IBM/fp-go/v2/function"
L "github.com/IBM/fp-go/v2/optics/lens"
G "github.com/IBM/fp-go/v2/readereither/generic"
)
@@ -34,8 +33,8 @@ import (
// result := readereither.Do(State{})
func Do[S any](
empty S,
) ReaderEither[S] {
return G.Do[ReaderEither[S], context.Context, error, S](empty)
) ReaderResult[S] {
return G.Do[ReaderResult[S]](empty)
}
// 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 {
// 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] {
// if uid, ok := ctx.Value("userID").(string); ok {
// return either.Right[error](uid)
@@ -71,7 +70,7 @@ func Do[S any](
// func(tid string) func(State) State {
// 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
// return func(ctx context.Context) either.Either[error, string] {
// return either.Right[error]("tenant-" + s.UserID)
@@ -82,31 +81,31 @@ func Do[S any](
func Bind[S1, S2, T any](
setter func(T) func(S1) S2,
f Kleisli[S1, T],
) Kleisli[ReaderEither[S1], S2] {
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
) Kleisli[ReaderResult[S1], S2] {
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]
func Let[S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) T,
) Kleisli[ReaderEither[S1], S2] {
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
) Kleisli[ReaderResult[S1], S2] {
return G.Let[ReaderResult[S1], ReaderResult[S2]](setter, f)
}
// LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetTo[S1, S2, T any](
setter func(T) func(S1) S2,
b T,
) Kleisli[ReaderEither[S1], S2] {
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
) Kleisli[ReaderResult[S1], S2] {
return G.LetTo[ReaderResult[S1], ReaderResult[S2]](setter, b)
}
// BindTo initializes a new state [S1] from a value [T]
func BindTo[S1, T any](
setter func(T) S1,
) Kleisli[ReaderEither[T], S1] {
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter)
) Kleisli[ReaderResult[T], S1] {
return G.BindTo[ReaderResult[S1], ReaderResult[T]](setter)
}
// 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](
setter func(T) func(S1) S2,
fa ReaderEither[T],
) Kleisli[ReaderEither[S1], S2] {
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
fa ReaderResult[T],
) Kleisli[ReaderResult[S1], S2] {
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.
@@ -159,10 +158,10 @@ func ApS[S1, S2, T any](
//
// Parameters:
// - 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:
// - 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:
//
@@ -186,8 +185,8 @@ func ApS[S1, S2, T any](
// )
func ApSL[S, T any](
lens L.Lens[S, T],
fa ReaderEither[T],
) Kleisli[ReaderEither[S], S] {
fa ReaderResult[T],
) Kleisli[ReaderResult[S], S] {
return ApS(lens.Set, fa)
}
@@ -199,10 +198,10 @@ func ApSL[S, T any](
//
// Parameters:
// - 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:
// - A function that transforms ReaderEither[S] to ReaderEither[S]
// - A function that transforms ReaderResult[S] to ReaderResult[S]
//
// Example:
//
@@ -215,7 +214,7 @@ func ApSL[S, T any](
// 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] {
// if v >= 100 {
// return either.Left[int](errors.New("value too large"))
@@ -231,10 +230,8 @@ func ApSL[S, T any](
func BindL[S, T any](
lens L.Lens[S, T],
f Kleisli[T, T],
) Kleisli[ReaderEither[S], S] {
return Bind[S, S, T](lens.Set, func(s S) ReaderEither[T] {
return f(lens.Get(s))
})
) Kleisli[ReaderResult[S], S] {
return Bind(lens.Set, F.Flow2(lens.Get, f))
}
// 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
//
// Returns:
// - A function that transforms ReaderEither[S] to ReaderEither[S]
// - A function that transforms ReaderResult[S] to ReaderResult[S]
//
// Example:
//
@@ -268,10 +265,8 @@ func BindL[S, T any](
func LetL[S, T any](
lens L.Lens[S, T],
f func(T) T,
) Kleisli[ReaderEither[S], S] {
return Let[S, S, T](lens.Set, func(s S) T {
return f(lens.Get(s))
})
) Kleisli[ReaderResult[S], S] {
return Let(lens.Set, F.Flow2(lens.Get, f))
}
// 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
//
// Returns:
// - A function that transforms ReaderEither[S] to ReaderEither[S]
// - A function that transforms ReaderResult[S] to ReaderResult[S]
//
// Example:
//
@@ -304,6 +299,6 @@ func LetL[S, T any](
func LetToL[S, T any](
lens L.Lens[S, T],
b T,
) Kleisli[ReaderEither[S], S] {
return LetTo[S, S, T](lens.Set, b)
) Kleisli[ReaderResult[S], S] {
return LetTo(lens.Set, b)
}

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
import (
"context"
@@ -25,11 +25,11 @@ import (
"github.com/stretchr/testify/assert"
)
func getLastName(s utils.Initial) ReaderEither[string] {
func getLastName(s utils.Initial) ReaderResult[string] {
return Of("Doe")
}
func getGivenName(s utils.WithLastName) ReaderEither[string] {
func getGivenName(s utils.WithLastName) ReaderResult[string] {
return Of("John")
}

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
import (
"context"
@@ -21,8 +21,8 @@ import (
E "github.com/IBM/fp-go/v2/either"
)
// withContext wraps an existing ReaderEither and performs a context check for cancellation before deletating
func WithContext[A any](ma ReaderEither[A]) ReaderEither[A] {
// withContext wraps an existing ReaderResult and performs a context check for cancellation before deletating
func WithContext[A any](ma ReaderResult[A]) ReaderResult[A] {
return func(ctx context.Context) E.Either[error, A] {
if err := context.Cause(ctx); err != nil {
return E.Left[A](err)

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
import (
"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
// 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)
}

View File

@@ -18,11 +18,10 @@ package exec
import (
"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/function"
INTE "github.com/IBM/fp-go/v2/internal/exec"
"github.com/IBM/fp-go/v2/result"
)
var (
@@ -32,8 +31,8 @@ var (
Command = function.Curry3(command)
)
func command(name string, args []string, in []byte) readereither.ReaderEither[exec.CommandOutput] {
return func(ctx context.Context) either.Either[error, exec.CommandOutput] {
return either.TryCatchError(INTE.Exec(ctx, name, args, in))
func command(name string, args []string, in []byte) ReaderResult[exec.CommandOutput] {
return func(ctx context.Context) Result[exec.CommandOutput] {
return result.TryCatchError(INTE.Exec(ctx, name, args, in))
}
}

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

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
import (
"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
// 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)
}
@@ -32,10 +32,10 @@ func From1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A] {
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)
}
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)
}

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
import (
"context"
@@ -21,19 +21,19 @@ import (
"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)
}
func Left[A any](l error) ReaderEither[A] {
func Left[A any](l error) ReaderResult[A] {
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)
}
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)
}
@@ -41,7 +41,7 @@ func Map[A, B any](f func(A) B) Operator[A, B] {
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)
}
@@ -49,15 +49,15 @@ func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
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)
}
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)
}
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)
}
@@ -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)
}
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)
}
func Ask() ReaderEither[context.Context] {
func Ask() ReaderResult[context.Context] {
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)
}
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)
}
@@ -85,7 +85,7 @@ func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operato
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)
}

View File

@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package readereither
package readerresult
import (
"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
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)
}
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)
}
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)
}
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)
}

View File

@@ -13,8 +13,8 @@
// See the License for the specific language governing permissions and
// 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 readereither
// 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 readerresult
import (
"context"
@@ -28,9 +28,9 @@ import (
type (
Option[A any] = option.Option[A]
Either[A any] = either.Either[error, A]
// ReaderEither is a specialization of the Reader monad for the typical golang scenario
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]]
Operator[A, B any] = Kleisli[ReaderEither[A], B]
Kleisli[A, B any] = reader.Reader[A, ReaderResult[B]]
Operator[A, B any] = Kleisli[ReaderResult[A], B]
)

View File

@@ -58,8 +58,8 @@ func Do[E, S any](
//go:inline
func Bind[E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) Either[E, T],
) func(Either[E, S1]) Either[E, S2] {
f Kleisli[E, S1, T],
) Operator[E, S1, S2] {
return C.Bind(
Chain[E, S1, S2],
Map[E, T, S2],
@@ -88,7 +88,7 @@ func Bind[E, S1, S2, T any](
func Let[E, S1, S2, T any](
key func(T) func(S1) S2,
f func(S1) T,
) func(Either[E, S1]) Either[E, S2] {
) Operator[E, S1, S2] {
return F.Let(
Map[E, S1, S2],
key,
@@ -115,7 +115,7 @@ func Let[E, S1, S2, T any](
func LetTo[E, S1, S2, T any](
key func(T) func(S1) S2,
b T,
) func(Either[E, S1]) Either[E, S2] {
) Operator[E, S1, S2] {
return F.LetTo(
Map[E, S1, S2],
key,
@@ -137,7 +137,7 @@ func LetTo[E, S1, S2, T any](
//go:inline
func BindTo[E, S1, T any](
setter func(T) S1,
) func(Either[E, T]) Either[E, S1] {
) Operator[E, T, S1] {
return C.BindTo(
Map[E, T, S1],
setter,
@@ -164,7 +164,7 @@ func BindTo[E, S1, T any](
func ApS[E, S1, S2, T any](
setter func(T) func(S1) S2,
fa Either[E, T],
) func(Either[E, S1]) Either[E, S2] {
) Operator[E, S1, S2] {
return A.ApS(
Ap[S2, E, T],
Map[E, S1, func(T) S2],
@@ -271,7 +271,7 @@ func ApSL[E, S, T any](
//go:inline
func BindL[E, S, T any](
lens Lens[S, T],
f func(T) Either[E, T],
f Kleisli[E, T, T],
) Endomorphism[Either[E, S]] {
return Bind[E, S, S, T](lens.Set, function.Flow2(lens.Get, f))
}

View File

@@ -20,47 +20,36 @@ import (
)
type (
either struct {
isLeft bool
value any
}
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
Either[E, A any] either
Either[E, A any] struct {
r A
l E
isL bool
}
)
// String prints some debug info for the object
//
//go:noinline
func eitherString(s *either) string {
if s.isLeft {
return fmt.Sprintf("Left[%T](%v)", s.value, s.value)
func (s Either[E, A]) String() string {
if !s.isL {
return fmt.Sprintf("Right[%T](%v)", s.r, s.r)
}
return fmt.Sprintf("Right[%T](%v)", s.value, s.value)
return fmt.Sprintf("Left[%T](%v)", s.l, s.l)
}
// Format prints some debug info for the object
//
//go:noinline
func eitherFormat(e *either, f fmt.State, c rune) {
func (s Either[E, A]) Format(f fmt.State, c rune) {
switch c {
case 's':
fmt.Fprint(f, eitherString(e))
fmt.Fprint(f, s.String())
default:
fmt.Fprint(f, eitherString(e))
fmt.Fprint(f, s.String())
}
}
// String prints some debug info for the object
func (s Either[E, A]) String() string {
return eitherString((*either)(&s))
}
// Format prints some debug info for the object
func (s Either[E, A]) Format(f fmt.State, c rune) {
eitherFormat((*either)(&s), f, c)
}
// IsLeft tests if the Either is a Left value.
// Rather use [Fold] or [MonadFold] if you need to access the values.
// Inverse is [IsRight].
@@ -72,7 +61,7 @@ func (s Either[E, A]) Format(f fmt.State, c rune) {
//
//go:inline
func IsLeft[E, A any](val Either[E, A]) bool {
return val.isLeft
return val.isL
}
// IsRight tests if the Either is a Right value.
@@ -86,7 +75,7 @@ func IsLeft[E, A any](val Either[E, A]) bool {
//
//go:inline
func IsRight[E, A any](val Either[E, A]) bool {
return !val.isLeft
return !val.isL
}
// Left creates a new Either representing a Left (error/failure) value.
@@ -98,7 +87,7 @@ func IsRight[E, A any](val Either[E, A]) bool {
//
//go:inline
func Left[A, E any](value E) Either[E, A] {
return Either[E, A]{true, value}
return Either[E, A]{l: value, isL: true}
}
// Right creates a new Either representing a Right (success) value.
@@ -110,7 +99,7 @@ func Left[A, E any](value E) Either[E, A] {
//
//go:inline
func Right[E, A any](value A) Either[E, A] {
return Either[E, A]{false, value}
return Either[E, A]{r: value}
}
// MonadFold extracts the value from an Either by providing handlers for both cases.
@@ -126,10 +115,10 @@ func Right[E, A any](value A) Either[E, A] {
//
//go:inline
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
if ma.isLeft {
return onLeft(ma.value.(E))
if !ma.isL {
return onRight(ma.r)
}
return onRight(ma.value.(A))
return onLeft(ma.l)
}
// Unwrap converts an Either into the idiomatic Go tuple (value, error).
@@ -143,11 +132,5 @@ func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a
//
//go:inline
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
if ma.isLeft {
var a A
return a, ma.value.(E)
} else {
var e E
return ma.value.(A), e
}
return ma.r, ma.l
}

154
v2/either/core_any.go Normal file
View File

@@ -0,0 +1,154 @@
// 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.
//go:build either_any
package either
import (
"fmt"
)
type (
either struct {
value any
isRight bool
}
// Either defines a data structure that logically holds either an E or an A. The flag discriminates the cases
Either[E, A any] either
)
// String prints some debug info for the object
//
//go:noinline
func eitherString(s *either) string {
if s.isRight {
return fmt.Sprintf("Right[%T](%v)", s.value, s.value)
}
return fmt.Sprintf("Left[%T](%v)", s.value, s.value)
}
// Format prints some debug info for the object
//
//go:noinline
func eitherFormat(e *either, f fmt.State, c rune) {
switch c {
case 's':
fmt.Fprint(f, eitherString(e))
default:
fmt.Fprint(f, eitherString(e))
}
}
// String prints some debug info for the object
func (s Either[E, A]) String() string {
return eitherString((*either)(&s))
}
// Format prints some debug info for the object
func (s Either[E, A]) Format(f fmt.State, c rune) {
eitherFormat((*either)(&s), f, c)
}
// IsLeft tests if the Either is a Left value.
// Rather use [Fold] or [MonadFold] if you need to access the values.
// Inverse is [IsRight].
//
// Example:
//
// either.IsLeft(either.Left[int](errors.New("err"))) // true
// either.IsLeft(either.Right[error](42)) // false
//
//go:inline
func IsLeft[E, A any](val Either[E, A]) bool {
return !val.isRight
}
// IsRight tests if the Either is a Right value.
// Rather use [Fold] or [MonadFold] if you need to access the values.
// Inverse is [IsLeft].
//
// Example:
//
// either.IsRight(either.Right[error](42)) // true
// either.IsRight(either.Left[int](errors.New("err"))) // false
//
//go:inline
func IsRight[E, A any](val Either[E, A]) bool {
return val.isRight
}
// Left creates a new Either representing a Left (error/failure) value.
// By convention, Left represents the error case.
//
// Example:
//
// result := either.Left[int](errors.New("something went wrong"))
//
//go:inline
func Left[A, E any](value E) Either[E, A] {
return Either[E, A]{value, false}
}
// Right creates a new Either representing a Right (success) value.
// By convention, Right represents the success case.
//
// Example:
//
// result := either.Right[error](42)
//
//go:inline
func Right[E, A any](value A) Either[E, A] {
return Either[E, A]{value, true}
}
// MonadFold extracts the value from an Either by providing handlers for both cases.
// This is the fundamental pattern matching operation for Either.
//
// Example:
//
// result := either.MonadFold(
// either.Right[error](42),
// func(err error) string { return "Error: " + err.Error() },
// func(n int) string { return fmt.Sprintf("Value: %d", n) },
// ) // "Value: 42"
//
//go:inline
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(e E) B, onRight func(a A) B) B {
if ma.isRight {
return onRight(ma.value.(A))
}
return onLeft(ma.value.(E))
}
// Unwrap converts an Either into the idiomatic Go tuple (value, error).
// For Right values, returns (value, zero-error).
// For Left values, returns (zero-value, error).
//
// Example:
//
// val, err := either.Unwrap(either.Right[error](42)) // 42, nil
// val, err := either.Unwrap(either.Left[int](errors.New("fail"))) // 0, error
//
//go:inline
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
if ma.isRight {
var e E
return ma.value.(A), e
} else {
var a A
return a, ma.value.(E)
}
}

View File

@@ -0,0 +1,94 @@
// 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.
//go:build either_pointers
package either
import "fmt"
type Either[E, A any] struct {
left *E
right *A
}
// String prints some debug info for the object
//
//go:noinline
func eitherString[E, A any](s *Either[E, A]) string {
if s.right != nil {
return fmt.Sprintf("Right[%T](%v)", *s.right, *s.right)
}
return fmt.Sprintf("Left[%T](%v)", *s.left, *s.left)
}
// Format prints some debug info for the object
//
//go:noinline
func eitherFormat[E, A any](e *Either[E, A], f fmt.State, c rune) {
switch c {
case 's':
fmt.Fprint(f, eitherString(e))
default:
fmt.Fprint(f, eitherString(e))
}
}
// String prints some debug info for the object
func (s Either[E, A]) String() string {
return eitherString(&s)
}
// Format prints some debug info for the object
func (s Either[E, A]) Format(f fmt.State, c rune) {
eitherFormat(&s, f, c)
}
//go:inline
func Left[A, E any](value E) Either[E, A] {
return Either[E, A]{left: &value}
}
//go:inline
func Right[E, A any](value A) Either[E, A] {
return Either[E, A]{right: &value}
}
//go:inline
func IsLeft[E, A any](e Either[E, A]) bool {
return e.left != nil
}
//go:inline
func IsRight[E, A any](e Either[E, A]) bool {
return e.right != nil
}
//go:inline
func MonadFold[E, A, B any](ma Either[E, A], onLeft func(E) B, onRight func(A) B) B {
if ma.right != nil {
return onRight(*ma.right)
}
return onLeft(*ma.left)
}
//go:inline
func Unwrap[E, A any](ma Either[E, A]) (A, E) {
if ma.right != nil {
var e E
return *ma.right, e
}
var a A
return a, *ma.left
}

View File

@@ -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(
// either.Left[int](errors.New("error")),
// func(e error) string { return e.Error() },
// error.Error,
// func(n int) string { return fmt.Sprint(n) },
// ) // 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] {
@@ -131,7 +131,7 @@ func MapTo[E, A, B any](b B) Operator[E, A, B] {
//
// result := either.MonadMapLeft(
// either.Left[int](errors.New("error")),
// func(e error) string { return e.Error() },
// error.Error,
// ) // Left("error")
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])
@@ -449,7 +449,7 @@ func Alt[E, A any](that L.Lazy[Either[E, A]]) Operator[E, A, A] {
// return either.Right[error](0) // default value
// })
// result := recover(either.Left[int](errors.New("fail"))) // Right(0)
func OrElse[E, A any](onLeft func(e E) Either[E, A]) Operator[E, A, A] {
func OrElse[E, A any](onLeft Kleisli[E, E, A]) Operator[E, A, A] {
return Fold(onLeft, Of[E, A])
}

View File

@@ -0,0 +1,652 @@
// 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 either
import (
"errors"
"testing"
F "github.com/IBM/fp-go/v2/function"
)
var (
errBench = errors.New("benchmark error")
benchResult Either[error, int]
benchBool bool
benchInt int
benchString string
)
// Benchmark core constructors
func BenchmarkLeft(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = Left[int](errBench)
}
}
func BenchmarkRight(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = Right[error](42)
}
}
func BenchmarkOf(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = Of[error](42)
}
}
// Benchmark predicates
func BenchmarkIsLeft(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchBool = IsLeft(left)
}
}
func BenchmarkIsRight(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchBool = IsRight(right)
}
}
// Benchmark fold operations
func BenchmarkMonadFold_Right(b *testing.B) {
right := Right[error](42)
onLeft := func(e error) int { return 0 }
onRight := func(a int) int { return a * 2 }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt = MonadFold(right, onLeft, onRight)
}
}
func BenchmarkMonadFold_Left(b *testing.B) {
left := Left[int](errBench)
onLeft := func(e error) int { return 0 }
onRight := func(a int) int { return a * 2 }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt = MonadFold(left, onLeft, onRight)
}
}
func BenchmarkFold_Right(b *testing.B) {
right := Right[error](42)
folder := Fold(
func(e error) int { return 0 },
func(a int) int { return a * 2 },
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt = folder(right)
}
}
func BenchmarkFold_Left(b *testing.B) {
left := Left[int](errBench)
folder := Fold(
func(e error) int { return 0 },
func(a int) int { return a * 2 },
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt = folder(left)
}
}
// Benchmark unwrap operations
func BenchmarkUnwrap_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt, _ = Unwrap(right)
}
}
func BenchmarkUnwrap_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt, _ = Unwrap(left)
}
}
func BenchmarkUnwrapError_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt, _ = UnwrapError(right)
}
}
func BenchmarkUnwrapError_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt, _ = UnwrapError(left)
}
}
// Benchmark functor operations
func BenchmarkMonadMap_Right(b *testing.B) {
right := Right[error](42)
mapper := func(a int) int { return a * 2 }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadMap(right, mapper)
}
}
func BenchmarkMonadMap_Left(b *testing.B) {
left := Left[int](errBench)
mapper := func(a int) int { return a * 2 }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadMap(left, mapper)
}
}
func BenchmarkMap_Right(b *testing.B) {
right := Right[error](42)
mapper := Map[error](func(a int) int { return a * 2 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = mapper(right)
}
}
func BenchmarkMap_Left(b *testing.B) {
left := Left[int](errBench)
mapper := Map[error](func(a int) int { return a * 2 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = mapper(left)
}
}
func BenchmarkMapLeft_Right(b *testing.B) {
right := Right[error](42)
mapper := MapLeft[int](error.Error)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = mapper(right)
}
}
func BenchmarkMapLeft_Left(b *testing.B) {
left := Left[int](errBench)
mapper := MapLeft[int](error.Error)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = mapper(left)
}
}
func BenchmarkBiMap_Right(b *testing.B) {
right := Right[error](42)
mapper := BiMap(
error.Error,
func(a int) string { return "value" },
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = mapper(right)
}
}
func BenchmarkBiMap_Left(b *testing.B) {
left := Left[int](errBench)
mapper := BiMap(
error.Error,
func(a int) string { return "value" },
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = mapper(left)
}
}
// Benchmark monad operations
func BenchmarkMonadChain_Right(b *testing.B) {
right := Right[error](42)
chainer := func(a int) Either[error, int] { return Right[error](a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadChain(right, chainer)
}
}
func BenchmarkMonadChain_Left(b *testing.B) {
left := Left[int](errBench)
chainer := func(a int) Either[error, int] { return Right[error](a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadChain(left, chainer)
}
}
func BenchmarkChain_Right(b *testing.B) {
right := Right[error](42)
chainer := Chain[error](func(a int) Either[error, int] { return Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = chainer(right)
}
}
func BenchmarkChain_Left(b *testing.B) {
left := Left[int](errBench)
chainer := Chain[error](func(a int) Either[error, int] { return Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = chainer(left)
}
}
func BenchmarkChainFirst_Right(b *testing.B) {
right := Right[error](42)
chainer := ChainFirst(func(a int) Either[error, string] { return Right[error]("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = chainer(right)
}
}
func BenchmarkChainFirst_Left(b *testing.B) {
left := Left[int](errBench)
chainer := ChainFirst[error](func(a int) Either[error, string] { return Right[error]("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = chainer(left)
}
}
func BenchmarkFlatten_Right(b *testing.B) {
nested := Right[error](Right[error](42))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = Flatten(nested)
}
}
func BenchmarkFlatten_Left(b *testing.B) {
nested := Left[Either[error, int]](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = Flatten(nested)
}
}
// Benchmark applicative operations
func BenchmarkMonadAp_RightRight(b *testing.B) {
fab := Right[error](func(a int) int { return a * 2 })
fa := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadAp(fab, fa)
}
}
func BenchmarkMonadAp_RightLeft(b *testing.B) {
fab := Right[error](func(a int) int { return a * 2 })
fa := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadAp(fab, fa)
}
}
func BenchmarkMonadAp_LeftRight(b *testing.B) {
fab := Left[func(int) int](errBench)
fa := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadAp(fab, fa)
}
}
func BenchmarkAp_RightRight(b *testing.B) {
fab := Right[error](func(a int) int { return a * 2 })
fa := Right[error](42)
ap := Ap[int, error, int](fa)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = ap(fab)
}
}
// Benchmark alternative operations
func BenchmarkAlt_RightRight(b *testing.B) {
right := Right[error](42)
alternative := Alt(func() Either[error, int] { return Right[error](99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = alternative(right)
}
}
func BenchmarkAlt_LeftRight(b *testing.B) {
left := Left[int](errBench)
alternative := Alt[error](func() Either[error, int] { return Right[error](99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = alternative(left)
}
}
func BenchmarkOrElse_Right(b *testing.B) {
right := Right[error](42)
recover := OrElse[error](func(e error) Either[error, int] { return Right[error](0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = recover(right)
}
}
func BenchmarkOrElse_Left(b *testing.B) {
left := Left[int](errBench)
recover := OrElse(func(e error) Either[error, int] { return Right[error](0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = recover(left)
}
}
// Benchmark conversion operations
func BenchmarkTryCatch_Success(b *testing.B) {
onThrow := func(err error) error { return err }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = TryCatch(42, nil, onThrow)
}
}
func BenchmarkTryCatch_Error(b *testing.B) {
onThrow := func(err error) error { return err }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = TryCatch(0, errBench, onThrow)
}
}
func BenchmarkTryCatchError_Success(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = TryCatchError(42, nil)
}
}
func BenchmarkTryCatchError_Error(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = TryCatchError(0, errBench)
}
}
func BenchmarkSwap_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = Swap(right)
}
}
func BenchmarkSwap_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = Swap(left)
}
}
func BenchmarkGetOrElse_Right(b *testing.B) {
right := Right[error](42)
getter := GetOrElse[error](func(e error) int { return 0 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt = getter(right)
}
}
func BenchmarkGetOrElse_Left(b *testing.B) {
left := Left[int](errBench)
getter := GetOrElse[error](func(e error) int { return 0 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchInt = getter(left)
}
}
// Benchmark pipeline operations
func BenchmarkPipeline_Map_Right(b *testing.B) {
right := Right[error](21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = F.Pipe1(
right,
Map[error](func(x int) int { return x * 2 }),
)
}
}
func BenchmarkPipeline_Map_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = F.Pipe1(
left,
Map[error](func(x int) int { return x * 2 }),
)
}
}
func BenchmarkPipeline_Chain_Right(b *testing.B) {
right := Right[error](21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = F.Pipe1(
right,
Chain[error](func(x int) Either[error, int] { return Right[error](x * 2) }),
)
}
}
func BenchmarkPipeline_Chain_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = F.Pipe1(
left,
Chain[error](func(x int) Either[error, int] { return Right[error](x * 2) }),
)
}
}
func BenchmarkPipeline_Complex_Right(b *testing.B) {
right := Right[error](10)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = F.Pipe3(
right,
Map[error](func(x int) int { return x * 2 }),
Chain[error](func(x int) Either[error, int] { return Right[error](x + 1) }),
Map[error](func(x int) int { return x * 2 }),
)
}
}
func BenchmarkPipeline_Complex_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = F.Pipe3(
left,
Map[error](func(x int) int { return x * 2 }),
Chain[error](func(x int) Either[error, int] { return Right[error](x + 1) }),
Map[error](func(x int) int { return x * 2 }),
)
}
}
// Benchmark sequence operations
func BenchmarkMonadSequence2_RightRight(b *testing.B) {
e1 := Right[error](10)
e2 := Right[error](20)
f := func(a, b int) Either[error, int] { return Right[error](a + b) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadSequence2(e1, e2, f)
}
}
func BenchmarkMonadSequence2_LeftRight(b *testing.B) {
e1 := Left[int](errBench)
e2 := Right[error](20)
f := func(a, b int) Either[error, int] { return Right[error](a + b) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadSequence2(e1, e2, f)
}
}
func BenchmarkMonadSequence3_RightRightRight(b *testing.B) {
e1 := Right[error](10)
e2 := Right[error](20)
e3 := Right[error](30)
f := func(a, b, c int) Either[error, int] { return Right[error](a + b + c) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchResult = MonadSequence3(e1, e2, e3, f)
}
}
// Benchmark do-notation operations
func BenchmarkDo(b *testing.B) {
type State struct{ value int }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = Do[error](State{})
}
}
func BenchmarkBind_Right(b *testing.B) {
type State struct{ value int }
initial := Do[error](State{})
binder := Bind[error, State, State](
func(v int) func(State) State {
return func(s State) State { return State{value: v} }
},
func(s State) Either[error, int] {
return Right[error](42)
},
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = binder(initial)
}
}
func BenchmarkLet_Right(b *testing.B) {
type State struct{ value int }
initial := Right[error](State{value: 10})
letter := Let[error, State, State](
func(v int) func(State) State {
return func(s State) State { return State{value: s.value + v} }
},
func(s State) int { return 32 },
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = letter(initial)
}
}
// Benchmark string formatting
func BenchmarkString_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchString = right.String()
}
}
func BenchmarkString_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
benchString = left.String()
}
}
// Made with Bob

View File

@@ -29,8 +29,8 @@ import (
// Test BiMap
func TestBiMap(t *testing.T) {
errToStr := func(e error) string { return e.Error() }
intToStr := func(i int) string { return strconv.Itoa(i) }
errToStr := error.Error
intToStr := strconv.Itoa
// Test Right case
result := BiMap(errToStr, intToStr)(Right[error](42))
@@ -43,8 +43,8 @@ func TestBiMap(t *testing.T) {
// Test MonadBiMap
func TestMonadBiMap(t *testing.T) {
errToStr := func(e error) string { return e.Error() }
intToStr := func(i int) string { return strconv.Itoa(i) }
errToStr := error.Error
intToStr := strconv.Itoa
result := MonadBiMap(Right[error](42), errToStr, intToStr)
assert.Equal(t, Right[string]("42"), result)
@@ -55,7 +55,7 @@ func TestMonadBiMap(t *testing.T) {
// Test MapLeft
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")))
assert.Equal(t, Left[int]("error"), result)
@@ -66,7 +66,7 @@ func TestMapLeft(t *testing.T) {
// Test MonadMapLeft
func TestMonadMapLeft(t *testing.T) {
errToStr := func(e error) string { return e.Error() }
errToStr := error.Error
result := MonadMapLeft(Left[int](errors.New("error")), errToStr)
assert.Equal(t, Left[int]("error"), result)

View File

@@ -117,7 +117,7 @@ func TestStringer(t *testing.T) {
assert.Equal(t, exp, e.String())
var s fmt.Stringer = e
var s fmt.Stringer = &e
assert.Equal(t, exp, s.String())
}

View File

@@ -59,7 +59,7 @@ func TestUneitherize1(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "positive", result)
result, err = uneitherized(-1)
_, err = uneitherized(-1)
assert.Error(t, err)
}

View File

@@ -43,7 +43,7 @@ import (
// // Use file here
// return either.Right[error]("data")
// })
func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) Either[E, any]) func(func(R) Either[E, A]) Either[E, A] {
func WithResource[E, R, A, ANY any](onCreate func() Either[E, R], onRelease Kleisli[E, R, ANY]) func(func(R) Either[E, A]) Either[E, A] {
return func(f func(R) Either[E, A]) Either[E, A] {
return MonadChain(
@@ -58,7 +58,7 @@ func WithResource[E, R, A any](onCreate func() Either[E, R], onRelease func(R) E
func(a A) Either[E, A] {
return F.Pipe1(
released,
MapTo[E, any](a),
MapTo[E, ANY](a),
)
})
},

View File

@@ -35,7 +35,7 @@ import (
// )(f)(eitherOfOption)
func Traverse[A, E, B, HKTB, HKTRB any](
mof func(Either[E, B]) HKTRB,
mmap func(func(B) Either[E, B]) func(HKTB) HKTRB,
mmap func(Kleisli[E, B, B]) func(HKTB) HKTRB,
) func(func(A) HKTB) func(Either[E, A]) HKTRB {
left := F.Flow2(Left[B, E], mof)
@@ -62,7 +62,7 @@ func Traverse[A, E, B, HKTB, HKTRB any](
// )(eitherOfOption)
func Sequence[E, A, HKTA, HKTRA any](
mof func(Either[E, A]) HKTRA,
mmap func(func(A) Either[E, A]) func(HKTA) HKTRA,
mmap func(Kleisli[E, A, A]) func(HKTA) HKTRA,
) func(Either[E, HKTA]) HKTRA {
return Fold(F.Flow2(Left[A, E], mof), mmap(Right[E, A]))
}

View File

@@ -34,7 +34,7 @@
// // result is Either[error, int]
//
// // Function erasure
// typedFunc := func(x int) string { return fmt.Sprintf("%d", x) }
// typedFunc := strconv.Itoa
// erasedFunc := erasure.Erase1(typedFunc)
// result := erasedFunc(erasure.Erase(42)) // returns erased "42"
package erasure
@@ -112,7 +112,7 @@ func Erase0[T1 any](f func() T1) func() any {
//
// Example:
//
// typedFunc := func(x int) string { return fmt.Sprintf("%d", x) }
// typedFunc := strconv.Itoa
// erasedFunc := Erase1(typedFunc)
// result := erasedFunc(Erase(42)) // returns erased "42"
func Erase1[T1, T2 any](f func(T1) T2) func(any) any {

View File

@@ -17,6 +17,7 @@ package erasure
import (
"fmt"
"strconv"
"strings"
"testing"
@@ -166,7 +167,7 @@ func TestErase0(t *testing.T) {
func TestErase1(t *testing.T) {
t.Run("erases unary function int to string", func(t *testing.T) {
typedFunc := func(x int) string { return fmt.Sprintf("%d", x) }
typedFunc := strconv.Itoa
erasedFunc := Erase1(typedFunc)
result := erasedFunc(Erase(42))
assert.NotNil(t, result)

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
package io
import (
F "github.com/IBM/fp-go/v2/function"
INTA "github.com/IBM/fp-go/v2/internal/apply"
INTC "github.com/IBM/fp-go/v2/internal/chain"
INTF "github.com/IBM/fp-go/v2/internal/functor"
@@ -216,9 +217,7 @@ func BindL[S, T any](
lens L.Lens[S, T],
f Kleisli[T, T],
) Operator[S, S] {
return Bind[S, S, T](lens.Set, func(s S) IO[T] {
return f(lens.Get(s))
})
return Bind[S, S, T](lens.Set, F.Flow2(lens.Get, f))
}
// LetL attaches the result of a pure computation to a context using a lens-based setter.
@@ -251,9 +250,7 @@ func LetL[S, T any](
lens L.Lens[S, T],
f func(T) T,
) Operator[S, S] {
return Let[S, S, T](lens.Set, func(s S) T {
return f(lens.Get(s))
})
return Let[S, S, T](lens.Set, F.Flow2(lens.Get, f))
}
// LetToL attaches a constant value to a context using a lens-based setter.

View File

@@ -16,6 +16,7 @@
package ioeither
import (
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/apply"
"github.com/IBM/fp-go/v2/internal/chain"
"github.com/IBM/fp-go/v2/internal/functor"
@@ -77,7 +78,7 @@ func Do[E, S any](
// )
func Bind[E, S1, S2, T any](
setter func(T) func(S1) S2,
f func(S1) IOEither[E, T],
f Kleisli[E, S1, T],
) Operator[E, S1, S2] {
return chain.Bind(
Chain[E, S1, S2],
@@ -230,11 +231,9 @@ func ApSL[E, S, T any](
// )
func BindL[E, S, T any](
lens L.Lens[S, T],
f func(T) IOEither[E, T],
f Kleisli[E, T, T],
) Operator[E, S, S] {
return Bind[E, S, S, T](lens.Set, func(s S) IOEither[E, T] {
return f(lens.Get(s))
})
return Bind[E, S, S, T](lens.Set, F.Flow2(lens.Get, f))
}
// LetL attaches the result of a pure computation to a context using a lens-based setter.
@@ -266,9 +265,7 @@ func LetL[E, S, T any](
lens L.Lens[S, T],
f func(T) T,
) Operator[E, S, S] {
return Let[E, S, S, T](lens.Set, func(s S) T {
return f(lens.Get(s))
})
return Let[E, S, S, T](lens.Set, F.Flow2(lens.Get, f))
}
// LetToL attaches a constant value to a context using a lens-based setter.

View File

@@ -24,7 +24,7 @@ import (
// whether the body action returns and error or not.
func Bracket[E, A, B, ANY any](
acquire IOEither[E, A],
use func(A) IOEither[E, B],
use Kleisli[E, A, B],
release func(A, Either[E, B]) IOEither[E, ANY],
) IOEither[E, B] {
return BR.Bracket[IOEither[E, A], IOEither[E, B], IOEither[E, ANY], Either[E, B], A, B](

View File

@@ -22,14 +22,14 @@ import (
)
// MkdirAll create a sequence of directories, see [os.MkdirAll]
func MkdirAll(path string, perm os.FileMode) ioeither.IOEither[error, string] {
func MkdirAll(path string, perm os.FileMode) IOEither[error, string] {
return ioeither.TryCatchError(func() (string, error) {
return path, os.MkdirAll(path, perm)
})
}
// Mkdir create a directory, see [os.Mkdir]
func Mkdir(path string, perm os.FileMode) ioeither.IOEither[error, string] {
func Mkdir(path string, perm os.FileMode) IOEither[error, string] {
return ioeither.TryCatchError(func() (string, error) {
return path, os.Mkdir(path, perm)
})

View File

@@ -32,8 +32,8 @@ var (
)
// WriteFile writes a data blob to a file
func WriteFile(dstName string, perm os.FileMode) func([]byte) ioeither.IOEither[error, []byte] {
return func(data []byte) ioeither.IOEither[error, []byte] {
func WriteFile(dstName string, perm os.FileMode) Kleisli[error, []byte, []byte] {
return func(data []byte) IOEither[error, []byte] {
return ioeither.TryCatchError(func() ([]byte, error) {
return data, os.WriteFile(dstName, data, perm)
})
@@ -41,14 +41,14 @@ func WriteFile(dstName string, perm os.FileMode) func([]byte) ioeither.IOEither[
}
// Remove removes a file by name
func Remove(name string) ioeither.IOEither[error, string] {
func Remove(name string) IOEither[error, string] {
return ioeither.TryCatchError(func() (string, error) {
return name, os.Remove(name)
})
}
// Close closes an object
func Close[C io.Closer](c C) ioeither.IOEither[error, any] {
func Close[C io.Closer](c C) IOEither[error, any] {
return ioeither.TryCatchError(func() (any, error) {
return c, c.Close()
})

View File

@@ -29,7 +29,7 @@ var (
)
// ReadAll uses a generator function to create a stream, reads it and closes it
func ReadAll[R io.ReadCloser](acquire ioeither.IOEither[error, R]) ioeither.IOEither[error, []byte] {
func ReadAll[R io.ReadCloser](acquire IOEither[error, R]) IOEither[error, []byte] {
return F.Pipe1(
F.Flow2(
FL.ToReader[R],

View File

@@ -39,6 +39,6 @@ var (
)
// WithTempFile creates a temporary file, then invokes a callback to create a resource based on the file, then close and remove the temp file
func WithTempFile[A any](f func(*os.File) ioeither.IOEither[error, A]) ioeither.IOEither[error, A] {
func WithTempFile[A any](f Kleisli[error, *os.File, A]) IOEither[error, A] {
return ioeither.WithResource[A](onCreateTempFile, onReleaseTempFile)(f)
}

View File

@@ -34,7 +34,7 @@ func TestWithTempFile(t *testing.T) {
func TestWithTempFileOnClosedFile(t *testing.T) {
res := WithTempFile(func(f *os.File) ioeither.IOEither[error, []byte] {
res := WithTempFile(func(f *os.File) IOEither[error, []byte] {
return F.Pipe2(
f,
onWriteAll[*os.File]([]byte("Carsten")),

11
v2/ioeither/file/types.go Normal file
View File

@@ -0,0 +1,11 @@
package file
import (
"github.com/IBM/fp-go/v2/ioeither"
)
type (
IOEither[E, T any] = ioeither.IOEither[E, T]
Kleisli[E, A, B any] = ioeither.Kleisli[E, A, B]
Operator[E, A, B any] = ioeither.Operator[E, A, B]
)

View File

@@ -21,8 +21,8 @@ import (
"github.com/IBM/fp-go/v2/ioeither"
)
func onWriteAll[W io.Writer](data []byte) func(w W) ioeither.IOEither[error, []byte] {
return func(w W) ioeither.IOEither[error, []byte] {
func onWriteAll[W io.Writer](data []byte) Kleisli[error, W, []byte] {
return func(w W) IOEither[error, []byte] {
return ioeither.TryCatchError(func() ([]byte, error) {
_, err := w.Write(data)
return data, err
@@ -31,9 +31,9 @@ func onWriteAll[W io.Writer](data []byte) func(w W) ioeither.IOEither[error, []b
}
// WriteAll uses a generator function to create a stream, writes data to it and closes it
func WriteAll[W io.WriteCloser](data []byte) func(acquire ioeither.IOEither[error, W]) ioeither.IOEither[error, []byte] {
func WriteAll[W io.WriteCloser](data []byte) Operator[error, W, []byte] {
onWrite := onWriteAll[W](data)
return func(onCreate ioeither.IOEither[error, W]) ioeither.IOEither[error, []byte] {
return func(onCreate IOEither[error, W]) IOEither[error, []byte] {
return ioeither.WithResource[[]byte](
onCreate,
Close[W])(
@@ -43,7 +43,7 @@ func WriteAll[W io.WriteCloser](data []byte) func(acquire ioeither.IOEither[erro
}
// Write uses a generator function to create a stream, writes data to it and closes it
func Write[R any, W io.WriteCloser](acquire ioeither.IOEither[error, W]) func(use func(W) ioeither.IOEither[error, R]) ioeither.IOEither[error, R] {
func Write[R any, W io.WriteCloser](acquire IOEither[error, W]) Kleisli[error, Kleisli[error, W, R], R] {
return ioeither.WithResource[R](
acquire,
Close[W])

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,8 @@ type (
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
IOEither[E, A any] = IO[Either[E, A]]
Operator[E, A, B any] = R.Reader[IOEither[E, A], IOEither[E, B]]
Kleisli[E, A, B any] = R.Reader[A, IOEither[E, B]]
Operator[E, A, B any] = Kleisli[E, IOEither[E, A], B]
)
func Left[A, E any](l E) IOEither[E, A] {
@@ -83,7 +84,7 @@ func FromIOOption[A, E any](onNone func() E) func(o IOO.IOOption[A]) IOEither[E,
return io.Map(either.FromOption[A](onNone))
}
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) func(IOEither[E, A]) IOEither[E, B] {
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) O.Option[B]) Operator[E, A, B] {
return fromeither.ChainOptionK(
MonadChain[E, A, B],
FromEither[E, B],
@@ -100,7 +101,7 @@ func MonadChainIOK[E, A, B any](ma IOEither[E, A], f func(A) IO[B]) IOEither[E,
)
}
func ChainIOK[E, A, B any](f func(A) IO[B]) func(IOEither[E, A]) IOEither[E, B] {
func ChainIOK[E, A, B any](f func(A) IO[B]) Operator[E, A, B] {
return fromio.ChainIOK(
Chain[E, A, B],
FromIO[E, B],
@@ -108,7 +109,7 @@ func ChainIOK[E, A, B any](f func(A) IO[B]) func(IOEither[E, A]) IOEither[E, B]
)
}
func ChainLazyK[E, A, B any](f func(A) lazy.Lazy[B]) func(IOEither[E, A]) IOEither[E, B] {
func ChainLazyK[E, A, B any](f func(A) lazy.Lazy[B]) Operator[E, A, B] {
return ChainIOK[E](f)
}
@@ -138,11 +139,11 @@ func MapTo[E, A, B any](b B) Operator[E, A, B] {
return Map[E](function.Constant1[A](b))
}
func MonadChain[E, A, B any](fa IOEither[E, A], f func(A) IOEither[E, B]) IOEither[E, B] {
func MonadChain[E, A, B any](fa IOEither[E, A], f Kleisli[E, A, B]) IOEither[E, B] {
return eithert.MonadChain(io.MonadChain[Either[E, A], Either[E, B]], io.MonadOf[Either[E, B]], fa, f)
}
func Chain[E, A, B any](f func(A) IOEither[E, B]) Operator[E, A, B] {
func Chain[E, A, B any](f Kleisli[E, A, B]) Operator[E, A, B] {
return eithert.Chain(io.Chain[Either[E, A], Either[E, B]], io.Of[Either[E, B]], f)
}
@@ -155,7 +156,7 @@ func MonadChainEitherK[E, A, B any](ma IOEither[E, A], f func(A) Either[E, B]) I
)
}
func ChainEitherK[E, A, B any](f func(A) Either[E, B]) func(IOEither[E, A]) IOEither[E, B] {
func ChainEitherK[E, A, B any](f func(A) Either[E, B]) Operator[E, A, B] {
return fromeither.ChainEitherK(
Chain[E, A, B],
FromEither[E, B],
@@ -229,7 +230,7 @@ func Memoize[E, A any](ma IOEither[E, A]) IOEither[E, A] {
return io.Memoize(ma)
}
func MonadMapLeft[E1, E2, A any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2, A] {
func MonadMapLeft[A, E1, E2 any](fa IOEither[E1, A], f func(E1) E2) IOEither[E2, A] {
return eithert.MonadMapLeft(
io.MonadMap[Either[E1, A], Either[E2, A]],
fa,
@@ -274,7 +275,7 @@ func ChainTo[A, E, B any](fb IOEither[E, B]) Operator[E, A, B] {
}
// MonadChainFirst runs the [IOEither] monad returned by the function but returns the result of the original monad
func MonadChainFirst[E, A, B any](ma IOEither[E, A], f func(A) IOEither[E, B]) IOEither[E, A] {
func MonadChainFirst[E, A, B any](ma IOEither[E, A], f Kleisli[E, A, B]) IOEither[E, A] {
return chain.MonadChainFirst(
MonadChain[E, A, A],
MonadMap[E, B, A],
@@ -284,7 +285,7 @@ func MonadChainFirst[E, A, B any](ma IOEither[E, A], f func(A) IOEither[E, B]) I
}
// ChainFirst runs the [IOEither] monad returned by the function but returns the result of the original monad
func ChainFirst[E, A, B any](f func(A) IOEither[E, B]) Operator[E, A, A] {
func ChainFirst[E, A, B any](f Kleisli[E, A, B]) Operator[E, A, A] {
return chain.ChainFirst(
Chain[E, A, A],
Map[E, B, A],
@@ -337,7 +338,7 @@ func MonadFold[E, A, B any](ma IOEither[E, A], onLeft func(E) IO[B], onRight fun
}
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
func WithResource[A, E, R, ANY any](onCreate IOEither[E, R], onRelease func(R) IOEither[E, ANY]) func(func(R) IOEither[E, A]) IOEither[E, A] {
func WithResource[A, E, R, ANY any](onCreate IOEither[E, R], onRelease Kleisli[E, R, ANY]) Kleisli[E, Kleisli[E, R, A], A] {
return file.WithResource(
MonadChain[E, R, A],
MonadFold[E, A, Either[E, A]],

View File

@@ -26,7 +26,7 @@ import (
// LogJSON converts the argument to pretty printed JSON and then logs it via the format string
// Can be used with [ChainFirst]
func LogJSON[A any](prefix string) func(A) IOEither[error, any] {
func LogJSON[A any](prefix string) Kleisli[error, A, any] {
return func(a A) IOEither[error, any] {
// log this
return function.Pipe3(

View File

@@ -41,7 +41,7 @@ func (o *ioEitherMonad[E, A, B]) Map(f func(A) B) Operator[E, A, B] {
return Map[E, A, B](f)
}
func (o *ioEitherMonad[E, A, B]) Chain(f func(A) IOEither[E, B]) Operator[E, A, B] {
func (o *ioEitherMonad[E, A, B]) Chain(f Kleisli[E, A, B]) Operator[E, A, B] {
return Chain[E, A, B](f)
}

View File

@@ -27,7 +27,7 @@ import (
// check - checks if the result of the action needs to be retried
func Retrying[E, A any](
policy R.RetryPolicy,
action func(R.RetryStatus) IOEither[E, A],
action Kleisli[E, R.RetryStatus, A],
check func(Either[E, A]) bool,
) IOEither[E, A] {
return io.Retrying(policy, action, check)

View File

@@ -24,7 +24,7 @@ type (
)
// AltSemigroup is a [Semigroup] that tries the first item and then the second one using an alternative
func AltSemigroup[E, A any]() semigroup.Semigroup[IOEither[E, A]] {
func AltSemigroup[E, A any]() Semigroup[E, A] {
return semigroup.AltSemigroup(
MonadAlt[E, A],
)

View File

@@ -22,6 +22,6 @@ import (
)
// WithLock executes the provided IO operation in the scope of a lock
func WithLock[E, A any](lock IO[context.CancelFunc]) func(fa IOEither[E, A]) IOEither[E, A] {
func WithLock[E, A any](lock IO[context.CancelFunc]) Operator[E, A, A] {
return io.WithLock[Either[E, A]](lock)
}

Some files were not shown because too many files have changed in this diff Show More