Documentation
¶
Overview ¶
Package readerresult provides a ReaderResult monad that combines the Reader and Result monads.
Fantasy Land Specification ¶
This is a monad transformer combining:
- Reader monad: https://github.com/fantasyland/fantasy-land
- Either monad: https://github.com/fantasyland/fantasy-land#either
Implemented Fantasy Land algebras:
- Functor: https://github.com/fantasyland/fantasy-land#functor
- Bifunctor: https://github.com/fantasyland/fantasy-land#bifunctor
- Apply: https://github.com/fantasyland/fantasy-land#apply
- Applicative: https://github.com/fantasyland/fantasy-land#applicative
- Chain: https://github.com/fantasyland/fantasy-land#chain
- Monad: https://github.com/fantasyland/fantasy-land#monad
- Alt: https://github.com/fantasyland/fantasy-land#alt
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:
- Dependency injection with error handling - pass configuration/services through computations that may fail
- Functional error handling - compose operations that depend on context and may error
- 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:
- Map - transform successful values
- Chain (FlatMap) - sequence dependent operations
- Ap - combine independent computations
- 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 ¶
- func ApEitherIS[R, S1, S2, T any](setter func(T) func(S1) S2) func(T, error) Operator[R, S1, S2]
- func ApResultIS[R, S1, S2, T any](setter func(T) func(S1) S2) func(T, error) Operator[R, S1, S2]
- func BindToEither[R, S1, T any](setter func(T) S1) func(Result[T]) ReaderResult[R, S1]
- func BindToEitherI[R, S1, T any](setter func(T) S1) func(T, error) ReaderResult[R, S1]
- func BindToReader[R, S1, T any](setter func(T) S1) func(Reader[R, T]) ReaderResult[R, S1]
- func BindToResult[R, S1, T any](setter func(T) S1) func(Result[T]) ReaderResult[R, S1]
- func BindToResultI[R, S1, T any](setter func(T) S1) func(T, error) ReaderResult[R, S1]
- func ChainOptionIK[R, A, B any](onNone Lazy[error]) func(OI.Kleisli[A, B]) Operator[R, A, B]
- func ChainOptionK[R, A, B any](onNone Lazy[error]) func(option.Kleisli[A, B]) Operator[R, A, B]
- func Curry1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderResult[R, A]
- func Curry2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1) func(T2) ReaderResult[R, A]
- 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]
- 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]
- func From0[R, A any](f func(R) (A, error)) func() ReaderResult[R, A]
- func From1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderResult[R, A]
- func From2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1, T2) ReaderResult[R, A]
- func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderResult[R, A]
- func GetOrElse[R, A any](onLeft reader.Kleisli[R, error, A]) func(ReaderResult[R, A]) Reader[R, A]
- func Local[A, R2, R1 any](f func(R2) R1) func(ReaderResult[R1, A]) ReaderResult[R2, A]
- func Read[A, R any](r R) func(ReaderResult[R, A]) Result[A]
- func Sequence[R1, R2, A any](ma ReaderResult[R2, ReaderResult[R1, A]]) reader.Kleisli[R2, R1, Result[A]]
- func SequenceReader[R1, R2, A any](ma ReaderResult[R2, Reader[R1, A]]) reader.Kleisli[R2, R1, Result[A]]
- func Traverse[R2, R1, A, B any](f Kleisli[R1, A, B]) func(ReaderResult[R2, A]) Kleisli[R2, R1, B]
- func TraverseReader[R2, R1, A, B any](f reader.Kleisli[R1, A, B]) func(ReaderResult[R2, A]) Kleisli[R2, R1, B]
- func Uncurry1[R, T1, A any](f func(T1) ReaderResult[R, A]) func(R, T1) (A, error)
- func Uncurry2[R, T1, T2, A any](f func(T1) func(T2) ReaderResult[R, A]) func(R, T1, T2) (A, error)
- 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)
- type Either
- type Endomorphism
- type Kleisli
- func FromPredicate[R, A any](pred func(A) bool, onFalse func(A) error) Kleisli[R, A, A]
- func TailRec[R, A, B any](f Kleisli[R, A, tailrec.Trampoline[A, B]]) Kleisli[R, A, B]
- func TraverseArray[L, A, B any](f Kleisli[L, A, B]) Kleisli[L, []A, []B]
- func TraverseArrayWithIndex[L, A, B any](f func(int, A) ReaderResult[L, B]) Kleisli[L, []A, []B]
- type Lazy
- type Monoid
- type Operator
- func Alt[R, A any](second Lazy[ReaderResult[R, A]]) Operator[R, A, A]
- func AltI[R, A any](second Lazy[RRI.ReaderResult[R, A]]) Operator[R, A, A]
- func Ap[B, R, A any](fa ReaderResult[R, A]) Operator[R, func(A) B, B]
- func ApEitherS[R, S1, S2, T any](setter func(T) func(S1) S2, fa Result[T]) Operator[R, S1, S2]
- func ApI[B, R, A any](fa RRI.ReaderResult[R, A]) Operator[R, func(A) B, B]
- func ApIS[R, S1, S2, T any](setter func(T) func(S1) S2, fa RRI.ReaderResult[R, T]) Operator[R, S1, S2]
- func ApISL[R, S, T any](lens L.Lens[S, T], fa RRI.ReaderResult[R, T]) Operator[R, S, S]
- func ApReader[B, R, A any](fa Reader[R, A]) Operator[R, func(A) B, B]
- func ApReaderS[R, S1, S2, T any](setter func(T) func(S1) S2, fa Reader[R, T]) Operator[R, S1, S2]
- func ApResult[B, R, A any](fa Result[A]) Operator[R, func(A) B, B]
- func ApResultI[B, R, A any](a A, err error) Operator[R, func(A) B, B]
- func ApResultS[R, S1, S2, T any](setter func(T) func(S1) S2, fa Result[T]) Operator[R, S1, S2]
- func ApS[R, S1, S2, T any](setter func(T) func(S1) S2, fa ReaderResult[R, T]) Operator[R, S1, S2]
- func ApSL[R, S, T any](lens L.Lens[S, T], fa ReaderResult[R, T]) Operator[R, S, S]
- func BiMap[R, A, B any](f Endomorphism[error], g func(A) B) Operator[R, A, B]
- func Bind[R, S1, S2, T any](setter func(T) func(S1) S2, f Kleisli[R, S1, T]) Operator[R, S1, S2]
- func BindEitherIK[R, S1, S2, T any](setter func(T) func(S1) S2, f RI.Kleisli[S1, T]) Operator[R, S1, S2]
- func BindEitherK[R, S1, S2, T any](setter func(T) func(S1) S2, f result.Kleisli[S1, T]) Operator[R, S1, S2]
- func BindI[R, S1, S2, T any](setter func(T) func(S1) S2, f RRI.Kleisli[R, S1, T]) Operator[R, S1, S2]
- func BindIL[R, S, T any](lens L.Lens[S, T], f RRI.Kleisli[R, T, T]) Operator[R, S, S]
- func BindL[R, S, T any](lens L.Lens[S, T], f Kleisli[R, T, T]) Operator[R, S, S]
- func BindReaderK[R, S1, S2, T any](setter func(T) func(S1) S2, f reader.Kleisli[R, S1, T]) Operator[R, S1, S2]
- func BindResultIK[R, S1, S2, T any](setter func(T) func(S1) S2, f RI.Kleisli[S1, T]) Operator[R, S1, S2]
- func BindResultK[R, S1, S2, T any](setter func(T) func(S1) S2, f result.Kleisli[S1, T]) Operator[R, S1, S2]
- func BindTo[R, S1, T any](setter func(T) S1) Operator[R, T, S1]
- func Chain[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, B]
- func ChainEitherIK[R, A, B any](f RI.Kleisli[A, B]) Operator[R, A, B]
- func ChainEitherK[R, A, B any](f result.Kleisli[A, B]) Operator[R, A, B]
- func ChainI[R, A, B any](f RRI.Kleisli[R, A, B]) Operator[R, A, B]
- func ChainReaderK[R, A, B any](f reader.Kleisli[R, A, B]) Operator[R, A, B]
- func Flap[R, B, A any](a A) Operator[R, func(A) B, B]
- func Let[R, S1, S2, T any](setter func(T) func(S1) S2, f func(S1) T) Operator[R, S1, S2]
- func LetL[R, S, T any](lens L.Lens[S, T], f Endomorphism[T]) Operator[R, S, S]
- func LetTo[R, S1, S2, T any](setter func(T) func(S1) S2, b T) Operator[R, S1, S2]
- func LetToL[R, S, T any](lens L.Lens[S, T], b T) Operator[R, S, S]
- func Map[R, A, B any](f func(A) B) Operator[R, A, B]
- func MapLeft[R, A any](f Endomorphism[error]) Operator[R, A, A]
- func OrElse[R, A any](onLeft Kleisli[R, error, A]) Operator[R, A, A]
- func OrElseI[R, A any](onLeft RRI.Kleisli[R, error, A]) Operator[R, A, A]
- func OrLeft[R, A any](onLeft reader.Kleisli[R, error, error]) Operator[R, A, A]
- type Option
- type Reader
- type ReaderResult
- func Ask[R any]() ReaderResult[R, R]
- func Asks[R, A any](r Reader[R, A]) ReaderResult[R, A]
- func Curry0[R, A any](f func(R) (A, error)) ReaderResult[R, A]
- func Do[R, S any](empty S) ReaderResult[R, S]
- func Flatten[R, A any](mma ReaderResult[R, ReaderResult[R, A]]) ReaderResult[R, A]
- func FlattenI[R, A any](mma ReaderResult[R, RRI.ReaderResult[R, A]]) ReaderResult[R, A]
- func FromEither[R, A any](e Result[A]) ReaderResult[R, A]
- func FromReader[R, A any](r Reader[R, A]) ReaderResult[R, A]
- func FromReaderResultI[R, A any](rr RRI.ReaderResult[R, A]) ReaderResult[R, A]
- func FromResult[R, A any](e Result[A]) ReaderResult[R, A]
- func FromResultI[R, A any](a A, err error) ReaderResult[R, A]
- func Left[R, A any](l error) ReaderResult[R, A]
- func LeftReader[A, R any](l Reader[R, error]) ReaderResult[R, A]
- func MonadAlt[R, A any](first ReaderResult[R, A], second Lazy[ReaderResult[R, A]]) ReaderResult[R, A]
- func MonadAltI[R, A any](first ReaderResult[R, A], second Lazy[RRI.ReaderResult[R, A]]) ReaderResult[R, A]
- func MonadAp[B, R, A any](fab ReaderResult[R, func(A) B], fa ReaderResult[R, A]) ReaderResult[R, B]
- func MonadApI[B, R, A any](fab ReaderResult[R, func(A) B], fa RRI.ReaderResult[R, A]) ReaderResult[R, B]
- func MonadApReader[B, R, A any](fab ReaderResult[R, func(A) B], fa Reader[R, A]) ReaderResult[R, B]
- func MonadApResult[B, R, A any](fab ReaderResult[R, func(A) B], fa result.Result[A]) ReaderResult[R, B]
- func MonadBiMap[R, A, B any](fa ReaderResult[R, A], f Endomorphism[error], g func(A) B) ReaderResult[R, B]
- func MonadChain[R, A, B any](ma ReaderResult[R, A], f Kleisli[R, A, B]) ReaderResult[R, B]
- func MonadChainEitherIK[R, A, B any](ma ReaderResult[R, A], f RI.Kleisli[A, B]) ReaderResult[R, B]
- func MonadChainEitherK[R, A, B any](ma ReaderResult[R, A], f result.Kleisli[A, B]) ReaderResult[R, B]
- func MonadChainI[R, A, B any](ma ReaderResult[R, A], f RRI.Kleisli[R, A, B]) ReaderResult[R, B]
- func MonadChainReaderK[R, A, B any](ma ReaderResult[R, A], f reader.Kleisli[R, A, B]) ReaderResult[R, B]
- func MonadFlap[R, A, B any](fab ReaderResult[R, func(A) B], a A) ReaderResult[R, B]
- func MonadMap[R, A, B any](fa ReaderResult[R, A], f func(A) B) ReaderResult[R, B]
- func MonadMapLeft[R, A any](fa ReaderResult[R, A], f Endomorphism[error]) ReaderResult[R, A]
- func Of[R, A any](a A) ReaderResult[R, A]
- func Right[R, A any](r A) ReaderResult[R, A]
- func RightReader[R, A any](r Reader[R, A]) ReaderResult[R, A]
- func SequenceArray[L, A any](ma []ReaderResult[L, A]) ReaderResult[L, []A]
- func SequenceT1[L, A any](a ReaderResult[L, A]) ReaderResult[L, T.Tuple1[A]]
- func SequenceT2[L, A, B any](a ReaderResult[L, A], b ReaderResult[L, B]) ReaderResult[L, T.Tuple2[A, B]]
- 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]]
- func SequenceT4[L, A, B, C, D any](a ReaderResult[L, A], b ReaderResult[L, B], c ReaderResult[L, C], ...) ReaderResult[L, T.Tuple4[A, B, C, D]]
- type Result
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 ¶
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 ¶
BindToResultI is an alias for BindToEitherI. It initializes a new state S1 from an idiomatic (value, error) pair.
func ChainOptionIK ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 Uncurry1 ¶
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 ¶
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 Endomorphism ¶
type Endomorphism[A any] = endomorphism.Endomorphism[A]
type Kleisli ¶
type Kleisli[R, A, B any] = Reader[A, ReaderResult[R, B]]
func FromPredicate ¶
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 ¶
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 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 ¶
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 ¶
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 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 ¶
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 ¶
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 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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 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 ¶
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 ¶
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 ¶
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 ¶
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 ReaderResult ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 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 ¶
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 ¶
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})