readerresult

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: 21 Imported by: 0

Documentation

Overview

Package readerresult provides a ReaderResult monad that combines the Reader and Result monads.

Fantasy Land Specification

This is a monad transformer combining:

Implemented Fantasy Land algebras:

A ReaderResult[R, A] represents a computation that:

  • Depends on an environment of type R (Reader aspect)
  • May fail with an error (Result aspect, which is Either[error, A])

This is equivalent to Reader[R, Result[A]] or Reader[R, Either[error, A]].

Use Cases

ReaderResult is particularly useful for:

  1. Dependency injection with error handling - pass configuration/services through computations that may fail
  2. Functional error handling - compose operations that depend on context and may error
  3. Testing - easily mock dependencies by changing the environment value

Basic Example

type Config struct {
    DatabaseURL string
}

// Function that needs config and may fail
func getUser(id int) readerresult.ReaderResult[Config, User] {
    return readerresult.Asks(func(cfg Config) result.Result[User] {
        // Use cfg.DatabaseURL to fetch user
        return result.Of(user)
    })
}

// Execute by providing the config
cfg := Config{DatabaseURL: "postgres://..."}
res := getUser(42)(cfg)  // Returns result.Result[User]

Composition

ReaderResult provides several ways to compose computations:

  1. Map - transform successful values
  2. Chain (FlatMap) - sequence dependent operations
  3. Ap - combine independent computations
  4. Do-notation - imperative-style composition with Bind

Do-Notation Example

type State struct {
    User   User
    Posts  []Post
}

result := F.Pipe2(
    readerresult.Do[Config](State{}),
    readerresult.Bind(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        func(s State) readerresult.ReaderResult[Config, User] {
            return getUser(42)
        },
    ),
    readerresult.Bind(
        func(posts []Post) func(State) State {
            return func(s State) State { s.Posts = posts; return s }
        },
        func(s State) readerresult.ReaderResult[Config, []Post] {
            return getPosts(s.User.ID)
        },
    ),
)

Error Handling

ReaderResult provides several functions for error handling:

  • Left/Right - create failed/successful values
  • GetOrElse - provide a default value for errors
  • OrElse - recover from errors with an alternative computation
  • Fold - handle both success and failure cases
  • ChainEitherK - lift result.Result computations into ReaderResult

Relationship to Other Monads

ReaderResult is related to several other monads in this library:

  • Reader[R, A] - ReaderResult without error handling
  • Result[A] (Either[error, A]) - error handling without environment
  • ReaderEither[R, E, A] - like ReaderResult but with custom error type E
  • IOResult[A] - like ReaderResult but with no environment (IO with errors)

Performance Note

ReaderResult is a zero-cost abstraction - it compiles to a simple function type with no runtime overhead beyond the underlying computation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApEitherIS

func ApEitherIS[
	R, S1, S2, T any](
	setter func(T) func(S1) S2,
) func(T, error) Operator[R, S1, S2]

ApEitherIS attaches a value from an idiomatic (value, error) pair to a context [S1] to produce a context [S2]. This is the idiomatic version of ApEitherS, accepting Go's native error handling pattern. It uses Applicative semantics (independent, non-sequential composition).

Example:

type State struct {
    Value1 int
    Value2 int
}

// Idiomatic parsing result
value, err := strconv.Atoi("42")

computation := F.Pipe1(
    readerresult.Do[context.Context](State{}),
    readerresult.ApEitherIS[context.Context](
        func(v int) func(State) State {
            return func(s State) State { s.Value1 = v; return s }
        },
    )(value, err),
)

func ApResultIS

func ApResultIS[
	R, S1, S2, T any](
	setter func(T) func(S1) S2,
) func(T, error) Operator[R, S1, S2]

ApResultIS is an alias for ApEitherIS. It attaches a value from an idiomatic (value, error) pair to a context [S1] to produce a context [S2].

func BindToEither

func BindToEither[
	R, S1, T any](
	setter func(T) S1,
) func(Result[T]) ReaderResult[R, S1]

func BindToEitherI

func BindToEitherI[
	R, S1, T any](
	setter func(T) S1,
) func(T, error) ReaderResult[R, S1]

BindToEitherI initializes a new state S1 from an idiomatic (value, error) pair. This is the idiomatic version of BindToEither, accepting Go's native error handling pattern. It's used to start a ReaderResult computation chain from an idiomatic Result that may contain an error.

Example:

type State struct {
    Value int
}

// Idiomatic result from parsing
value, err := strconv.Atoi("42")

computation := readerresult.BindToEitherI[context.Context](
    func(value int) State {
        return State{Value: value}
    },
)(value, err)

func BindToReader

func BindToReader[
	R, S1, T any](
	setter func(T) S1,
) func(Reader[R, T]) ReaderResult[R, S1]

BindToReader initializes a new state S1 from a Reader[R, T] computation. This is used to start a ReaderResult computation chain from a pure Reader value.

The setter function takes the result T from the Reader and initializes the state S1. This is useful when you want to begin a do-notation chain with a Reader computation that doesn't involve error handling.

Example:

type Env struct {
    ConfigPath string
}
type State struct {
    Config string
}

// A Reader that just reads from the environment
getConfigPath := func(env Env) string {
    return env.ConfigPath
}

result := F.Pipe1(
    reader.Of[Env](getConfigPath),
    readerresult.BindToReader(func(path string) State {
        return State{Config: path}
    }),
)

func BindToResult

func BindToResult[
	R, S1, T any](
	setter func(T) S1,
) func(Result[T]) ReaderResult[R, S1]

BindToResult initializes a new state S1 from a Result[T] value. This is used to start a ReaderResult computation chain from a Result that may contain an error.

The setter function takes the successful result T and initializes the state S1. If the Result is an error, the entire computation will carry that error forward. This is useful when you want to begin a do-notation chain with a Result computation that doesn't need environment access.

Example:

type State struct {
    Value int
}

// A Result that might contain an error
parseResult := result.TryCatch(func() int {
    // some parsing logic that might fail
    return 42
})

computation := F.Pipe1(
    parseResult,
    readerresult.BindToResult[any](func(value int) State {
        return State{Value: value}
    }),
)

func BindToResultI

func BindToResultI[
	R, S1, T any](
	setter func(T) S1,
) func(T, error) ReaderResult[R, S1]

BindToResultI is an alias for BindToEitherI. It initializes a new state S1 from an idiomatic (value, error) pair.

func ChainOptionIK

func ChainOptionIK[R, A, B any](onNone Lazy[error]) func(OI.Kleisli[A, B]) Operator[R, A, B]

ChainOptionIK chains with an idiomatic function that returns (Option[B], error), converting None to an error. This is the idiomatic version of ChainOptionK, accepting functions in Go's native error handling pattern. The onNone function is called when the Option is None to generate an error.

Example:

// Idiomatic function returning (Option[User], error)
findUser := func(id int) (option.Option[User], error) {
    user, err := db.Query(id)
    if err != nil {
        return option.None[User](), err
    }
    if user == nil {
        return option.None[User](), nil
    }
    return option.Some(*user), nil
}
notFound := func() error { return errors.New("user not found") }
chain := readerresult.ChainOptionIK[Config, int, User](notFound)
result := F.Pipe1(readerresult.Of[Config](42), chain(findUser))

func ChainOptionK

func ChainOptionK[R, A, B any](onNone Lazy[error]) func(option.Kleisli[A, B]) Operator[R, A, B]

ChainOptionK chains with a function that returns an Option, converting None to an error. This is useful for integrating functions that return optional values.

Example:

findUser := func(id int) option.Option[User] { ... }
notFound := func() error { return errors.New("user not found") }
chain := readerresult.ChainOptionK[Config, int, User](notFound)
result := F.Pipe1(readerresult.Of[Config](42), chain(findUser))

func Curry1

func Curry1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderResult[R, A]

Curry1 converts a function with one parameter into a curried function returning a ReaderResult.

Example:

getUser := func(ctx context.Context, id int) (User, error) { ... }
curried := readerresult.Curry1(getUser)
// curried(42) returns ReaderResult[context.Context, User]

func Curry2

func Curry2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1) func(T2) ReaderResult[R, A]

Curry2 converts a function with two parameters into a fully curried function. Each parameter is applied one at a time.

Example:

queryDB := func(ctx context.Context, table string, id int) (Record, error) { ... }
curried := readerresult.Curry2(queryDB)
// curried("users")(42) returns ReaderResult[context.Context, Record]

func Curry3

func Curry3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderResult[R, A]

Curry3 converts a function with three parameters into a fully curried function.

Example:

updateRecord := func(ctx context.Context, table string, id int, data string) (Result, error) { ... }
curried := readerresult.Curry3(updateRecord)
// curried("users")(42)("data") returns ReaderResult[context.Context, Result]

func Fold

func Fold[R, A, B any](onLeft reader.Kleisli[R, error, B], onRight reader.Kleisli[R, A, B]) func(ReaderResult[R, A]) Reader[R, B]

Fold handles both success and failure cases by providing functions for each. The result is always a Reader[R, B] (without the error channel). This is useful for converting a ReaderResult into a plain Reader by handling the error case.

Example:

handleError := func(err error) reader.Reader[Config, string] {
    return func(cfg Config) string { return "Error: " + err.Error() }
}
handleSuccess := func(user User) reader.Reader[Config, string] {
    return func(cfg Config) string { return user.Name }
}
result := readerresult.Fold[Config, User, string](handleError, handleSuccess)(getUserRR)

func From0

func From0[R, A any](f func(R) (A, error)) func() ReaderResult[R, A]

From0 converts a function that takes only a context and returns (A, error) into a ReaderResult.

Example:

getConfig := func(ctx context.Context) (Config, error) { ... }
rr := readerresult.From0(getConfig)()
// rr is a ReaderResult[context.Context, Config]

func From1

func From1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderResult[R, A]

From1 converts a function with one parameter into a ReaderResult-returning function. The context parameter is moved to the end (ReaderResult style).

Example:

getUser := func(ctx context.Context, id int) (User, error) { ... }
rr := readerresult.From1(getUser)
// rr(42) returns ReaderResult[context.Context, User]

func From2

func From2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1, T2) ReaderResult[R, A]

From2 converts a function with two parameters into a ReaderResult-returning function. The context parameter is moved to the end (ReaderResult style).

Example:

queryDB := func(ctx context.Context, table string, id int) (Record, error) { ... }
rr := readerresult.From2(queryDB)
// rr("users", 42) returns ReaderResult[context.Context, Record]

func From3

func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderResult[R, A]

From3 converts a function with three parameters into a ReaderResult-returning function. The context parameter is moved to the end (ReaderResult style).

Example:

updateRecord := func(ctx context.Context, table string, id int, data string) (Result, error) { ... }
rr := readerresult.From3(updateRecord)
// rr("users", 42, "data") returns ReaderResult[context.Context, Result]

func GetOrElse

func GetOrElse[R, A any](onLeft reader.Kleisli[R, error, A]) func(ReaderResult[R, A]) Reader[R, A]

GetOrElse extracts the success value or computes a default value from the error. The result is a Reader[R, A] that always succeeds.

Example:

defaultUser := func(err error) reader.Reader[Config, User] {
    return func(cfg Config) User { return User{Name: "Guest"} }
}
result := readerresult.GetOrElse[Config](defaultUser)(getUserRR)

func Local

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

Local changes the environment type during execution of a ReaderResult. This is similar to Contravariant's contramap and allows adapting computations to work with different environment types.

Example:

// Convert DB environment to Config environment
toConfig := func(db DB) Config { return db.Config }
rr := readerresult.Of[Config](42)
adapted := readerresult.Local[int](toConfig)(rr)
// adapted now accepts DB instead of Config

func Read

func Read[A, R any](r R) func(ReaderResult[R, A]) Result[A]

Read applies an environment value to a ReaderResult to execute it and obtain the Result. This is the primary way to "run" a ReaderResult computation.

Example:

rr := readerresult.Asks(func(cfg Config) int { return cfg.Port })
run := readerresult.Read[int](myConfig)
res := run(rr)  // Returns result.Result[int]

func Sequence

func Sequence[R1, R2, A any](ma ReaderResult[R2, ReaderResult[R1, A]]) reader.Kleisli[R2, R1, Result[A]]

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

This function takes a ReaderResult that produces another ReaderResult 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 a Result[A].

Type Parameters:

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

Parameters:

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

Returns:

  • A reader.Kleisli[R2, R1, Result[A]], which is func(R2) func(R1) Result[A]

The function preserves error handling at both levels. Errors from the outer computation become errors in the inner Result.

Note: This is an inline wrapper around readereither.Sequence since ReaderResult is an alias for ReaderEither with error type fixed to error.

Example:

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

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

// Original: takes Config, may fail, produces ReaderResult[Database, string]
original := func(cfg Config) result.Result[ReaderResult[Database, string]] {
    if cfg.Timeout <= 0 {
        return result.Error[ReaderResult[Database, string]](errors.New("invalid timeout"))
    }
    return result.Ok[error](func(db Database) result.Result[string] {
        if S.IsEmpty(db.ConnectionString) {
            return result.Error[string](errors.New("empty connection string"))
        }
        return result.Ok[error](fmt.Sprintf("Query on %s with timeout %d",
            db.ConnectionString, cfg.Timeout))
    })
}

// Sequenced: takes Database first, then Config
sequenced := Sequence(original)

db := Database{ConnectionString: "localhost:5432"}
cfg := Config{Timeout: 30}

// Apply database first to get a function that takes config
configReader := sequenced(db)
// Then apply config to get the final result
result := configReader(cfg)
// result is Result[string]

func SequenceReader

func SequenceReader[R1, R2, A any](ma ReaderResult[R2, Reader[R1, A]]) reader.Kleisli[R2, R1, Result[A]]

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

This function is similar to Sequence but specialized for the case where the innermost computation is a pure Reader (without error handling) rather than another ReaderResult. It takes a ReaderResult that produces a Reader and returns a reader.Kleisli that produces Results.

Type Parameters:

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

Parameters:

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

Returns:

  • A reader.Kleisli[R2, R1, Result[A]], which is func(R2) func(R1) Result[A]

The function preserves error handling from the outer ReaderResult layer. If the outer computation fails, the error is propagated to the inner Result.

Note: This is an inline wrapper around readereither.SequenceReader since ReaderResult is an alias for ReaderEither with error type fixed to error.

Example:

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

// Original: takes Config, may fail, produces Reader[Database, string]
original := func(cfg Config) result.Result[Reader[Database, string]] {
    if cfg.Timeout <= 0 {
        return result.Error[Reader[Database, string]](errors.New("invalid timeout"))
    }
    return result.Ok[error](func(db Database) string {
        return fmt.Sprintf("Query on %s with timeout %d",
            db.ConnectionString, cfg.Timeout)
    })
}

// Sequenced: takes Database first, then Config
sequenced := SequenceReader(original)

db := Database{ConnectionString: "localhost:5432"}
cfg := Config{Timeout: 30}

// Apply database first to get a function that takes config
configReader := sequenced(db)
// Then apply config to get the final result
result := configReader(cfg)
// result is Result[string]

func Traverse

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

func TraverseReader

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

func Uncurry1

func Uncurry1[R, T1, A any](f func(T1) ReaderResult[R, A]) func(R, T1) (A, error)

Uncurry1 converts a ReaderResult-returning function back into an idiomatic Go function. This is useful for adapting functional code to work with traditional Go APIs.

Example:

rrf := func(id int) readerresult.ReaderResult[context.Context, User] { ... }
gofunc := readerresult.Uncurry1(rrf)
// gofunc(ctx, 42) returns (User, error)

func Uncurry2

func Uncurry2[R, T1, T2, A any](f func(T1) func(T2) ReaderResult[R, A]) func(R, T1, T2) (A, error)

Uncurry2 converts a curried two-parameter ReaderResult function into an idiomatic Go function.

Example:

rrf := func(table string) func(int) readerresult.ReaderResult[context.Context, Record] { ... }
gofunc := readerresult.Uncurry2(rrf)
// gofunc(ctx, "users", 42) returns (Record, error)

func Uncurry3

func Uncurry3[R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderResult[R, A]) func(R, T1, T2, T3) (A, error)

Uncurry3 converts a curried three-parameter ReaderResult function into an idiomatic Go function.

Example:

rrf := func(table string) func(int) func(string) readerresult.ReaderResult[context.Context, Result] { ... }
gofunc := readerresult.Uncurry3(rrf)
// gofunc(ctx, "users", 42, "data") returns (Result, error)

Types

type Either

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

type Endomorphism

type Endomorphism[A any] = endomorphism.Endomorphism[A]

type Kleisli

type Kleisli[R, A, B any] = Reader[A, ReaderResult[R, B]]

func FromPredicate

func FromPredicate[R, A any](pred func(A) bool, onFalse func(A) error) Kleisli[R, A, A]

FromPredicate creates a Kleisli arrow that tests a predicate and returns either the input value or an error. If the predicate returns true, the value is returned as a success. If false, the onFalse function is called to generate an error.

Example:

isPositive := readerresult.FromPredicate[Config](
    func(x int) bool { return x > 0 },
    func(x int) error { return fmt.Errorf("%d is not positive", x) },
)
result := isPositive(5)  // Returns ReaderResult that succeeds with 5
result := isPositive(-1) // Returns ReaderResult that fails with error

func TailRec

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

func TraverseArray

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

TraverseArray applies a ReaderResult-returning function to each element of an array, collecting the results. If any element fails, the entire operation fails with the first error.

Example:

parseUser := func(id int) readerresult.ReaderResult[DB, User] { ... }
ids := []int{1, 2, 3}
result := readerresult.TraverseArray[DB](parseUser)(ids)
// result(db) returns result.Result[[]User] with all users or first error

func TraverseArrayWithIndex

func TraverseArrayWithIndex[L, A, B any](f func(int, A) ReaderResult[L, B]) Kleisli[L, []A, []B]

TraverseArrayWithIndex is like TraverseArray but the function also receives the element's index. This is useful when the transformation depends on the position in the array.

Example:

processItem := func(idx int, item string) readerresult.ReaderResult[Config, int] {
    return readerresult.Of[Config](idx + len(item))
}
items := []string{"a", "bb", "ccc"}
result := readerresult.TraverseArrayWithIndex[Config](processItem)(items)

type Lazy

type Lazy[A any] = lazy.Lazy[A]

type Monoid

type Monoid[R, A any] = monoid.Monoid[ReaderResult[R, A]]

func AltMonoid

func AltMonoid[R, A any](zero Lazy[ReaderResult[R, A]]) Monoid[R, A]

AltMonoid creates a Monoid for ReaderResult based on the Alternative pattern. The empty element is the provided zero computation, and concat tries the first computation, falling back to the second if it fails.

This is useful for combining computations where you want to try alternatives until one succeeds.

Example:

zero := func() readerresult.ReaderResult[Config, User] {
    return readerresult.Left[Config, User](errors.New("no user"))
}
userMonoid := readerresult.AltMonoid[Config](zero)

primary := getPrimaryUser()
backup := getBackupUser()
combined := userMonoid.Concat(primary, backup)
// Tries primary, falls back to backup if primary fails

func AlternativeMonoid

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

AlternativeMonoid creates a Monoid for ReaderResult that combines both Alternative and Applicative behavior. It uses the provided monoid for the success values and falls back to alternative computations on failure.

The empty element is Of(m.Empty()), and concat tries the first computation, falling back to the second if it fails, then combines successful values using the underlying monoid.

Example:

intAdd := monoid.MakeMonoid(0, func(a, b int) int { return a + b })
rrMonoid := readerresult.AlternativeMonoid[Config](intAdd)

rr1 := readerresult.Of[Config](5)
rr2 := readerresult.Of[Config](3)
combined := rrMonoid.Concat(rr1, rr2)
// combined(cfg) returns result.Of(8)

func ApplicativeMonoid

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

ApplicativeMonoid creates a Monoid for ReaderResult based on Applicative functor composition. The empty element is Of(m.Empty()), and concat combines two computations using the underlying monoid. Both computations must succeed for the result to succeed.

This is useful for accumulating results from multiple independent computations.

Example:

intAdd := monoid.MakeMonoid(0, func(a, b int) int { return a + b })
rrMonoid := readerresult.ApplicativeMonoid[Config](intAdd)

rr1 := readerresult.Of[Config](5)
rr2 := readerresult.Of[Config](3)
combined := rrMonoid.Concat(rr1, rr2)
// combined(cfg) returns result.Of(8)

// If either fails, the whole computation fails
rr3 := readerresult.Left[Config, int](errors.New("error"))
failed := rrMonoid.Concat(rr1, rr3)
// failed(cfg) returns result.Left[int](error)

type Operator

type Operator[R, A, B any] = Kleisli[R, ReaderResult[R, A], B]

func Alt

func Alt[R, A any](second Lazy[ReaderResult[R, A]]) Operator[R, A, A]

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

func AltI

func AltI[R, A any](second Lazy[RRI.ReaderResult[R, A]]) Operator[R, A, A]

AltI is the curried version of MonadAltI. It tries the first computation, and if it fails, tries the idiomatic alternative that returns (A, error).

Example:

// Idiomatic alternative returning (int, error)
alternative := func() func(cfg Config) (int, error) {
    return func(cfg Config) (int, error) {
        return 42, nil
    }
}
result := F.Pipe1(primary, readerresult.AltI[Config](alternative))

func Ap

func Ap[B, R, A any](fa ReaderResult[R, A]) Operator[R, func(A) B, B]

Ap is the curried version of MonadAp. It returns an Operator that can be used in function composition pipelines.

func ApEitherS

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

func ApI

func ApI[B, R, A any](fa RRI.ReaderResult[R, A]) Operator[R, func(A) B, B]

ApI is the curried version of MonadApI. It allows applying to idiomatic ReaderResult values that return (A, error).

Example:

// Idiomatic computation returning (int, error)
fa := func(cfg Config) (int, error) { return cfg.Port, nil }
result := F.Pipe1(fabr, readerresult.ApI[int, Config](fa))

func ApIS

func ApIS[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa RRI.ReaderResult[R, T],
) Operator[R, S1, S2]

ApIS attaches a value from an idiomatic ReaderResult to a context [S1] to produce a context [S2]. This is the idiomatic version of ApS, where the computation returns (T, error) instead of Result[T]. Unlike BindI which sequences operations, ApIS uses applicative semantics, meaning the computation is independent of the current state and can conceptually run in parallel.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService UserService
}

// Idiomatic independent computation returning (User, error)
getUser := func(env Env) (User, error) {
    return env.UserService.GetUser()
}

result := F.Pipe1(
    readerresult.Do[Env](State{}),
    readerresult.ApIS(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        getUser,
    ),
)

func ApISL

func ApISL[R, S, T any](
	lens L.Lens[S, T],
	fa RRI.ReaderResult[R, T],
) Operator[R, S, S]

ApISL attaches a value from an idiomatic ReaderResult to a context using a lens-based setter. This is the idiomatic version of ApSL, where the computation returns (T, error) instead of Result[T]. It combines ApIS with a lens, allowing you to use optics to update nested structures in a more composable way.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    ConfigService ConfigService
}

configLens := lens.MakeLens(
    func(s State) Config { return s.Config },
    func(s State, c Config) State { s.Config = c; return s },
)

// Idiomatic computation returning (Config, error)
getConfig := func(env Env) (Config, error) {
    return env.ConfigService.GetConfig()
}

result := F.Pipe1(
    readerresult.Of[Env](State{}),
    readerresult.ApISL(configLens, getConfig),
)

func ApReader

func ApReader[B, R, A any](fa Reader[R, A]) Operator[R, func(A) B, B]

func ApReaderS

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

ApReaderS attaches a value from a pure Reader computation to a context [S1] to produce a context [S2] using Applicative semantics (independent, non-sequential composition).

Unlike BindReaderK which uses monadic bind (sequential), ApReaderS uses applicative apply, meaning the Reader computation fa is independent of the current state and can conceptually execute in parallel.

This is useful when you want to combine a Reader computation with your ReaderResult state without creating a dependency between them.

Example:

type Env struct {
    DefaultPort int
    DefaultHost string
}
type State struct {
    Port int
    Host string
}

getDefaultPort := func(env Env) int { return env.DefaultPort }
getDefaultHost := func(env Env) string { return env.DefaultHost }

result := F.Pipe2(
    readerresult.Do[Env](State{}),
    readerresult.ApReaderS(
        func(port int) func(State) State {
            return func(s State) State { s.Port = port; return s }
        },
        getDefaultPort,
    ),
    readerresult.ApReaderS(
        func(host string) func(State) State {
            return func(s State) State { s.Host = host; return s }
        },
        getDefaultHost,
    ),
)

func ApResult

func ApResult[B, R, A any](fa Result[A]) Operator[R, func(A) B, B]

ApResult is the curried version of MonadApResult. It returns an Operator that applies a pre-computed Result value to a function in a ReaderResult context. This is useful in function composition pipelines when you have a static Result value.

Example:

fa := result.Of(10)
result := F.Pipe1(
    readerresult.Of[Config](utils.Double),
    readerresult.ApResult[int, Config](fa),
)
// result(cfg) returns result.Of(20)

func ApResultI

func ApResultI[B, R, A any](a A, err error) Operator[R, func(A) B, B]

ApResultI is the curried idiomatic version of ApResult. It accepts a (value, error) pair directly and applies it to a function in a ReaderResult context. This bridges Go's idiomatic error handling with the functional ApResult operation.

Example:

value, err := strconv.Atoi("10")  // Returns (10, nil)
result := F.Pipe1(
    readerresult.Of[Config](utils.Double),
    readerresult.ApResultI[int, Config](value, err),
)
// result(cfg) returns result.Of(20)

func ApResultS

func ApResultS[
	R, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Result[T],
) Operator[R, S1, S2]

ApResultS attaches a value from a Result to a context [S1] to produce a context [S2] using Applicative semantics (independent, non-sequential composition).

Unlike BindResultK which uses monadic bind (sequential), ApResultS uses applicative apply, meaning the Result computation fa is independent of the current state and can conceptually execute in parallel.

If the Result fa contains an error, the entire computation short-circuits with that error. This is useful when you want to combine a Result value with your ReaderResult state without creating a dependency between them.

Example:

type State struct {
    Value1 int
    Value2 int
}

// Independent Result computations
parseValue1 := result.TryCatch(func() int { return 42 })
parseValue2 := result.TryCatch(func() int { return 100 })

computation := F.Pipe2(
    readerresult.Do[any](State{}),
    readerresult.ApResultS(
        func(v int) func(State) State {
            return func(s State) State { s.Value1 = v; return s }
        },
        parseValue1,
    ),
    readerresult.ApResultS(
        func(v int) func(State) State {
            return func(s State) State { s.Value2 = v; return s }
        },
        parseValue2,
    ),
)

func ApS

func ApS[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa ReaderResult[R, T],
) Operator[R, 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
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

// These operations are independent and can be combined with ApS
getUser := readereither.Asks(func(env Env) either.Either[error, User] {
    return env.UserService.GetUser()
})
getConfig := readereither.Asks(func(env Env) either.Either[error, Config] {
    return env.ConfigService.GetConfig()
})

result := F.Pipe2(
    readereither.Do[Env, error](State{}),
    readereither.ApS(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        getUser,
    ),
    readereither.ApS(
        func(cfg Config) func(State) State {
            return func(s State) State { s.Config = cfg; return s }
        },
        getConfig,
    ),
)

func ApSL

func ApSL[R, S, T any](
	lens L.Lens[S, T],
	fa ReaderResult[R, T],
) Operator[R, 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
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

configLens := lens.MakeLens(
    func(s State) Config { return s.Config },
    func(s State, c Config) State { s.Config = c; return s },
)

getConfig := readereither.Asks(func(env Env) either.Either[error, Config] {
    return env.ConfigService.GetConfig()
})
result := F.Pipe2(
    readereither.Of[Env, error](State{}),
    readereither.ApSL(configLens, getConfig),
)

func BiMap

func BiMap[R, A, B any](f Endomorphism[error], g func(A) B) Operator[R, A, B]

BiMap is the curried version of MonadBiMap. It maps a pair of functions over the error and success channels.

Example:

enrichErr := func(e error) error { return fmt.Errorf("enriched: %w", e) }
double := N.Mul(2)
result := F.Pipe1(rr, readerresult.BiMap[Config](enrichErr, double))

func Bind

func Bind[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f Kleisli[R, S1, T],
) Operator[R, 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
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

result := F.Pipe2(
    readereither.Do[Env, error](State{}),
    readereither.Bind(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        func(s State) readereither.ReaderResult[Env, error, User] {
            return readereither.Asks(func(env Env) either.Either[error, User] {
                return env.UserService.GetUser()
            })
        },
    ),
    readereither.Bind(
        func(cfg Config) func(State) State {
            return func(s State) State { s.Config = cfg; return s }
        },
        func(s State) readereither.ReaderResult[Env, error, Config] {
            // This can access s.User from the previous step
            return readereither.Asks(func(env Env) either.Either[error, Config] {
                return env.ConfigService.GetConfigForUser(s.User.ID)
            })
        },
    ),
)

func BindEitherIK

func BindEitherIK[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f RI.Kleisli[S1, T],
) Operator[R, S1, S2]

BindEitherIK lifts an idiomatic Result Kleisli arrow into a ReaderResult context and binds it to the state. This is the idiomatic version of BindEitherK, where the function returns (T, error) instead of Result[T]. It allows you to integrate idiomatic Result computations (that may fail but don't need environment access) into a ReaderResult computation chain.

Example:

type State struct {
    Value       int
    ParsedValue int
}

// Idiomatic function returning (int, error)
parseValue := func(s State) (int, error) {
    if s.Value < 0 {
        return 0, errors.New("negative value")
    }
    return s.Value * 2, nil
}

result := F.Pipe1(
    readerresult.Do[context.Context](State{Value: 5}),
    readerresult.BindEitherIK[context.Context](
        func(parsed int) func(State) State {
            return func(s State) State { s.ParsedValue = parsed; return s }
        },
        parseValue,
    ),
)

func BindEitherK

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

func BindI

func BindI[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f RRI.Kleisli[R, S1, T],
) Operator[R, S1, S2]

BindI attaches the result of an idiomatic computation to a context [S1] to produce a context [S2]. This is the idiomatic version of Bind, where the computation returns (T, error) instead of Result[T]. This enables sequential composition with Go's native error handling style where each step can depend on the results of previous steps and access the shared environment.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService UserService
}

// Idiomatic function returning (User, error)
getUser := func(s State) func(env Env) (User, error) {
    return func(env Env) (User, error) {
        return env.UserService.GetUser()
    }
}

result := F.Pipe1(
    readerresult.Do[Env](State{}),
    readerresult.BindI(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        getUser,
    ),
)

func BindIL

func BindIL[R, S, T any](
	lens L.Lens[S, T],
	f RRI.Kleisli[R, T, T],
) Operator[R, S, S]

BindIL is a variant of BindI that uses a lens to focus on a specific part of the context. This is the idiomatic version of BindL, where the computation returns (T, error) instead of Result[T]. It provides a more ergonomic API when working with nested structures, eliminating the need to manually write setter functions while supporting Go's native error handling pattern.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService UserService
}

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

// Idiomatic function returning (User, error)
updateUser := func(user User) func(env Env) (User, error) {
    return func(env Env) (User, error) {
        return env.UserService.UpdateUser(user)
    }
}

result := F.Pipe1(
    readerresult.Do[Env](State{}),
    readerresult.BindIL(userLens, updateUser),
)

func BindL

func BindL[R, S, T any](
	lens L.Lens[S, T],
	f Kleisli[R, T, T],
) Operator[R, 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 ReaderEither computation that produces an updated value.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

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

result := F.Pipe2(
    readereither.Do[Env, error](State{}),
    readereither.BindL(userLens, func(user User) readereither.ReaderResult[Env, error, User] {
        return readereither.Asks(func(env Env) either.Either[error, User] {
            return env.UserService.GetUser()
        })
    }),
)

func BindReaderK

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

BindReaderK lifts a Reader Kleisli arrow into a ReaderResult context and binds it to the state. This allows you to integrate pure Reader computations (that don't have error handling) into a ReaderResult computation chain.

The function f takes the current state S1 and returns a Reader[R, T] computation. The result T is then attached to the state using the setter to produce state S2.

Example:

type Env struct {
    ConfigPath string
}
type State struct {
    Config string
}

// A pure Reader computation that reads from environment
getConfigPath := func(s State) reader.Reader[Env, string] {
    return func(env Env) string {
        return env.ConfigPath
    }
}

result := F.Pipe2(
    readerresult.Do[Env](State{}),
    readerresult.BindReaderK(
        func(path string) func(State) State {
            return func(s State) State { s.Config = path; return s }
        },
        getConfigPath,
    ),
)

func BindResultIK

func BindResultIK[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f RI.Kleisli[S1, T],
) Operator[R, S1, S2]

BindResultIK is an alias for BindEitherIK. It lifts an idiomatic Result Kleisli arrow into a ReaderResult context and binds it to the state. The function f returns (T, error) in Go's idiomatic style.

func BindResultK

func BindResultK[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f result.Kleisli[S1, T],
) Operator[R, S1, S2]

BindResultK lifts a Result Kleisli arrow into a ReaderResult context and binds it to the state. This allows you to integrate Result computations (that may fail with an error but don't need environment access) into a ReaderResult computation chain.

The function f takes the current state S1 and returns a Result[T] computation. If the Result is successful, the value T is attached to the state using the setter to produce state S2. If the Result is an error, the entire computation short-circuits with that error.

Example:

type State struct {
    Value int
    ParsedValue int
}

// A Result computation that may fail
parseValue := func(s State) result.Result[int] {
    if s.Value < 0 {
        return result.Error[int](errors.New("negative value"))
    }
    return result.Of(s.Value * 2)
}

result := F.Pipe2(
    readerresult.Do[any](State{Value: 5}),
    readerresult.BindResultK(
        func(parsed int) func(State) State {
            return func(s State) State { s.ParsedValue = parsed; return s }
        },
        parseValue,
    ),
)

func BindTo

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

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

func Chain

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

Chain is the curried version of MonadChain. It returns an Operator that can be used in function composition pipelines.

Example:

getPosts := func(user User) readerresult.ReaderResult[DB, []Post] { ... }
result := F.Pipe1(getUser(42), readerresult.Chain[DB](getPosts))

func ChainEitherIK

func ChainEitherIK[R, A, B any](f RI.Kleisli[A, B]) Operator[R, A, B]

ChainEitherIK is the curried version of MonadChainEitherIK. It lifts an idiomatic function returning (B, error) into a ReaderResult operator.

Example:

// Idiomatic parser returning (User, error)
parseUser := func(data string) (User, error) {
    return json.Unmarshal([]byte(data), &User{})
}
result := F.Pipe1(getUserDataRR, readerresult.ChainEitherIK[DB](parseUser))

func ChainEitherK

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

ChainEitherK is the curried version of MonadChainEitherK. It lifts a Result-returning function into a ReaderResult operator.

Example:

parseUser := func(data string) result.Result[User] { ... }
result := F.Pipe1(getUserDataRR, readerresult.ChainEitherK[Config](parseUser))

func ChainI

func ChainI[R, A, B any](f RRI.Kleisli[R, A, B]) Operator[R, A, B]

ChainI is the curried version of MonadChainI. It allows chaining with idiomatic Kleisli arrows that return (B, error).

Example:

// Idiomatic function that returns (User, error)
fetchUser := func(id int) func(db DB) (User, error) {
    return func(db DB) (User, error) {
        return db.GetUser(id)  // returns (User, error)
    }
}
result := F.Pipe1(getUserIDRR, readerresult.ChainI[DB](fetchUser))

func ChainReaderK

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

func Flap

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

Flap is the curried version of MonadFlap.

func Let

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

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

func LetL

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

Example:

type State struct {
    User   User
    Config Config
}

configLens := lens.MakeLens(
    func(s State) Config { return s.Config },
    func(s State, c Config) State { s.Config = c; return s },
)

result := F.Pipe2(
    readereither.Do[any, error](State{Config: Config{Host: "localhost"}}),
    readereither.LetL(configLens, func(cfg Config) Config {
        cfg.Port = 8080
        return cfg
    }),
)

func LetTo

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

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

func LetToL

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

configLens := lens.MakeLens(
    func(s State) Config { return s.Config },
    func(s State, c Config) State { s.Config = c; return s },
)

newConfig := Config{Host: "localhost", Port: 8080}
result := F.Pipe2(
    readereither.Do[any, error](State{}),
    readereither.LetToL(configLens, newConfig),
)

func Map

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

Map is the curried version of MonadMap. It returns an Operator that can be used in function composition pipelines.

Example:

double := readerresult.Map[Config](N.Mul(2))
result := F.Pipe1(readerresult.Of[Config](5), double)

func MapLeft

func MapLeft[R, A any](f Endomorphism[error]) Operator[R, A, A]

MapLeft is the curried version of MonadMapLeft. It applies a mapping function to the error channel only.

Example:

enrichErr := func(e error) error { return fmt.Errorf("DB error: %w", e) }
result := F.Pipe1(getUserRR, readerresult.MapLeft[Config](enrichErr))

func OrElse

func OrElse[R, A any](onLeft Kleisli[R, error, A]) Operator[R, A, A]

OrElse provides an alternative ReaderResult computation if the first one fails. This is useful for fallback logic or retry scenarios.

Example:

getPrimaryUser := func(id int) readerresult.ReaderResult[DB, User] { ... }
getBackupUser := func(err error) readerresult.ReaderResult[DB, User] {
    return readerresult.Of[DB](User{Name: "Guest"})
}
result := F.Pipe1(getPrimaryUser(42), readerresult.OrElse[DB](getBackupUser))

func OrElseI

func OrElseI[R, A any](onLeft RRI.Kleisli[R, error, A]) Operator[R, A, A]

OrElseI provides an alternative ReaderResult computation using an idiomatic Kleisli arrow if the first one fails. This is the idiomatic version of OrElse, where the fallback function returns (A, error) instead of Result[A]. This is useful for fallback logic or retry scenarios with idiomatic Go functions.

Example:

getPrimaryUser := func(id int) readerresult.ReaderResult[DB, User] { ... }
// Idiomatic fallback returning (User, error)
getBackupUser := func(err error) func(db DB) (User, error) {
    return func(db DB) (User, error) {
        return User{Name: "Guest"}, nil
    }
}
result := F.Pipe1(getPrimaryUser(42), readerresult.OrElseI[DB](getBackupUser))

func OrLeft

func OrLeft[R, A any](onLeft reader.Kleisli[R, error, error]) Operator[R, A, A]

OrLeft transforms the error value if the computation fails, leaving successful values unchanged. This is useful for error mapping or enriching error information.

Example:

enrichError := func(err error) reader.Reader[Config, error] {
    return func(cfg Config) error {
        return fmt.Errorf("DB error on %s: %w", cfg.DBHost, err)
    }
}
result := F.Pipe1(getUserRR, readerresult.OrLeft[Config](enrichError))

type Option

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

type Reader

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

type ReaderResult

type ReaderResult[R, A any] = Reader[R, Result[A]]

func Ask

func Ask[R any]() ReaderResult[R, R]

Ask retrieves the current environment as a successful ReaderResult. This is useful for accessing configuration or context within a ReaderResult computation.

Example:

result := F.Pipe1(
    readerresult.Ask[Config](),
    readerresult.Map[Config](func(cfg Config) int { return cfg.Port }),
)
// result(cfg) returns result.Of(cfg.Port)

func Asks

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

Asks retrieves a value from the environment using the provided Reader function. This lifts a Reader computation into a ReaderResult that always succeeds.

Example:

getPort := func(cfg Config) int { return cfg.Port }
result := readerresult.Asks[Config](getPort)
// result(cfg) returns result.Of(cfg.Port)

func Curry0

func Curry0[R, A any](f func(R) (A, error)) ReaderResult[R, A]

Curry0 converts a context-only function into a ReaderResult (same as From0 but emphasizes immediate application).

Example:

getConfig := func(ctx context.Context) (Config, error) { ... }
rr := readerresult.Curry0(getConfig)
// rr is a ReaderResult[context.Context, Config]

func Do

func Do[R, S any](
	empty S,
) ReaderResult[R, 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
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}
result := readereither.Do[Env, error](State{})

func Flatten

func Flatten[R, A any](mma ReaderResult[R, ReaderResult[R, A]]) ReaderResult[R, A]

Flatten removes one level of nesting from a nested ReaderResult. This converts ReaderResult[R, ReaderResult[R, A]] into ReaderResult[R, A].

Example:

nested := readerresult.Of[Config](readerresult.Of[Config](42))
flat := readerresult.Flatten(nested)
// flat(cfg) returns result.Of(42)

func FlattenI

func FlattenI[R, A any](mma ReaderResult[R, RRI.ReaderResult[R, A]]) ReaderResult[R, A]

FlattenI removes one level of nesting from a ReaderResult containing an idiomatic ReaderResult. This converts ReaderResult[R, RRI.ReaderResult[R, A]] into ReaderResult[R, A]. The inner computation returns (A, error) in Go's idiomatic style.

Example:

// Nested computation where inner returns (A, error)
nested := readerresult.Of[Config](func(cfg Config) (int, error) {
    return 42, nil
})
flat := readerresult.FlattenI(nested)
// flat(cfg) returns result.Of(42)

func FromEither

func FromEither[R, A any](e Result[A]) ReaderResult[R, A]

FromEither lifts a Result[A] into a ReaderResult[R, A] that ignores the environment. The resulting computation will always produce the same result regardless of the environment provided.

Example:

res := result.Of(42)
rr := readerresult.FromEither[Config](res)
// rr(anyConfig) will always return result.Of(42)

func FromReader

func FromReader[R, A any](r Reader[R, A]) ReaderResult[R, A]

FromReader is an alias for RightReader. It lifts a Reader[R, A] into a ReaderResult[R, A] that always succeeds.

func FromReaderResultI

func FromReaderResultI[R, A any](rr RRI.ReaderResult[R, A]) ReaderResult[R, A]

FromReaderResultI converts an idiomatic ReaderResult (that returns (A, error)) into a functional ReaderResult (that returns Result[A]). This bridges the gap between Go's idiomatic error handling and functional programming style. The idiomatic RRI.ReaderResult[R, A] is a function R -> (A, error). The functional ReaderResult[R, A] is a function R -> Result[A].

Example:

// Idiomatic ReaderResult
getUserID := func(cfg Config) (int, error) {
    if cfg.Valid {
        return 42, nil
    }
    return 0, errors.New("invalid config")
}
rr := readerresult.FromReaderResultI(getUserID)
// rr is now a functional ReaderResult[Config, int]

func FromResult

func FromResult[R, A any](e Result[A]) ReaderResult[R, A]

FromResult is an alias for FromEither. It lifts a Result[A] into a ReaderResult[R, A] that ignores the environment.

func FromResultI

func FromResultI[R, A any](a A, err error) ReaderResult[R, A]

FromResultI lifts an idiomatic Go (value, error) pair into a ReaderResult[R, A] that ignores the environment. This is the idiomatic version of FromResult, accepting Go's native error handling pattern. If err is non-nil, the resulting computation will always fail with that error. If err is nil, the resulting computation will always succeed with the value a.

Example:

value, err := strconv.Atoi("42")
rr := readerresult.FromResultI[Config](value, err)
// rr(anyConfig) will return result.Of(42) if err is nil

func Left

func Left[R, A any](l error) ReaderResult[R, A]

Left creates a ReaderResult that always fails with the given error, ignoring the environment.

Example:

rr := readerresult.Left[Config, User](errors.New("not found"))
// rr(anyConfig) always returns result.Left[User](error)

func LeftReader

func LeftReader[A, R any](l Reader[R, error]) ReaderResult[R, A]

LeftReader lifts a Reader[R, error] into a ReaderResult[R, A] that always fails. The resulting computation reads an error from the environment and wraps it in a failed Result.

Example:

getError := func(cfg Config) error { return cfg.InitError }
rr := readerresult.LeftReader[User](getError)
// rr(cfg) returns result.Left[User](cfg.InitError)

func MonadAlt

func MonadAlt[R, A any](first ReaderResult[R, A], second Lazy[ReaderResult[R, A]]) ReaderResult[R, A]

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

func MonadAltI

func MonadAltI[R, A any](first ReaderResult[R, A], second Lazy[RRI.ReaderResult[R, A]]) ReaderResult[R, A]

MonadAltI tries the first computation, and if it fails, tries the second idiomatic computation. This is the idiomatic version of MonadAlt, where the alternative computation returns (A, error). The second computation is lazy-evaluated and only executed if the first fails.

Example:

primary := readerresult.Left[Config, int](errors.New("primary failed"))
// Idiomatic alternative returning (int, error)
alternative := func() func(cfg Config) (int, error) {
    return func(cfg Config) (int, error) {
        return 42, nil
    }
}
result := readerresult.MonadAltI(primary, alternative)

func MonadAp

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

MonadAp applies a function wrapped in a ReaderResult to a value wrapped in a ReaderResult. Both computations share the same environment. This is useful for combining independent computations that don't depend on each other's results.

Example:

add := func(x int) func(int) int { return func(y int) int { return x + y } }
fabr := readerresult.Of[Config](add(5))
fa := readerresult.Of[Config](3)
result := readerresult.MonadAp(fabr, fa)  // Returns Of(8)

func MonadApI

func MonadApI[B, R, A any](fab ReaderResult[R, func(A) B], fa RRI.ReaderResult[R, A]) ReaderResult[R, B]

MonadApI applies a function wrapped in a ReaderResult to a value wrapped in an idiomatic ReaderResult. This is the idiomatic version of MonadAp, where the second parameter returns (A, error) instead of Result[A]. Both computations share the same environment.

Example:

add := func(x int) func(int) int { return func(y int) int { return x + y } }
fabr := readerresult.Of[Config](add(5))
// Idiomatic computation returning (int, error)
fa := func(cfg Config) (int, error) { return cfg.Port, nil }
result := readerresult.MonadApI(fabr, fa)

func MonadApReader

func MonadApReader[B, R, A any](fab ReaderResult[R, func(A) B], fa Reader[R, A]) ReaderResult[R, B]

func MonadApResult

func MonadApResult[B, R, A any](fab ReaderResult[R, func(A) B], fa result.Result[A]) ReaderResult[R, B]

MonadApResult applies a function wrapped in a ReaderResult to a value wrapped in a plain Result. The Result value is independent of the environment, while the function may depend on it. This is useful when you have a pre-computed Result value that you want to apply a context-dependent function to.

Example:

add := func(x int) func(int) int { return func(y int) int { return x + y } }
fabr := readerresult.Of[Config](add(5))
fa := result.Of(3)  // Pre-computed Result, independent of environment
result := readerresult.MonadApResult(fabr, fa)  // Returns Of(8)

func MonadBiMap

func MonadBiMap[R, A, B any](fa ReaderResult[R, A], f Endomorphism[error], g func(A) B) ReaderResult[R, B]

MonadBiMap maps functions over both the error and success channels simultaneously. This transforms both the error type and the success type in a single operation.

Example:

enrichErr := func(e error) error { return fmt.Errorf("enriched: %w", e) }
double := N.Mul(2)
result := readerresult.MonadBiMap(rr, enrichErr, double)

func MonadChain

func MonadChain[R, A, B any](ma ReaderResult[R, A], f Kleisli[R, A, B]) ReaderResult[R, B]

MonadChain sequences two ReaderResult computations, where the second depends on the result of the first. This is also known as "flatMap" or "bind". If the first computation fails, the second is not executed.

Example:

getUser := func(id int) readerresult.ReaderResult[DB, User] { ... }
getPosts := func(user User) readerresult.ReaderResult[DB, []Post] { ... }
userPosts := readerresult.MonadChain(getUser(42), getPosts)

func MonadChainEitherIK

func MonadChainEitherIK[R, A, B any](ma ReaderResult[R, A], f RI.Kleisli[A, B]) ReaderResult[R, B]

MonadChainEitherIK chains a ReaderResult with an idiomatic function that returns (B, error). This is the idiomatic version of MonadChainEitherK, accepting functions in Go's native error handling pattern. The function f doesn't need environment access and returns a (value, error) pair.

Example:

getUserDataRR := readerresult.Of[DB]("user_data")
// Idiomatic parser returning (User, error)
parseUser := func(data string) (User, error) {
    return json.Unmarshal([]byte(data), &User{})
}
result := readerresult.MonadChainEitherIK(getUserDataRR, parseUser)

func MonadChainEitherK

func MonadChainEitherK[R, A, B any](ma ReaderResult[R, A], f result.Kleisli[A, B]) ReaderResult[R, B]

MonadChainEitherK chains a ReaderResult with a function that returns a plain Result. This is useful for integrating functions that don't need environment access.

Example:

parseUser := func(data string) result.Result[User] { ... }
result := readerresult.MonadChainEitherK(getUserDataRR, parseUser)

func MonadChainI

func MonadChainI[R, A, B any](ma ReaderResult[R, A], f RRI.Kleisli[R, A, B]) ReaderResult[R, B]

MonadChainI sequences two ReaderResult computations, where the second is an idiomatic Kleisli arrow. This is the idiomatic version of MonadChain, allowing you to chain with functions that return (B, error). The idiomatic Kleisli arrow RRI.Kleisli[R, A, B] is a function A -> R -> (B, error). If the first computation fails, the second is not executed.

Example:

getUserID := readerresult.Of[DB](42)
// Idiomatic function that returns (User, error)
fetchUser := func(id int) func(db DB) (User, error) {
    return func(db DB) (User, error) {
        return db.GetUser(id)  // returns (User, error)
    }
}
result := readerresult.MonadChainI(getUserID, fetchUser)

func MonadChainReaderK

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

func MonadFlap

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

MonadFlap applies a wrapped function to a concrete value (reverse of Ap). This is useful when you have a function in a context and a plain value.

Example:

fabr := readerresult.Of[Config](N.Mul(2))
result := readerresult.MonadFlap(fabr, 5)  // Returns Of(10)

func MonadMap

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

MonadMap transforms the success value of a ReaderResult using the given function. If the computation fails, the error is propagated unchanged.

Example:

rr := readerresult.Of[Config](5)
doubled := readerresult.MonadMap(rr, N.Mul(2))
// doubled(cfg) returns result.Of(10)

func MonadMapLeft

func MonadMapLeft[R, A any](fa ReaderResult[R, A], f Endomorphism[error]) ReaderResult[R, A]

MonadMapLeft transforms the error value without affecting successful results. This is useful for error enrichment, wrapping, or transformation.

Example:

enrichErr := func(e error) error { return fmt.Errorf("DB error: %w", e) }
result := readerresult.MonadMapLeft(getUserRR, enrichErr)

func Of

func Of[R, A any](a A) ReaderResult[R, A]

Of creates a ReaderResult that always succeeds with the given value. This is an alias for Right and is the "pure" or "return" operation for the ReaderResult monad.

Example:

rr := readerresult.Of[Config](42)
// rr(anyConfig) always returns result.Of(42)
func Right[R, A any](r A) ReaderResult[R, A]

Right creates a ReaderResult that always succeeds with the given value, ignoring the environment. This is the "pure" or "return" operation for the ReaderResult monad.

Example:

rr := readerresult.Right[Config](42)
// rr(anyConfig) always returns result.Of(42)

func RightReader

func RightReader[R, A any](r Reader[R, A]) ReaderResult[R, A]

RightReader lifts a Reader[R, A] into a ReaderResult[R, A] that always succeeds. The resulting computation reads a value from the environment and wraps it in a successful Result.

Example:

getPort := func(cfg Config) int { return cfg.Port }
rr := readerresult.RightReader[Config](getPort)
// rr(cfg) returns result.Of(cfg.Port)

func SequenceArray

func SequenceArray[L, A any](ma []ReaderResult[L, A]) ReaderResult[L, []A]

SequenceArray converts an array of ReaderResult values into a single ReaderResult of an array. If any element fails, the entire operation fails with the first error encountered. All computations share the same environment.

Example:

readers := []readerresult.ReaderResult[Config, int]{
    readerresult.Of[Config](1),
    readerresult.Of[Config](2),
    readerresult.Of[Config](3),
}
result := readerresult.SequenceArray(readers)
// result(cfg) returns result.Of([]int{1, 2, 3})

func SequenceT1

func SequenceT1[L, A any](a ReaderResult[L, A]) ReaderResult[L, T.Tuple1[A]]

SequenceT1 wraps a single ReaderResult value in a Tuple1. This is primarily for consistency with the other SequenceT functions.

Example:

rr := readerresult.Of[Config](42)
result := readerresult.SequenceT1(rr)
// result(cfg) returns result.Of(Tuple1{42})

func SequenceT2

func SequenceT2[L, A, B any](
	a ReaderResult[L, A],
	b ReaderResult[L, B],
) ReaderResult[L, T.Tuple2[A, B]]

SequenceT2 combines two independent ReaderResult computations into a tuple. Both computations share the same environment.

Example:

getPort := readerresult.Asks(func(cfg Config) int { return cfg.Port })
getHost := readerresult.Asks(func(cfg Config) string { return cfg.Host })
result := readerresult.SequenceT2(getPort, getHost)
// result(cfg) returns result.Of(Tuple2{cfg.Port, cfg.Host})

func SequenceT3

func SequenceT3[L, A, B, C any](
	a ReaderResult[L, A],
	b ReaderResult[L, B],
	c ReaderResult[L, C],
) ReaderResult[L, T.Tuple3[A, B, C]]

SequenceT3 combines three independent ReaderResult computations into a tuple.

Example:

getUser := getUserRR(42)
getConfig := getConfigRR()
getStats := getStatsRR()
result := readerresult.SequenceT3(getUser, getConfig, getStats)
// result(env) returns result.Of(Tuple3{user, config, stats})

func SequenceT4

func SequenceT4[L, A, B, C, D any](
	a ReaderResult[L, A],
	b ReaderResult[L, B],
	c ReaderResult[L, C],
	d ReaderResult[L, D],
) ReaderResult[L, T.Tuple4[A, B, C, D]]

SequenceT4 combines four independent ReaderResult computations into a tuple.

Example:

result := readerresult.SequenceT4(getUserRR, getConfigRR, getStatsRR, getMetadataRR)
// result(env) returns result.Of(Tuple4{user, config, stats, metadata})

type Result

type Result[A any] = result.Result[A]

Jump to

Keyboard shortcuts

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