readerioeither

package
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 18, 2025 License: Apache-2.0 Imports: 32 Imported by: 0

Documentation

Overview

Package readerioeither provides a functional programming abstraction that combines three powerful concepts: Reader, IO, and Either monads.

Fantasy Land Specification

This is a monad transformer combining:

Implemented Fantasy Land algebras:

ReaderIOEither

ReaderIOEither[R, E, A] represents a computation that:

  • Depends on some context/environment of type R (Reader)
  • Performs side effects (IO)
  • Can fail with an error of type E or succeed with a value of type A (Either)

This is particularly useful for:

  • Dependency injection patterns
  • Error handling in effectful computations
  • Composing operations that need access to shared configuration or context

Core Operations

Construction:

  • Of/Right: Create a successful computation
  • Left/ThrowError: Create a failed computation
  • FromEither: Lift an Either into ReaderIOEither
  • FromIO: Lift an IO into ReaderIOEither
  • FromReader: Lift a Reader into ReaderIOEither
  • FromIOEither: Lift an IOEither into ReaderIOEither
  • TryCatch: Wrap error-returning functions

Transformation:

  • Map: Transform the success value
  • MapLeft: Transform the error value
  • BiMap: Transform both success and error values
  • Chain/Bind: Sequence dependent computations
  • Flatten: Flatten nested ReaderIOEither

Combination:

  • Ap: Apply a function in a context to a value in a context
  • SequenceArray: Convert array of ReaderIOEither to ReaderIOEither of array
  • TraverseArray: Map and sequence in one operation

Error Handling:

  • Fold: Handle both success and error cases
  • GetOrElse: Provide a default value on error
  • OrElse: Try an alternative computation on error
  • Alt: Choose the first successful computation

Context Access:

  • Ask: Get the current context
  • Asks: Get a value derived from the context
  • Local: Run a computation with a modified context

Resource Management:

  • Bracket: Ensure resource cleanup
  • WithResource: Manage resource lifecycle

Example Usage

type Config struct {
    BaseURL string
    Timeout time.Duration
}

// A computation that depends on Config, performs IO, and can fail
func fetchUser(id int) readerioeither.ReaderIOEither[Config, error, User] {
    return func(cfg Config) ioeither.IOEither[error, User] {
        return func() either.Either[error, User] {
            // Use cfg.BaseURL and cfg.Timeout to fetch user
            // Return either.Right(user) or either.Left(err)
        }
    }
}

// Compose operations
result := function.Pipe2(
    fetchUser(123),
    readerioeither.Map[Config, error](func(u User) string { return u.Name }),
    readerioeither.Chain[Config, error](func(name string) readerioeither.ReaderIOEither[Config, error, string] {
        return readerioeither.Of[Config, error]("Hello, " + name)
    }),
)

// Execute with config
config := Config{BaseURL: "https://api.example.com", Timeout: 30 * time.Second}
outcome := result(config)() // Returns either.Either[error, string]

Copyright (c) 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.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Ap

func Ap[B, R, E, A any](fa ReaderIOEither[R, E, A]) func(fab ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B]

Ap returns a function that applies a function in a context to a value in a context. This is the curried version of MonadAp.

func BiMap

func BiMap[R, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, B]

BiMap returns a function that maps over both the error and success channels. This is the curried version of MonadBiMap.

func ChainFirstReaderOptionK

func ChainFirstReaderOptionK[R, A, B, E any](onNone func() E) func(readeroption.Kleisli[R, A, B]) Operator[R, E, A, A]

func ChainLeft

func ChainLeft[R, EA, EB, A any](f Kleisli[R, EB, EA, A]) func(ReaderIOEither[R, EA, A]) ReaderIOEither[R, EB, A]

func ChainOptionK

func ChainOptionK[R, A, B, E any](onNone func() E) func(func(A) Option[B]) Operator[R, E, A, B]

ChainOptionK returns a function that chains an Option-returning function into ReaderIOEither. If the Option is None, the provided error function is called to produce the error value.

func ChainReaderOptionK

func ChainReaderOptionK[R, A, B, E any](onNone func() E) func(readeroption.Kleisli[R, A, B]) Operator[R, E, A, B]

func Eitherize0

func Eitherize0[F ~func(C) (R, error), C, R any](f F) func() ReaderIOEither[C, error, R]

Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize1

func Eitherize1[F ~func(C, T0) (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R]

Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize10

func Eitherize10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R]

Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize2

func Eitherize2[F ~func(C, T0, T1) (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R]

Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize3

func Eitherize3[F ~func(C, T0, T1, T2) (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R]

Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize4

func Eitherize4[F ~func(C, T0, T1, T2, T3) (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R]

Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize5

func Eitherize5[F ~func(C, T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R]

Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize6

func Eitherize6[F ~func(C, T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R]

Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize7

func Eitherize7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R]

Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize8

func Eitherize8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R]

Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eitherize9

func Eitherize9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R]

Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[C, error, R]] The first parameter is considered to be the context [C].

func Eq

func Eq[R, E, A any](eq EQ.Eq[either.Either[E, A]]) func(R) EQ.Eq[ReaderIOEither[R, E, A]]

Eq implements the equals predicate for values contained in the IOEither monad

func Flap

func Flap[R, E, B, A any](a A) func(ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B]

Flap returns a function that applies a fixed value to a function in a context. This is the curried version of MonadFlap.

func Fold

func Fold[R, E, A, B any](onLeft func(E) ReaderIO[R, B], onRight func(A) ReaderIO[R, B]) func(ReaderIOEither[R, E, A]) ReaderIO[R, B]

Fold handles both success and error cases, producing a ReaderIO. This is useful for converting a ReaderIOEither into a ReaderIO by handling all cases.

func From0

func From0[F ~func(C) func() (R, error), C, R any](f F) func() ReaderIOEither[C, error, R]

From0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From1

func From1[F ~func(C, T0) func() (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R]

From1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From10

func From10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R]

From10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From2

func From2[F ~func(C, T0, T1) func() (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R]

From2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From3

func From3[F ~func(C, T0, T1, T2) func() (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R]

From3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From4

func From4[F ~func(C, T0, T1, T2, T3) func() (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R]

From4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From5

func From5[F ~func(C, T0, T1, T2, T3, T4) func() (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R]

From5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From6

func From6[F ~func(C, T0, T1, T2, T3, T4, T5) func() (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R]

From6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From7

func From7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) func() (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R]

From7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From8

func From8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R]

From8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func From9

func From9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R]

From9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[R]] The first parameter is considered to be the context [C].

func FromOption

func FromOption[R, A, E any](onNone func() E) func(Option[A]) ReaderIOEither[R, E, A]

FromOption converts an Option to a ReaderIOEither. If the Option is None, the provided function is called to produce the error.

func FromPredicate

func FromPredicate[R, E, A any](pred func(A) bool, onFalse func(A) E) func(A) ReaderIOEither[R, E, A]

FromPredicate creates a ReaderIOEither from a predicate. If the predicate returns false, the onFalse function is called to produce the error.

func FromStrictEquals

func FromStrictEquals[R any, E, A comparable]() func(R) EQ.Eq[ReaderIOEither[R, E, A]]

FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function

func Functor

func Functor[R, E, A, B any]() functor.Functor[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]]

Functor returns the functor operations for ReaderIOEither

func GetOrElse

func GetOrElse[R, E, A any](onLeft func(E) ReaderIO[R, A]) func(ReaderIOEither[R, E, A]) ReaderIO[R, A]

GetOrElse provides a default value in case of error. The default is computed lazily via a ReaderIO.

func Local

func Local[E, A, R1, R2 any](f func(R2) R1) func(ReaderIOEither[R1, E, A]) ReaderIOEither[R2, E, A]

Local runs a computation with a modified context. The function f transforms the context before passing it to the computation. This is similar to Contravariant's contramap operation.

func MapLeft

func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A]

MapLeft returns a function that transforms the error channel. This is the curried version of MonadMapLeft.

func Monad

func Monad[R, E, A, B any]() monad.Monad[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]]

Monad returns the monadic operations for ReaderIOEither

func OrElse

func OrElse[R, E1, A, E2 any](onLeft func(E1) ReaderIOEither[R, E2, A]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A]

OrElse tries an alternative computation if the first one fails. The alternative can produce a different error type.

func OrLeft

func OrLeft[A, E1, R, E2 any](onLeft func(E1) ReaderIO[R, E2]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A]

OrLeft transforms the error using a ReaderIO if the computation fails. The success value is preserved unchanged.

func Pointed

func Pointed[R, E, A any]() pointed.Pointed[A, ReaderIOEither[R, E, A]]

Pointed returns the pointed operations for ReaderIOEither

func Read

func Read[E, A, R any](r R) func(ReaderIOEither[R, E, A]) IOEither[E, A]

func TapReaderOptionK

func TapReaderOptionK[R, A, B, E any](onNone func() E) func(readeroption.Kleisli[R, A, B]) Operator[R, E, A, A]

func Traverse

func Traverse[R2, R1, E, A, B any](
	f Kleisli[R1, E, A, B],
) func(ReaderIOEither[R2, E, A]) Kleisli[R2, E, R1, B]

Traverse transforms a ReaderIOEither computation by applying a function that produces another ReaderIOEither, effectively swapping the order of environment parameters.

This function is useful when you have a computation that depends on environment R2 and produces a value of type A, and you want to transform it using a function that takes A and produces a computation depending on environment R1. The result is a curried function that takes R2 first, then R1, and produces an IOEither[E, B].

Type Parameters:

  • R2: The outer environment type (provided first)
  • R1: The inner environment type (provided second)
  • E: The error type
  • A: The input value type
  • B: The output value type

Parameters:

  • f: A Kleisli arrow that transforms A into a ReaderIOEither[R1, E, B]

Returns:

  • A function that takes a ReaderIOEither[R2, E, A] and returns a Kleisli[R2, E, R1, B], which is func(R2) ReaderIOEither[R1, E, B]

The function preserves error handling and IO effects while reordering the environment dependencies.

func TraverseArrayWithIndex

func TraverseArrayWithIndex[R, E, A, B any](f func(int, A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B]

TraverseArrayWithIndex is like TraverseArray but the transformation function also receives the index.

This is useful when the transformation depends on the element's position in the array.

Type parameters:

  • R: The context type
  • E: The error type
  • A: The input element type
  • B: The output element type

Parameters:

  • f: A function that transforms each element and its index into a ReaderIOEither

Returns:

A function that takes an array and returns a ReaderIOEither of an array

Example:

processWithIndex := TraverseArrayWithIndex(func(i int, val string) ReaderIOEither[Config, error, string] {
    return Of[Config, error](fmt.Sprintf("%d: %s", i, val))
})
Example

Example of TraverseArrayWithIndex - process items with their positions

package main

import (
	"fmt"

	E "github.com/IBM/fp-go/v2/either"

	RIE "github.com/IBM/fp-go/v2/readerioeither"
)

type Config struct {
	APIKey  string
	BaseURL string
}

func main() {
	cfg := Config{APIKey: "secret"}

	items := []string{"apple", "banana", "cherry"}

	processWithIndex := RIE.TraverseArrayWithIndex(func(i int, item string) RIE.ReaderIOEither[Config, error, string] {
		return RIE.Of[Config, error](fmt.Sprintf("%d: %s", i+1, item))
	})

	result := processWithIndex(items)(cfg)()

	E.Fold(
		func(err error) int {
			fmt.Printf("Error: %v\n", err)
			return 0
		},
		func(processed []string) int {
			for _, item := range processed {
				fmt.Println(item)
			}
			return len(processed)
		},
	)(result)
}
Output:

1: apple
2: banana
3: cherry

func TraverseReader

func TraverseReader[R2, R1, E, A, B any](
	f reader.Kleisli[R1, A, B],
) func(ReaderIOEither[R2, E, A]) Kleisli[R2, E, R1, B]

TraverseReader transforms a ReaderIOEither computation by applying a Reader-based function, effectively introducing a new environment dependency.

This function takes a Reader-based transformation (Kleisli arrow) and returns a function that can transform a ReaderIOEither. The result allows you to provide the Reader's environment (R1) first, which then produces a ReaderIOEither that depends on environment R2.

Type Parameters:

  • R2: The outer environment type (from the original ReaderIOEither)
  • R1: The inner environment type (introduced by the Reader transformation)
  • E: The error type
  • A: The input value type
  • B: The output value type

Parameters:

  • f: A Reader-based Kleisli arrow that transforms A to B using environment R1

Returns:

  • A function that takes a ReaderIOEither[R2, E, A] and returns a Kleisli[R2, E, R1, B], which is func(R2) ReaderIOEither[R1, E, B]

The function preserves error handling and IO effects while adding the Reader environment dependency.

Example:

type Config struct {
    Multiplier int
}
type Database struct {
    ConnectionString string
}

// Original computation that depends on Database
original := func(db Database) IOEither[error, int] {
    return ioeither.Right[error](len(db.ConnectionString))
}

// Reader-based transformation that depends on Config
multiply := func(x int) func(Config) int {
    return func(cfg Config) int {
        return x * cfg.Multiplier
    }
}

// Apply TraverseReader to introduce Config dependency
traversed := TraverseReader[Database, Config, error, int, int](multiply)
result := traversed(original)

// Provide Config first, then Database
cfg := Config{Multiplier: 5}
db := Database{ConnectionString: "localhost:5432"}
finalResult := result(cfg)(db)() // Returns Right(80) = len("localhost:5432") * 5

func TraverseRecord

func TraverseRecord[K comparable, R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B]

TraverseRecord transforms each value in a map using a function that returns a ReaderIOEither, then collects the results into a single ReaderIOEither containing a map.

If any transformation fails, the entire operation fails with the first error encountered. The keys are preserved in the output map.

Type parameters:

  • R: The context type
  • K: The key type (must be comparable)
  • E: The error type
  • A: The input value type
  • B: The output value type

Parameters:

  • f: A function that transforms each value into a ReaderIOEither

Returns:

A function that takes a map and returns a ReaderIOEither of a map

Example:

enrichUsers := TraverseRecord(func(user User) ReaderIOEither[Config, error, EnrichedUser] {
    return enrichUser(user)
})
result := enrichUsers(map[string]User{"alice": user1, "bob": user2})

func TraverseRecordWithIndex

func TraverseRecordWithIndex[K comparable, R, E, A, B any](f func(K, A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B]

TraverseRecordWithIndex is like TraverseRecord but the transformation function also receives the key.

This is useful when the transformation depends on the key associated with each value.

Type parameters:

  • R: The context type
  • K: The key type (must be comparable)
  • E: The error type
  • A: The input value type
  • B: The output value type

Parameters:

  • f: A function that transforms each key-value pair into a ReaderIOEither

Returns:

A function that takes a map and returns a ReaderIOEither of a map

Example:

processWithKey := TraverseRecordWithIndex(func(key string, val int) ReaderIOEither[Config, error, string] {
    return Of[Config, error](fmt.Sprintf("%s: %d", key, val))
})

func Uneitherize0

func Uneitherize0[F ~func() ReaderIOEither[C, error, R], C, R any](f F) func(C) (R, error)

Uneitherize0 converts a function with 1 parameters returning a [ReaderIOEither[C, error, R]] into a function with 0 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize1

func Uneitherize1[F ~func(T0) ReaderIOEither[C, error, R], T0, C, R any](f F) func(C, T0) (R, error)

Uneitherize1 converts a function with 2 parameters returning a [ReaderIOEither[C, error, R]] into a function with 1 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize10

func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error)

Uneitherize10 converts a function with 11 parameters returning a [ReaderIOEither[C, error, R]] into a function with 10 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize2

func Uneitherize2[F ~func(T0, T1) ReaderIOEither[C, error, R], T0, T1, C, R any](f F) func(C, T0, T1) (R, error)

Uneitherize2 converts a function with 3 parameters returning a [ReaderIOEither[C, error, R]] into a function with 2 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize3

func Uneitherize3[F ~func(T0, T1, T2) ReaderIOEither[C, error, R], T0, T1, T2, C, R any](f F) func(C, T0, T1, T2) (R, error)

Uneitherize3 converts a function with 4 parameters returning a [ReaderIOEither[C, error, R]] into a function with 3 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize4

func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOEither[C, error, R], T0, T1, T2, T3, C, R any](f F) func(C, T0, T1, T2, T3) (R, error)

Uneitherize4 converts a function with 5 parameters returning a [ReaderIOEither[C, error, R]] into a function with 4 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize5

func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, C, R any](f F) func(C, T0, T1, T2, T3, T4) (R, error)

Uneitherize5 converts a function with 6 parameters returning a [ReaderIOEither[C, error, R]] into a function with 5 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize6

func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5) (R, error)

Uneitherize6 converts a function with 7 parameters returning a [ReaderIOEither[C, error, R]] into a function with 6 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize7

func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6) (R, error)

Uneitherize7 converts a function with 8 parameters returning a [ReaderIOEither[C, error, R]] into a function with 7 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize8

func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error)

Uneitherize8 converts a function with 9 parameters returning a [ReaderIOEither[C, error, R]] into a function with 8 parameters returning a tuple. The first parameter is considered to be the context [C].

func Uneitherize9

func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error)

Uneitherize9 converts a function with 10 parameters returning a [ReaderIOEither[C, error, R]] into a function with 9 parameters returning a tuple. The first parameter is considered to be the context [C].

Types

type Consumer

type Consumer[A any] = consumer.Consumer[A]

type Either

type Either[E, A any] = either.Either[E, A]

Either represents a value of one of two possible types (a disjoint union). An instance of Either is either Left (representing an error) or Right (representing a success).

type IO

type IO[T any] = io.IO[T]

type IOEither

type IOEither[E, A any] = ioeither.IOEither[E, A]

IOEither represents a computation that performs side effects and can either fail with an error of type E or succeed with a value of type A.

type Kleisli

type Kleisli[R, E, A, B any] = reader.Reader[A, ReaderIOEither[R, E, B]]

func FromReaderOption

func FromReaderOption[R, A, E any](onNone func() E) Kleisli[R, E, ReaderOption[R, A], A]

func ReduceArray

func ReduceArray[R, E, A, B any](reduce func(B, A) B, initial B) Kleisli[R, E, []ReaderIOEither[R, E, A], B]

func ReduceArrayM

func ReduceArrayM[R, E, A any](m monoid.Monoid[A]) Kleisli[R, E, []ReaderIOEither[R, E, A], A]

func Sequence

func Sequence[R1, R2, E, A any](ma ReaderIOEither[R2, E, ReaderIOEither[R1, E, A]]) Kleisli[R2, E, R1, A]

Sequence swaps the order of nested environment parameters in a ReaderIOEither computation.

This function takes a ReaderIOEither that produces another ReaderIOEither and returns a reader.Kleisli that reverses the order of the environment parameters. The result is a curried function that takes R2 first, then R1, and produces an IOEither[E, A].

Type Parameters:

  • R1: The first environment type (becomes inner after sequence)
  • R2: The second environment type (becomes outer after sequence)
  • E: The error type
  • A: The success value type

Parameters:

  • ma: A ReaderIOEither that takes R2 and may produce a ReaderIOEither[R1, E, A]

Returns:

  • A Kleisli[R2, E, R1, A], which is func(R2) func(R1) IOEither[E, A]

The function preserves error handling and IO effects at both levels.

func SequenceReader

func SequenceReader[R1, R2, E, A any](ma ReaderIOEither[R2, E, Reader[R1, A]]) Kleisli[R2, E, R1, A]

SequenceReader swaps the order of environment parameters when the inner computation is a pure Reader.

This function is similar to Sequence but specialized for the case where the innermost computation is a pure Reader (without IO or error handling) rather than another ReaderIOEither.

Type Parameters:

  • R1: The first environment type (becomes outer after sequence)
  • R2: The second environment type (becomes inner after sequence)
  • E: The error type (only present in the ReaderIOEither layer)
  • A: The success value type

Parameters:

  • ma: A ReaderIOEither that takes R2 and may produce a Reader[R1, A]

Returns:

  • A Kleisli[R2, E, R1, A], which is func(R2) func(R1) IOEither[E, A]

func SequenceReaderEither

func SequenceReaderEither[R1, R2, E, A any](ma ReaderIOEither[R2, E, ReaderEither[R1, E, A]]) Kleisli[R2, E, R1, A]

SequenceReaderEither swaps the order of environment parameters when the inner computation is a ReaderEither.

This function is specialized for the case where the innermost computation is a ReaderEither (with error handling but no IO effects) rather than another ReaderIOEither.

Type Parameters:

  • R1: The first environment type (becomes outer after sequence)
  • R2: The second environment type (becomes inner after sequence)
  • E: The error type (present in both layers)
  • A: The success value type

Parameters:

  • ma: A ReaderIOEither that takes R2 and may produce a ReaderEither[R1, E, A]

Returns:

  • A Kleisli[R2, E, R1, A], which is func(R2) func(R1) IOEither[E, A]

func SequenceReaderIO

func SequenceReaderIO[R1, R2, E, A any](ma ReaderIOEither[R2, E, ReaderIO[R1, A]]) Kleisli[R2, E, R1, A]

SequenceReaderIO swaps the order of environment parameters when the inner computation is a ReaderIO.

This function is specialized for the case where the innermost computation is a ReaderIO (with IO effects but no error handling) rather than another ReaderIOEither.

Type Parameters:

  • R1: The first environment type (becomes outer after sequence)
  • R2: The second environment type (becomes inner after sequence)
  • E: The error type (only present in the outer ReaderIOEither layer)
  • A: The success value type

Parameters:

  • ma: A ReaderIOEither that takes R2 and may produce a ReaderIO[R1, A]

Returns:

  • A Kleisli[R2, E, R1, A], which is func(R2) func(R1) IOEither[E, A]

func TailRec

func TailRec[R, E, A, B any](f Kleisli[R, E, A, tailrec.Trampoline[A, B]]) Kleisli[R, E, A, B]

TailRec implements stack-safe tail recursion for the ReaderIOEither monad.

This function enables recursive computations that combine three powerful concepts:

  • Environment dependency (Reader aspect): Access to configuration, context, or dependencies
  • Side effects (IO aspect): Logging, file I/O, network calls, etc.
  • Error handling (Either aspect): Computations that can fail with an error

The function uses an iterative loop to execute the recursion, making it safe for deep or unbounded recursion without risking stack overflow.

How It Works

TailRec takes a Kleisli arrow that returns IOEither[E, Trampoline[A, B]]:

  • Left(E): Computation failed with error E - recursion terminates
  • Right(Bounce(A)): Continue recursion with the new state A
  • Right(Land(B)): Terminate recursion successfully and return the final result B

The function iteratively applies the Kleisli arrow, passing the environment R to each iteration, until either an error (Left) or a final result (Right(Right(B))) is produced.

Type Parameters

  • R: The environment type (Reader context) - e.g., Config, Logger, Database connection
  • E: The error type that can occur during computation
  • A: The state type that changes during recursion
  • B: The final result type when recursion terminates successfully

Parameters

  • f: A Kleisli arrow (A => ReaderIOEither[R, E, Either[A, B]]) that:
  • Takes the current state A
  • Returns a ReaderIOEither that depends on environment R
  • Can fail with error E (Left)
  • Produces Either[A, B] to control recursion flow (Right)

Returns

A Kleisli arrow (A => ReaderIOEither[R, E, B]) that:

  • Takes an initial state A
  • Returns a ReaderIOEither that requires environment R
  • Can fail with error E
  • Produces the final result B after recursion completes

Comparison with Other Monads

Compared to other tail recursion implementations:

  • Like IOEither: Has error handling (Left for errors)
  • Like ReaderIO: Has environment dependency (R passed to each iteration)
  • Unlike IOOption: Uses Either for both errors and recursion control
  • Most powerful: Combines all three aspects (Reader + IO + Either)

Use Cases

  1. Environment-dependent recursive algorithms with error handling: - Recursive computations that need configuration and can fail - Algorithms that log progress and handle errors gracefully - Recursive operations with retry logic based on environment settings

  2. Stateful computations with context and error handling: - Tree traversals that need environment context and can fail - Graph algorithms with configuration-dependent behavior and error cases - Recursive parsers with environment-based rules and error reporting

  3. Recursive operations with side effects and error handling: - File system traversals with logging and error handling - Network operations with retry configuration and failure handling - Database operations with connection pooling and error recovery

Example: Factorial with Logging and Error Handling

type Env struct {
    Logger func(string)
    MaxN   int
}

type State struct {
    n   int
    acc int
}

// Factorial that logs each step and validates input
factorialStep := func(state State) readerioeither.ReaderIOEither[Env, string, tailrec.Trampoline[State, int]] {
    return func(env Env) ioeither.IOEither[string, tailrec.Trampoline[State, int]] {
        return func() either.Either[string, tailrec.Trampoline[State, int]] {
            if state.n > env.MaxN {
                return either.Left[tailrec.Trampoline[State, int]](fmt.Sprintf("n too large: %d > %d", state.n, env.MaxN))
            }
            if state.n <= 0 {
                env.Logger(fmt.Sprintf("Factorial complete: %d", state.acc))
                return either.Right[string](tailrec.Land[State](state.acc))
            }
            env.Logger(fmt.Sprintf("Computing: %d * %d", state.n, state.acc))
            return either.Right[string](tailrec.Bounce[int](State{state.n - 1, state.acc * state.n}))
        }
    }
}

factorial := readerioeither.TailRec(factorialStep)
env := Env{Logger: func(msg string) { fmt.Println(msg) }, MaxN: 20}
result := factorial(State{5, 1})(env)() // Returns Right(120), logs each step
// If n > MaxN, returns Left("n too large: ...")

Example: File Processing with Error Handling

type Config struct {
    MaxRetries int
    Logger     func(string)
}

type ProcessState struct {
    files   []string
    results []string
    retries int
}

processFilesStep := func(state ProcessState) readerioeither.ReaderIOEither[Config, error, tailrec.Trampoline[ProcessState, []string]] {
    return func(cfg Config) ioeither.IOEither[error, tailrec.Trampoline[ProcessState, []string]] {
        return func() either.Either[error, tailrec.Trampoline[ProcessState, []string]] {
            if len(state.files) == 0 {
                cfg.Logger("All files processed")
                return either.Right[error](tailrec.Land[ProcessState](state.results))
            }
            file := state.files[0]
            cfg.Logger(fmt.Sprintf("Processing: %s", file))

            // Simulate file processing that might fail
            if err := processFile(file); err != nil {
                if state.retries >= cfg.MaxRetries {
                    return either.Left[tailrec.Trampoline[ProcessState, []string]](
                        fmt.Errorf("max retries exceeded for %s: %w", file, err))
                }
                cfg.Logger(fmt.Sprintf("Retry %d for %s", state.retries+1, file))
                return either.Right[error](tailrec.Bounce[[]string](ProcessState{
                    files:   state.files,
                    results: state.results,
                    retries: state.retries + 1,
                }))
            }

            return either.Right[error](tailrec.Bounce[[]string](ProcessState{
                files:   state.files[1:],
                results: append(state.results, file),
                retries: 0,
            }))
        }
    }
}

processFiles := readerioeither.TailRec(processFilesStep)
config := Config{MaxRetries: 3, Logger: func(msg string) { fmt.Println(msg) }}
result := processFiles(ProcessState{files: []string{"a.txt", "b.txt"}, results: []string{}, retries: 0})(config)()
// Returns Right([]string{"a.txt", "b.txt"}) on success
// Returns Left(error) if max retries exceeded

Stack Safety

The iterative implementation ensures that even deeply recursive computations (thousands or millions of iterations) will not cause stack overflow:

// Safe for very large inputs
countdownStep := func(n int) readerioeither.ReaderIOEither[Env, error, tailrec.Trampoline[int, int]] {
    return func(env Env) ioeither.IOEither[error, tailrec.Trampoline[int, int]] {
        return func() either.Either[error, tailrec.Trampoline[int, int]] {
            if n <= 0 {
                return either.Right[error](tailrec.Land[int](0))
            }
            return either.Right[error](tailrec.Bounce[int](n - 1))
        }
    }
}
countdown := readerioeither.TailRec(countdownStep)
result := countdown(1000000)(env)() // Safe, no stack overflow

Error Handling Patterns

The Either[E, Trampoline[A, B]] structure provides two levels of control:

  1. Outer Either (Left(E)): Unrecoverable errors that terminate recursion - Validation failures - Resource exhaustion - Fatal errors

  2. Inner Trampoline (Right(Bounce(A)) or Right(Land(B))): Recursion control - Bounce(A): Continue with new state - Land(B): Terminate successfully

This separation allows for:

  • Early termination on errors
  • Graceful error propagation
  • Clear distinction between "continue" and "error" states

Performance Considerations

  • Each iteration creates a new IOEither action by calling f(a)(r)()
  • The environment R is passed to every iteration
  • Error checking happens on each iteration (Left vs Right)
  • For performance-critical code, consider if error handling is necessary at each step
  • Memoization of environment-derived values may improve performance

See Also

  • ioeither.TailRec: Tail recursion with error handling (no environment)
  • readerio.TailRec: Tail recursion with environment (no error handling)
  • [iooption.TailRec]: Tail recursion with optional results
  • Chain: For sequencing ReaderIOEither computations
  • Ask: For accessing the environment
  • Left/Right: For creating error/success values

func TraverseArray

func TraverseArray[R, E, A, B any](f Kleisli[R, E, A, B]) Kleisli[R, E, []A, []B]

TraverseArray transforms each element of an array using a function that returns a ReaderIOEither, then collects the results into a single ReaderIOEither containing an array.

If any transformation fails, the entire operation fails with the first error encountered. All transformations are executed sequentially.

Type parameters:

  • R: The context type
  • E: The error type
  • A: The input element type
  • B: The output element type

Parameters:

  • f: A function that transforms each element into a ReaderIOEither

Returns:

A function that takes an array and returns a ReaderIOEither of an array

Example:

fetchUsers := TraverseArray(func(id int) ReaderIOEither[Config, error, User] {
    return fetchUser(id)
})
result := fetchUsers([]int{1, 2, 3})
// result(cfg)() returns Right([user1, user2, user3]) or Left(error)
Example

Example of TraverseArray - fetch multiple users

package main

import (
	"fmt"

	E "github.com/IBM/fp-go/v2/either"

	RIE "github.com/IBM/fp-go/v2/readerioeither"

	S "github.com/IBM/fp-go/v2/string"
)

type Config struct {
	APIKey  string
	BaseURL string
}

type User struct {
	ID   int
	Name string
}

// Simulate fetching a user by ID
func fetchUser(id int) RIE.ReaderIOEither[Config, error, User] {
	return func(cfg Config) func() E.Either[error, User] {
		return func() E.Either[error, User] {
			if S.IsEmpty(cfg.APIKey) {
				return E.Left[User](fmt.Errorf("missing API key"))
			}
			if id <= 0 {
				return E.Left[User](fmt.Errorf("invalid user ID: %d", id))
			}

			return E.Right[error](User{ID: id, Name: fmt.Sprintf("User%d", id)})
		}
	}
}

func main() {
	cfg := Config{APIKey: "secret", BaseURL: "https://api.example.com"}

	// Fetch users with IDs 1, 2, 3
	userIDs := []int{1, 2, 3}

	fetchUsers := RIE.TraverseArray(fetchUser)
	result := fetchUsers(userIDs)(cfg)()

	E.Fold(
		func(err error) string {
			fmt.Println("Failed to fetch users")
			return ""
		},
		func(users []User) string {
			fmt.Println("Successfully fetched all users")
			return ""
		},
	)(result)
}
Output:

Successfully fetched all users
Example (ErrorHandling)

Example showing error handling with TraverseArray

package main

import (
	"fmt"

	E "github.com/IBM/fp-go/v2/either"

	RIE "github.com/IBM/fp-go/v2/readerioeither"

	S "github.com/IBM/fp-go/v2/string"
)

type Config struct {
	APIKey  string
	BaseURL string
}

type User struct {
	ID   int
	Name string
}

// Simulate fetching a user by ID
func fetchUser(id int) RIE.ReaderIOEither[Config, error, User] {
	return func(cfg Config) func() E.Either[error, User] {
		return func() E.Either[error, User] {
			if S.IsEmpty(cfg.APIKey) {
				return E.Left[User](fmt.Errorf("missing API key"))
			}
			if id <= 0 {
				return E.Left[User](fmt.Errorf("invalid user ID: %d", id))
			}

			return E.Right[error](User{ID: id, Name: fmt.Sprintf("User%d", id)})
		}
	}
}

func main() {
	cfg := Config{APIKey: "secret"}

	// One invalid ID will cause the entire operation to fail
	userIDs := []int{1, -1, 3} // -1 is invalid

	fetchUsers := RIE.TraverseArray(fetchUser)
	result := fetchUsers(userIDs)(cfg)()

	E.Fold(
		func(err error) string {
			fmt.Printf("Failed: %v\n", err)
			return ""
		},
		func(users []User) string {
			fmt.Printf("Success: fetched %d users\n", len(users))
			return ""
		},
	)(result)
}
Output:

Failed: invalid user ID: -1
Example (Pipeline)

Example of combining Traverse with other operations

package main

import (
	"fmt"

	E "github.com/IBM/fp-go/v2/either"
	F "github.com/IBM/fp-go/v2/function"

	RIE "github.com/IBM/fp-go/v2/readerioeither"

	S "github.com/IBM/fp-go/v2/string"
)

type Config struct {
	APIKey  string
	BaseURL string
}

type User struct {
	ID   int
	Name string
}

// Simulate fetching a user by ID
func fetchUser(id int) RIE.ReaderIOEither[Config, error, User] {
	return func(cfg Config) func() E.Either[error, User] {
		return func() E.Either[error, User] {
			if S.IsEmpty(cfg.APIKey) {
				return E.Left[User](fmt.Errorf("missing API key"))
			}
			if id <= 0 {
				return E.Left[User](fmt.Errorf("invalid user ID: %d", id))
			}

			return E.Right[error](User{ID: id, Name: fmt.Sprintf("User%d", id)})
		}
	}
}

func main() {
	cfg := Config{APIKey: "secret"}

	// Pipeline: fetch users, then extract their names
	userIDs := []int{1, 2}

	pipeline := F.Pipe2(
		userIDs,
		RIE.TraverseArray(fetchUser),
		RIE.Map[Config, error](func(users []User) []string {
			names := make([]string, len(users))
			for i, user := range users {
				names[i] = user.Name
			}
			return names
		}),
	)

	result := pipeline(cfg)()

	E.Fold(
		func(err error) string {
			fmt.Printf("Error: %v\n", err)
			return ""
		},
		func(names []string) string {
			fmt.Printf("User names: %v\n", names)
			return ""
		},
	)(result)
}
Output:

User names: [User1 User2]

func TraverseReduceArray

func TraverseReduceArray[R, E, A, B, C any](trfrm Kleisli[R, E, A, B], reduce func(C, B) C, initial C) Kleisli[R, E, []A, C]

func TraverseReduceArrayM

func TraverseReduceArrayM[R, E, A, B any](trfrm Kleisli[R, E, A, B], m monoid.Monoid[B]) Kleisli[R, E, []A, B]

func WithResource

func WithResource[A, L, E, R, ANY any](onCreate ReaderIOEither[L, E, R], onRelease Kleisli[L, E, R, ANY]) Kleisli[L, E, Kleisli[L, E, R, A], A]

WithResource constructs a function that creates a resource, operates on it, and then releases the resource. This ensures proper resource cleanup even in the presence of errors, following the Resource Acquisition Is Initialization (RAII) pattern.

The resource lifecycle is:

  1. onCreate: Acquires the resource
  2. use: Operates on the resource (provided as argument to the returned function)
  3. onRelease: Releases the resource (called regardless of success or failure)

Type parameters:

  • A: The type of the result produced by using the resource
  • L: The context type
  • E: The error type
  • R: The resource type
  • ANY: The type returned by the release function (typically ignored)

Parameters:

  • onCreate: A computation that acquires the resource
  • onRelease: A function that releases the resource, called with the resource and executed regardless of errors

Returns:

A function that takes a resource-using function and returns a ReaderIOEither that manages the resource lifecycle

Example:

withFile := WithResource(
    openFile("data.txt"),
    func(f *File) ReaderIOEither[Config, error, int] {
        return closeFile(f)
    },
)
result := withFile(func(f *File) ReaderIOEither[Config, error, string] {
    return readContent(f)
})

type Monoid

type Monoid[R, E, A any] = monoid.Monoid[ReaderIOEither[R, E, A]]

func AltMonoid

func AltMonoid[R, E, A any](zero lazy.Lazy[ReaderIOEither[R, E, A]]) Monoid[R, E, A]

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 ReaderIOResult[A] with Alt-based combination.

func AlternativeMonoid

func AlternativeMonoid[R, E, A any](m monoid.Monoid[A]) Monoid[R, E, A]

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 ReaderIOResult[A] with alternative semantics.

func ApplicativeMonoid

func ApplicativeMonoid[R, E, A any](m monoid.Monoid[A]) Monoid[R, E, A]

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 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 ReaderIOResult[A].

func ApplicativeMonoidPar

func ApplicativeMonoidPar[R, E, A any](m monoid.Monoid[A]) Monoid[R, E, A]

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 ReaderIOResult[A] with parallel execution.

func ApplicativeMonoidSeq

func ApplicativeMonoidSeq[R, E, A any](m monoid.Monoid[A]) Monoid[R, E, A]

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 ReaderIOResult[A] with sequential execution.

type Operator

type Operator[R, E, A, B any] = Kleisli[R, E, ReaderIOEither[R, E, A], B]

Operator represents a transformation from one ReaderIOEither to another. It's a Reader that takes a ReaderIOEither[R, E, A] and produces a ReaderIOEither[R, E, B]. This type is commonly used for composing operations in a point-free style.

Type parameters:

  • R: The context type
  • E: The error type
  • A: The input value type
  • B: The output value type

Example:

var doubleOp Operator[Config, error, int, int] = Map(N.Mul(2))

func After

func After[R, E, A any](timestamp time.Time) Operator[R, E, A, A]

After creates an operation that passes after the given time.Time

func Alt

func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) Operator[R, E, A, A]

Alt returns a function that tries an alternative computation if the first fails. This is the curried version of MonadAlt.

func ApEitherS

func ApEitherS[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Either[E, T],
) Operator[R, E, S1, S2]

ApEitherS is an applicative variant that works with Either values. It lifts an Either value into the ReaderIOEither context using applicative composition.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • fa: An Either value

func ApEitherSL

func ApEitherSL[R, E, S, T any](
	lens L.Lens[S, T],
	fa Either[E, T],
) Operator[R, E, S, S]

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 ApIOEitherS

func ApIOEitherS[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa IOEither[E, T],
) Operator[R, E, S1, S2]

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 ApIOEitherSL

func ApIOEitherSL[R, E, S, T any](
	lens L.Lens[S, T],
	fa IOEither[E, T],
) Operator[R, E, S, S]

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 ApIOS

func ApIOS[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa IO[T],
) Operator[R, E, S1, S2]

ApIOS is an applicative variant that works with IO values. It lifts an IO value into the ReaderIOEither context using applicative composition.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • fa: An IO value

func ApIOSL

func ApIOSL[R, E, S, T any](
	lens L.Lens[S, T],
	fa IO[T],
) Operator[R, E, S, S]

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 ApReaderIOS

func ApReaderIOS[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa ReaderIO[R, T],
) Operator[R, E, S1, S2]

ApReaderIOS is an applicative variant that works with ReaderIO values. It lifts a ReaderIO value into the ReaderIOEither context using applicative composition.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • fa: A ReaderIO value

func ApReaderIOSL

func ApReaderIOSL[R, E, S, T any](
	lens L.Lens[S, T],
	fa ReaderIO[R, T],
) Operator[R, E, S, S]

ApReaderIOSL is a lens-based variant of ApReaderIOS. It combines a lens with a ReaderIO value using applicative composition.

Parameters:

  • lens: A lens focusing on field T within state S
  • fa: A ReaderIO value

func ApReaderS

func ApReaderS[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Reader[R, T],
) Operator[R, E, S1, S2]

ApReaderS is an applicative variant that works with Reader values. It lifts a Reader value into the ReaderIOEither context using applicative composition.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • fa: A Reader value

func ApReaderSL

func ApReaderSL[R, E, S, T any](
	lens L.Lens[S, T],
	fa Reader[R, T],
) Operator[R, E, S, S]

ApReaderSL is a lens-based variant of ApReaderS. It combines a lens with a Reader value using applicative composition.

Parameters:

  • lens: A lens focusing on field T within state S
  • fa: A Reader value

func ApS

func ApS[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa ReaderIOEither[R, E, T],
) Operator[R, E, S1, S2]

ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently (using Applicative rather than Monad). This allows independent computations to be combined without one depending on the result of the other.

Unlike Bind, which sequences operations, ApS can be used when operations are independent and can conceptually run in parallel.

Example:

type State struct {
    User   User
    Posts  []Post
}
type Env struct {
    UserRepo UserRepository
    PostRepo PostRepository
}

// These operations are independent and can be combined with ApS
getUser := readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
    return env.UserRepo.FindUser()
})
getPosts := readerioeither.Asks(func(env Env) ioeither.IOEither[error, []Post] {
    return env.PostRepo.FindPosts()
})

result := F.Pipe2(
    readerioeither.Do[Env, error](State{}),
    readerioeither.ApS(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        getUser,
    ),
    readerioeither.ApS(
        func(posts []Post) func(State) State {
            return func(s State) State { s.Posts = posts; return s }
        },
        getPosts,
    ),
)

func ApSL

func ApSL[R, E, S, T any](
	lens L.Lens[S, T],
	fa ReaderIOEither[R, E, T],
) Operator[R, E, S, S]

ApSL attaches a value to a context using a lens-based setter. This is a convenience function that combines ApS with a lens, allowing you to use optics to update nested structures in a more composable way.

The lens parameter provides both the getter and setter for a field within the structure S. This eliminates the need to manually write setter functions.

Example:

type State struct {
    User   User
    Posts  []Post
}
type Env struct {
    UserRepo UserRepository
    PostRepo PostRepository
}

userLens := lens.MakeLens(
    func(s State) User { return s.User },
    func(s State, u User) State { s.User = u; return s },
)

getUser := readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
    return env.UserRepo.FindUser()
})
result := F.Pipe2(
    readerioeither.Of[Env, error](State{}),
    readerioeither.ApSL(userLens, getUser),
)

func Bind

func Bind[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	f func(S1) ReaderIOEither[R, E, T],
) Operator[R, E, S1, S2]

Bind attaches the result of a computation to a context [S1] to produce a context [S2]. This enables sequential composition where each step can depend on the results of previous steps and access the shared environment.

The setter function takes the result of the computation and returns a function that updates the context from S1 to S2.

Example:

type State struct {
    User   User
    Posts  []Post
}
type Env struct {
    UserRepo UserRepository
    PostRepo PostRepository
}

result := F.Pipe2(
    readerioeither.Do[Env, error](State{}),
    readerioeither.Bind(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        func(s State) readerioeither.ReaderIOEither[Env, error, User] {
            return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
                return env.UserRepo.FindUser()
            })
        },
    ),
    readerioeither.Bind(
        func(posts []Post) func(State) State {
            return func(s State) State { s.Posts = posts; return s }
        },
        func(s State) readerioeither.ReaderIOEither[Env, error, []Post] {
            // This can access s.User from the previous step
            return readerioeither.Asks(func(env Env) ioeither.IOEither[error, []Post] {
                return env.PostRepo.FindPostsByUser(s.User.ID)
            })
        },
    ),
)

func BindEitherK

func BindEitherK[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	f either.Kleisli[E, S1, T],
) Operator[R, E, S1, S2]

BindEitherK is a variant of Bind that works with Either computations. It lifts an Either Kleisli arrow into the ReaderIOEither context.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • f: An Either Kleisli arrow (S1 -> Either[E, T])

func BindIOEitherK

func BindIOEitherK[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	f ioeither.Kleisli[E, S1, T],
) Operator[R, E, S1, S2]

BindIOEitherK is a variant of Bind that works with IOEither computations. It lifts an IOEither Kleisli arrow into the ReaderIOEither context, allowing you to compose IOEither operations within a do-notation chain.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • f: An IOEither Kleisli arrow (S1 -> IOEither[E, T])

Returns:

  • An Operator that can be used in a do-notation chain

func BindIOEitherKL

func BindIOEitherKL[R, E, S, T any](
	lens L.Lens[S, T],
	f ioeither.Kleisli[E, T, T],
) Operator[R, E, S, S]

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[E, T])

func BindIOK

func BindIOK[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	f io.Kleisli[S1, T],
) Operator[R, E, S1, S2]

BindIOK is a variant of Bind that works with IO computations. It lifts an IO Kleisli arrow into the ReaderIOEither context.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • f: An IO Kleisli arrow (S1 -> IO[T])

func BindIOKL

func BindIOKL[R, E, S, T any](
	lens L.Lens[S, T],
	f io.Kleisli[T, T],
) Operator[R, E, S, S]

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 BindL

func BindL[R, E, S, T any](
	lens L.Lens[S, T],
	f func(T) ReaderIOEither[R, E, T],
) Operator[R, E, S, S]

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
    Posts  []Post
}
type Env struct {
    UserRepo UserRepository
    PostRepo PostRepository
}

userLens := lens.MakeLens(
    func(s State) User { return s.User },
    func(s State, u User) State { s.User = u; return s },
)

result := F.Pipe2(
    readerioeither.Do[Env, error](State{}),
    readerioeither.BindL(userLens, func(user User) readerioeither.ReaderIOEither[Env, error, User] {
        return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
            return env.UserRepo.FindUser()
        })
    }),
)

func BindReaderIOK

func BindReaderIOK[E, R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f readerio.Kleisli[R, S1, T],
) Operator[R, E, S1, S2]

BindReaderIOK is a variant of Bind that works with ReaderIO computations. It lifts a ReaderIO Kleisli arrow into the ReaderIOEither context.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • f: A ReaderIO Kleisli arrow (S1 -> ReaderIO[R, T])

func BindReaderIOKL

func BindReaderIOKL[E, R, S, T any](
	lens L.Lens[S, T],
	f readerio.Kleisli[R, T, T],
) Operator[R, E, S, S]

BindReaderIOKL is a lens-based variant of BindReaderIOK. It combines a lens with a ReaderIO Kleisli arrow, 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[R, T])

func BindReaderK

func BindReaderK[E, R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f reader.Kleisli[R, S1, T],
) Operator[R, E, S1, S2]

BindReaderK is a variant of Bind that works with Reader computations. It lifts a Reader Kleisli arrow into the ReaderIOEither context.

Parameters:

  • setter: Updates state from S1 to S2 using result T
  • f: A Reader Kleisli arrow (S1 -> Reader[R, T])

func BindReaderKL

func BindReaderKL[E, R, S, T any](
	lens L.Lens[S, T],
	f reader.Kleisli[R, T, T],
) Operator[R, E, S, S]

BindReaderKL is a lens-based variant of BindReaderK. It combines a lens with a Reader Kleisli arrow, 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[R, T])

func BindTo

func BindTo[R, E, S1, T any](
	setter func(T) S1,
) Operator[R, E, T, S1]

BindTo initializes a new state [S1] from a value [T]

func Chain

func Chain[R, E, A, B any](f Kleisli[R, E, A, B]) Operator[R, E, A, B]

Chain returns a function that sequences computations where the second depends on the first. This is the curried version of MonadChain.

func ChainConsumer

func ChainConsumer[R, E, A any](c Consumer[A]) Operator[R, E, A, struct{}]

func ChainEitherK

func ChainEitherK[R, E, A, B any](f either.Kleisli[E, A, B]) Operator[R, E, A, B]

ChainEitherK returns a function that chains an Either-returning function into ReaderIOEither. This is the curried version of MonadChainEitherK.

func ChainFirst

func ChainFirst[R, E, A, B any](f Kleisli[R, E, A, B]) Operator[R, E, A, A]

ChainFirst returns a function that sequences computations but keeps the first result. This is the curried version of MonadChainFirst.

func ChainFirstConsumer

func ChainFirstConsumer[R, E, A any](c Consumer[A]) Operator[R, E, A, A]

func ChainFirstEitherK

func ChainFirstEitherK[R, E, A, B any](f either.Kleisli[E, A, B]) Operator[R, E, A, A]

ChainFirstEitherK returns a function that chains an Either computation while preserving the original value. This is the curried version of MonadChainFirstEitherK.

func ChainFirstIOK

func ChainFirstIOK[R, E, A, B any](f io.Kleisli[A, B]) Operator[R, E, A, A]

ChainFirstIOK returns a function that chains an IO computation while preserving the original value. This is the curried version of MonadChainFirstIOK.

func ChainFirstLeft

func ChainFirstLeft[A, R, EA, EB, B any](f Kleisli[R, EB, EA, B]) Operator[R, EA, A, A]

ChainFirstLeft is the curried version of MonadChainFirstLeft. It returns a function that chains a computation on the left (error) side while always preserving the original error.

This is particularly useful for adding error handling side effects (like logging, metrics, or notifications) in a functional pipeline. The original error is always returned regardless of what f returns (Left or Right), ensuring the error path is preserved.

Parameters:

  • f: A function that takes an error of type EA and returns a ReaderIOEither (typically for side effects)

Returns:

  • An Operator that performs the side effect but always returns the original error if input was Left

func ChainFirstReaderEitherK

func ChainFirstReaderEitherK[E, R, A, B any](f RE.Kleisli[R, E, A, B]) Operator[R, E, A, A]

ChainReaderK returns a function that chains a Reader-returning function into ReaderIOEither. This is the curried version of MonadChainReaderK.

func ChainFirstReaderIOK

func ChainFirstReaderIOK[E, R, A, B any](f readerio.Kleisli[R, A, B]) Operator[R, E, A, A]

func ChainFirstReaderK

func ChainFirstReaderK[E, R, A, B any](f reader.Kleisli[R, A, B]) Operator[R, E, A, A]

ChainReaderK returns a function that chains a Reader-returning function into ReaderIOEither. This is the curried version of MonadChainReaderK.

func ChainIOEitherK

func ChainIOEitherK[R, E, A, B any](f IOE.Kleisli[E, A, B]) Operator[R, E, A, B]

ChainIOEitherK returns a function that chains an IOEither-returning function into ReaderIOEither. This is the curried version of MonadChainIOEitherK.

func ChainIOK

func ChainIOK[R, E, A, B any](f io.Kleisli[A, B]) Operator[R, E, A, B]

ChainIOK returns a function that chains an IO-returning function into ReaderIOEither. This is the curried version of MonadChainIOK.

func ChainReaderEitherK

func ChainReaderEitherK[E, R, A, B any](f RE.Kleisli[R, E, A, B]) Operator[R, E, A, B]

ChainReaderK returns a function that chains a Reader-returning function into ReaderIOEither. This is the curried version of MonadChainReaderK.

func ChainReaderIOK

func ChainReaderIOK[E, R, A, B any](f readerio.Kleisli[R, A, B]) Operator[R, E, A, B]

func ChainReaderK

func ChainReaderK[E, R, A, B any](f reader.Kleisli[R, A, B]) Operator[R, E, A, B]

ChainReaderK returns a function that chains a Reader-returning function into ReaderIOEither. This is the curried version of MonadChainReaderK.

func Delay

func Delay[R, E, A any](delay time.Duration) Operator[R, E, A, A]

Delay creates an operation that passes in the value after some delay

func Let

func Let[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	f func(S1) T,
) Operator[R, E, S1, S2]

Let attaches the result of a computation to a context [S1] to produce a context [S2]

func LetL

func LetL[R, E, S, T any](
	lens L.Lens[S, T],
	f func(T) T,
) Operator[R, E, S, 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
    Posts  []Post
}

userLens := lens.MakeLens(
    func(s State) User { return s.User },
    func(s State, u User) State { s.User = u; return s },
)

result := F.Pipe2(
    readerioeither.Do[any, error](State{User: User{Name: "Alice"}}),
    readerioeither.LetL(userLens, func(user User) User {
        user.Name = "Bob"
        return user
    }),
)

func LetTo

func LetTo[R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	b T,
) Operator[R, E, S1, S2]

LetTo attaches the a value to a context [S1] to produce a context [S2]

func LetToL

func LetToL[R, E, S, T any](
	lens L.Lens[S, T],
	b T,
) Operator[R, E, S, 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
    Posts  []Post
}

userLens := lens.MakeLens(
    func(s State) User { return s.User },
    func(s State, u User) State { s.User = u; return s },
)

newUser := User{Name: "Bob", ID: 123}
result := F.Pipe2(
    readerioeither.Do[any, error](State{}),
    readerioeither.LetToL(userLens, newUser),
)

func Map

func Map[R, E, A, B any](f func(A) B) Operator[R, E, A, B]

Map returns a function that applies a transformation to the success value of a ReaderIOEither. This is the curried version of MonadMap, useful for function composition.

func MapTo

func MapTo[R, E, A, B any](b B) Operator[R, E, A, B]

MapTo returns a function that replaces the success value with a constant. This is the curried version of MonadMapTo.

func Tap

func Tap[R, E, A, B any](f Kleisli[R, E, A, B]) Operator[R, E, A, A]

func TapEitherK

func TapEitherK[R, E, A, B any](f either.Kleisli[E, A, B]) Operator[R, E, A, A]

func TapIOK

func TapIOK[R, E, A, B any](f io.Kleisli[A, B]) Operator[R, E, A, A]

func TapLeft

func TapLeft[A, R, EA, EB, B any](f Kleisli[R, EB, EA, B]) Operator[R, EA, A, A]

func TapReaderEitherK

func TapReaderEitherK[E, R, A, B any](f RE.Kleisli[R, E, A, B]) Operator[R, E, A, A]

func TapReaderIOK

func TapReaderIOK[E, R, A, B any](f readerio.Kleisli[R, A, B]) Operator[R, E, A, A]

func TapReaderK

func TapReaderK[E, R, A, B any](f reader.Kleisli[R, A, B]) Operator[R, E, A, A]

func WithLock

func WithLock[R, E, A any](lock func() context.CancelFunc) Operator[R, E, A, A]

WithLock executes a ReaderIOEither operation within the scope of a lock. The lock is acquired before the operation executes and released after it completes, regardless of whether the operation succeeds or fails.

This is useful for ensuring thread-safe access to shared resources or for implementing critical sections in concurrent code.

Type parameters:

  • R: The context type
  • E: The error type
  • A: The value type

Parameters:

  • lock: A function that acquires a lock and returns a CancelFunc to release it

Returns:

An Operator that wraps the computation with lock acquisition and release

Example:

var mu sync.Mutex
safeFetch := F.Pipe1(
    fetchData(),
    WithLock[Config, error, Data](func() context.CancelFunc {
        mu.Lock()
        return func() { mu.Unlock() }
    }),
)

type Option

type Option[A any] = option.Option[A]

type Reader

type Reader[R, A any] = reader.Reader[R, A]

Reader represents a computation that depends on some context/environment of type R and produces a value of type A. It's useful for dependency injection patterns.

type ReaderEither

type ReaderEither[R, E, A any] = readereither.ReaderEither[R, E, A]

type ReaderIO

type ReaderIO[R, A any] = readerio.ReaderIO[R, A]

ReaderIO represents a computation that depends on some context R and performs side effects to produce a value of type A.

func MonadFold

func MonadFold[R, E, A, B any](ma ReaderIOEither[R, E, A], onLeft func(E) ReaderIO[R, B], onRight func(A) ReaderIO[R, B]) ReaderIO[R, B]

type ReaderIOEither

type ReaderIOEither[R, E, A any] = Reader[R, IOEither[E, A]]

ReaderIOEither represents a computation that:

  • Depends on some context/environment of type R (Reader)
  • Performs side effects (IO)
  • Can fail with an error of type E or succeed with a value of type A (Either)

It combines three powerful functional programming concepts:

  1. Reader monad for dependency injection
  2. IO monad for side effects
  3. Either monad for error handling

Type parameters:

  • R: The type of the context/environment (e.g., configuration, dependencies)
  • E: The type of errors that can occur
  • A: The type of the success value

Example:

type Config struct { BaseURL string }
func fetchUser(id int) ReaderIOEither[Config, error, User] {
    return func(cfg Config) IOEither[error, User] {
        return func() Either[error, User] {
            // Use cfg.BaseURL to fetch user
            // Return either.Right(user) or either.Left(err)
        }
    }
}

func Ask

func Ask[R, E any]() ReaderIOEither[R, E, R]

Ask returns a ReaderIOEither that retrieves the current context. Useful for accessing configuration or dependencies.

func Asks

func Asks[E, R, A any](r Reader[R, A]) ReaderIOEither[R, E, A]

Asks returns a ReaderIOEither that retrieves a value derived from the context. This is useful for extracting specific fields from a configuration object.

func Bracket

func Bracket[
	R, E, A, B, ANY any](

	acquire ReaderIOEither[R, E, A],
	use func(A) ReaderIOEither[R, E, B],
	release func(A, either.Either[E, B]) ReaderIOEither[R, E, ANY],
) ReaderIOEither[R, E, B]

Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of whether the body action returns and error or not.

func Defer

func Defer[R, E, A any](gen L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A]

Defer creates a ReaderIOEither lazily via a generator function. The generator is called each time the ReaderIOEither is executed.

func Do

func Do[R, E, S any](
	empty S,
) ReaderIOEither[R, E, S]

Do creates an empty context of type [S] to be used with the Bind operation. This is the starting point for do-notation style composition.

Example:

type State struct {
    User   User
    Posts  []Post
}
type Env struct {
    UserRepo UserRepository
    PostRepo PostRepository
}
result := readerioeither.Do[Env, error](State{})

func Flatten

func Flatten[R, E, A any](mma ReaderIOEither[R, E, ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A]

Flatten removes one level of nesting from a nested ReaderIOEither. Converts ReaderIOEither[R, E, ReaderIOEither[R, E, A]] to ReaderIOEither[R, E, A].

func FromEither

func FromEither[R, E, A any](t either.Either[E, A]) ReaderIOEither[R, E, A]

FromEither lifts an Either into a ReaderIOEither context. The Either value is independent of any context or IO effects.

func FromIO

func FromIO[R, E, A any](ma IO[A]) ReaderIOEither[R, E, A]

FromIO lifts an IO into a ReaderIOEither context. The IO result is placed in the Right side (success).

func FromIOEither

func FromIOEither[R, E, A any](ma IOEither[E, A]) ReaderIOEither[R, E, A]

FromIOEither lifts an IOEither into a ReaderIOEither context. The computation becomes independent of any reader context.

func FromReader

func FromReader[E, R, A any](ma Reader[R, A]) ReaderIOEither[R, E, A]

FromReader lifts a Reader into a ReaderIOEither context. The Reader result is placed in the Right side (success).

func FromReaderEither

func FromReaderEither[R, E, A any](ma RE.ReaderEither[R, E, A]) ReaderIOEither[R, E, A]

FromReaderEither lifts a ReaderEither into a ReaderIOEither context. The Either result is lifted into an IO effect.

func FromReaderIO

func FromReaderIO[E, R, A any](ma ReaderIO[R, A]) ReaderIOEither[R, E, A]

func Left

func Left[R, A, E any](e E) ReaderIOEither[R, E, A]

Left creates a failed ReaderIOEither with the given error.

func LeftIO

func LeftIO[R, A, E any](ma IO[E]) ReaderIOEither[R, E, A]

LeftIO lifts an IO into a ReaderIOEither, placing the result in the Left (error) side.

func LeftReader

func LeftReader[A, R, E any](ma Reader[R, E]) ReaderIOEither[R, E, A]

LeftReader lifts a Reader into a ReaderIOEither, placing the result in the Left (error) side.

func LeftReaderIO

func LeftReaderIO[A, R, E any](me ReaderIO[R, E]) ReaderIOEither[R, E, A]

LeftReaderIO lifts a ReaderIO into a ReaderIOEither, placing the result in the Left (error) side.

func Memoize

func Memoize[
	R, E, A any](rdr ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A]

Memoize computes the value of the ReaderIOEither lazily but exactly once. The context used is from the first call. Do not use if the value depends on the context.

func MonadAlt

func MonadAlt[R, E, A any](first ReaderIOEither[R, E, A], second L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A]

MonadAlt tries the first computation, and if it fails, tries the second. This implements the Alternative pattern for error recovery.

func MonadAp

func MonadAp[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B]

MonadAp applies a function wrapped in a context to a value wrapped in a context. Both computations are executed (default behavior may be sequential or parallel depending on implementation).

func MonadApPar

func MonadApPar[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B]

MonadApPar applies a function in a context to a value in a context, executing them in parallel.

func MonadApSeq

func MonadApSeq[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B]

MonadApSeq applies a function in a context to a value in a context, executing them sequentially.

func MonadBiMap

func MonadBiMap[R, E1, E2, A, B any](fa ReaderIOEither[R, E1, A], f func(E1) E2, g func(A) B) ReaderIOEither[R, E2, B]

MonadBiMap applies two functions: one to the error, one to the success value. This allows transforming both channels simultaneously.

func MonadChain

func MonadChain[R, E, A, B any](fa ReaderIOEither[R, E, A], f Kleisli[R, E, A, B]) ReaderIOEither[R, E, B]

MonadChain sequences two computations where the second depends on the result of the first. This is the fundamental operation for composing dependent effectful computations. If the first computation fails, the second is not executed.

func MonadChainEitherK

func MonadChainEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f either.Kleisli[E, A, B]) ReaderIOEither[R, E, B]

MonadChainEitherK chains a computation that returns an Either into a ReaderIOEither. The Either is automatically lifted into the ReaderIOEither context.

func MonadChainFirst

func MonadChainFirst[R, E, A, B any](fa ReaderIOEither[R, E, A], f Kleisli[R, E, A, B]) ReaderIOEither[R, E, A]

MonadChainFirst sequences two computations but keeps the result of the first. Useful for performing side effects while preserving the original value.

func MonadChainFirstEitherK

func MonadChainFirstEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f either.Kleisli[E, A, B]) ReaderIOEither[R, E, A]

MonadChainFirstEitherK chains an Either-returning computation but keeps the original value. Useful for validation or side effects that return Either.

func MonadChainFirstIOK

func MonadChainFirstIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f io.Kleisli[A, B]) ReaderIOEither[R, E, A]

MonadChainFirstIOK chains an IO computation but keeps the original value. Useful for performing IO side effects while preserving the original value.

func MonadChainFirstLeft

func MonadChainFirstLeft[A, R, EA, EB, B any](ma ReaderIOEither[R, EA, A], f Kleisli[R, EB, EA, B]) ReaderIOEither[R, EA, A]

MonadChainFirstLeft chains a computation on the left (error) side but always returns the original error. If the input is a Left value, it applies the function f to the error and executes the resulting computation, but always returns the original Left error regardless of what f returns (Left or Right). If the input is a Right value, it passes through unchanged without calling f.

This is useful for side effects on errors (like logging or metrics) where you want to perform an action when an error occurs but always propagate the original error, ensuring the error path is preserved.

Parameters:

  • ma: The input ReaderIOEither that may contain an error of type EA
  • f: A function that takes an error of type EA and returns a ReaderIOEither (typically for side effects)

Returns:

  • A ReaderIOEither with the original error preserved if input was Left, or the original Right value

func MonadChainFirstReaderEitherK

func MonadChainFirstReaderEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f RE.Kleisli[R, E, A, B]) ReaderIOEither[R, E, A]

func MonadChainFirstReaderIOK

func MonadChainFirstReaderIOK[E, R, A, B any](ma ReaderIOEither[R, E, A], f readerio.Kleisli[R, A, B]) ReaderIOEither[R, E, A]

func MonadChainFirstReaderK

func MonadChainFirstReaderK[E, R, A, B any](ma ReaderIOEither[R, E, A], f reader.Kleisli[R, A, B]) ReaderIOEither[R, E, A]

func MonadChainIOEitherK

func MonadChainIOEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f IOE.Kleisli[E, A, B]) ReaderIOEither[R, E, B]

MonadChainIOEitherK chains an IOEither-returning computation into a ReaderIOEither. The IOEither is automatically lifted into the ReaderIOEither context.

func MonadChainIOK

func MonadChainIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f io.Kleisli[A, B]) ReaderIOEither[R, E, B]

MonadChainIOK chains an IO-returning computation into a ReaderIOEither. The IO is automatically lifted into the ReaderIOEither context (always succeeds).

func MonadChainLeft

func MonadChainLeft[R, EA, EB, A any](fa ReaderIOEither[R, EA, A], f Kleisli[R, EB, EA, A]) ReaderIOEither[R, EB, A]

func MonadChainReaderEitherK

func MonadChainReaderEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f RE.Kleisli[R, E, A, B]) ReaderIOEither[R, E, B]

func MonadChainReaderIOK

func MonadChainReaderIOK[E, R, A, B any](ma ReaderIOEither[R, E, A], f readerio.Kleisli[R, A, B]) ReaderIOEither[R, E, B]

func MonadChainReaderK

func MonadChainReaderK[E, R, A, B any](ma ReaderIOEither[R, E, A], f reader.Kleisli[R, A, B]) ReaderIOEither[R, E, B]

MonadChainReaderK chains a Reader-returning computation into a ReaderIOEither. The Reader is automatically lifted into the ReaderIOEither context.

func MonadFlap

func MonadFlap[R, E, B, A any](fab ReaderIOEither[R, E, func(A) B], a A) ReaderIOEither[R, E, B]

MonadFlap applies a value to a function wrapped in a context. This is the reverse of Ap - the value is fixed and the function varies.

func MonadMap

func MonadMap[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) B) ReaderIOEither[R, E, B]

MonadMap applies a function to the value inside a ReaderIOEither context. If the computation is successful (Right), the function is applied to the value. If it's an error (Left), the error is propagated unchanged.

func MonadMapLeft

func MonadMapLeft[R, E1, E2, A any](fa ReaderIOEither[R, E1, A], f func(E1) E2) ReaderIOEither[R, E2, A]

MonadMapLeft applies a function to the error value, leaving success unchanged.

func MonadMapTo

func MonadMapTo[R, E, A, B any](fa ReaderIOEither[R, E, A], b B) ReaderIOEither[R, E, B]

MonadMapTo replaces the success value with a constant value. Useful when you want to discard the result but keep the effect.

func MonadReduceArray

func MonadReduceArray[R, E, A, B any](as []ReaderIOEither[R, E, A], reduce func(B, A) B, initial B) ReaderIOEither[R, E, B]

func MonadReduceArrayM

func MonadReduceArrayM[R, E, A any](as []ReaderIOEither[R, E, A], m monoid.Monoid[A]) ReaderIOEither[R, E, A]

func MonadTap

func MonadTap[R, E, A, B any](fa ReaderIOEither[R, E, A], f Kleisli[R, E, A, B]) ReaderIOEither[R, E, A]

func MonadTapEitherK

func MonadTapEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f either.Kleisli[E, A, B]) ReaderIOEither[R, E, A]

func MonadTapIOK

func MonadTapIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f io.Kleisli[A, B]) ReaderIOEither[R, E, A]

func MonadTapLeft

func MonadTapLeft[A, R, EA, EB, B any](ma ReaderIOEither[R, EA, A], f Kleisli[R, EB, EA, B]) ReaderIOEither[R, EA, A]

func MonadTapReaderEitherK

func MonadTapReaderEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f RE.Kleisli[R, E, A, B]) ReaderIOEither[R, E, A]

func MonadTapReaderIOK

func MonadTapReaderIOK[E, R, A, B any](ma ReaderIOEither[R, E, A], f readerio.Kleisli[R, A, B]) ReaderIOEither[R, E, A]

func MonadTapReaderK

func MonadTapReaderK[E, R, A, B any](ma ReaderIOEither[R, E, A], f reader.Kleisli[R, A, B]) ReaderIOEither[R, E, A]

func MonadTraverseReduceArray

func MonadTraverseReduceArray[R, E, A, B, C any](as []A, trfrm Kleisli[R, E, A, B], reduce func(C, B) C, initial C) ReaderIOEither[R, E, C]

func MonadTraverseReduceArrayM

func MonadTraverseReduceArrayM[R, E, A, B any](as []A, trfrm Kleisli[R, E, A, B], m monoid.Monoid[B]) ReaderIOEither[R, E, B]

func Of

func Of[R, E, A any](a A) ReaderIOEither[R, E, A]

Of creates a successful ReaderIOEither with the given value. This is the pointed functor operation, lifting a pure value into the ReaderIOEither context.

func Retrying

func Retrying[R, E, A any](
	policy retry.RetryPolicy,
	action Kleisli[R, E, retry.RetryStatus, A],
	check func(Either[E, A]) bool,
) ReaderIOEither[R, E, A]
func Right[R, E, A any](a A) ReaderIOEither[R, E, A]

Right creates a successful ReaderIOEither with the given value.

func RightIO

func RightIO[R, E, A any](ma IO[A]) ReaderIOEither[R, E, A]

RightIO lifts an IO into a ReaderIOEither, placing the result in the Right side.

func RightReader

func RightReader[E, R, A any](ma Reader[R, A]) ReaderIOEither[R, E, A]

RightReader lifts a Reader into a ReaderIOEither, placing the result in the Right side.

func RightReaderIO

func RightReaderIO[E, R, A any](ma ReaderIO[R, A]) ReaderIOEither[R, E, A]

RightReaderIO lifts a ReaderIO into a ReaderIOEither, placing the result in the Right side.

func SequenceArray

func SequenceArray[R, E, A any](ma []ReaderIOEither[R, E, A]) ReaderIOEither[R, E, []A]

SequenceArray converts an array of ReaderIOEither into a ReaderIOEither of an array.

This is useful when you have multiple independent computations and want to execute them all and collect their results. If any computation fails, the entire operation fails with the first error.

Type parameters:

  • R: The context type
  • E: The error type
  • A: The element type

Parameters:

  • ma: An array of ReaderIOEither computations

Returns:

A ReaderIOEither that produces an array of results

Example:

computations := []ReaderIOEither[Config, error, int]{
    fetchCount("users"),
    fetchCount("posts"),
    fetchCount("comments"),
}
result := SequenceArray(computations)
// result(cfg)() returns Right([userCount, postCount, commentCount]) or Left(error)
Example

Example of SequenceArray - execute multiple independent computations

package main

import (
	"fmt"

	E "github.com/IBM/fp-go/v2/either"

	RIE "github.com/IBM/fp-go/v2/readerioeither"
)

type Config struct {
	APIKey  string
	BaseURL string
}

func main() {
	cfg := Config{APIKey: "secret"}

	computations := []RIE.ReaderIOEither[Config, error, int]{
		RIE.Of[Config, error](10),
		RIE.Of[Config, error](20),
		RIE.Of[Config, error](30),
	}

	result := RIE.SequenceArray(computations)(cfg)()

	E.Fold(
		func(err error) string {
			fmt.Printf("Error: %v\n", err)
			return ""
		},
		func(values []int) string {
			sum := 0
			for _, v := range values {
				sum += v
			}
			fmt.Printf("Sum: %d\n", sum)
			return ""
		},
	)(result)
}
Output:

Sum: 60

func SequenceRecord

func SequenceRecord[K comparable, R, E, A any](ma map[K]ReaderIOEither[R, E, A]) ReaderIOEither[R, E, map[K]A]

SequenceRecord converts a map of ReaderIOEither into a ReaderIOEither of a map.

This is useful when you have multiple independent computations keyed by some identifier and want to execute them all and collect their results. If any computation fails, the entire operation fails with the first error.

Type parameters:

  • R: The context type
  • K: The key type (must be comparable)
  • E: The error type
  • A: The value type

Parameters:

  • ma: A map of ReaderIOEither computations

Returns:

A ReaderIOEither that produces a map of results

Example:

computations := map[string]ReaderIOEither[Config, error, int]{
    "users": fetchCount("users"),
    "posts": fetchCount("posts"),
}
result := SequenceRecord(computations)
// result(cfg)() returns Right(map[string]int{"users": 100, "posts": 50}) or Left(error)

func SequenceT1

func SequenceT1[R, E, A any](a ReaderIOEither[R, E, A]) ReaderIOEither[R, E, T.Tuple1[A]]

SequenceT1 converts a single ReaderIOEither into a ReaderIOEither of a 1-tuple. This is useful for uniformly handling computations with different arities.

If the input computation fails, the result will be a Left with the error. If it succeeds, the result will be a Right with a tuple containing the value.

Example:

result := SequenceT1(Of[Config, error](42))
// result(cfg)() returns Right(Tuple1{42})

func SequenceT2

func SequenceT2[R, E, A, B any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B]) ReaderIOEither[R, E, T.Tuple2[A, B]]

SequenceT2 combines two ReaderIOEither computations into a single ReaderIOEither of a 2-tuple. Both computations are executed, and if both succeed, their results are combined into a tuple. If either fails, the result is a Left with the first error encountered.

This is useful for running multiple independent computations and collecting their results.

Example:

result := SequenceT2(
    fetchUser(123),
    fetchProfile(123),
)
// result(cfg)() returns Right(Tuple2{user, profile}) or Left(error)

func SequenceT3

func SequenceT3[R, E, A, B, C any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C]) ReaderIOEither[R, E, T.Tuple3[A, B, C]]

SequenceT3 combines three ReaderIOEither computations into a single ReaderIOEither of a 3-tuple. All three computations are executed, and if all succeed, their results are combined into a tuple. If any fails, the result is a Left with the first error encountered.

Example:

result := SequenceT3(
    fetchUser(123),
    fetchProfile(123),
    fetchSettings(123),
)
// result(cfg)() returns Right(Tuple3{user, profile, settings}) or Left(error)

func SequenceT4

func SequenceT4[R, E, A, B, C, D any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C], d ReaderIOEither[R, E, D]) ReaderIOEither[R, E, T.Tuple4[A, B, C, D]]

SequenceT4 combines four ReaderIOEither computations into a single ReaderIOEither of a 4-tuple. All four computations are executed, and if all succeed, their results are combined into a tuple. If any fails, the result is a Left with the first error encountered.

Example:

result := SequenceT4(
    fetchUser(123),
    fetchProfile(123),
    fetchSettings(123),
    fetchPreferences(123),
)
// result(cfg)() returns Right(Tuple4{user, profile, settings, prefs}) or Left(error)

func Swap

func Swap[R, E, A any](val ReaderIOEither[R, E, A]) ReaderIOEither[R, A, E]

Swap exchanges the error and success types. Left becomes Right and Right becomes Left.

func ThrowError

func ThrowError[R, A, E any](e E) ReaderIOEither[R, E, A]

ThrowError creates a failed ReaderIOEither with the given error. This is an alias for Left, following the naming convention from other functional libraries.

func TryCatch

func TryCatch[R, E, A any](f func(R) func() (A, error), onThrow func(error) E) ReaderIOEither[R, E, A]

TryCatch wraps a function that returns (value, error) into a ReaderIOEither. The onThrow function converts the error into the desired error type.

type ReaderOption

type ReaderOption[R, A any] = readeroption.ReaderOption[R, A]

Directories

Path Synopsis
Code generated by go generate; DO NOT EDIT.
Code generated by go generate; DO NOT EDIT.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL