Documentation
¶
Index ¶
- func Ap[B, E, L, A any](fa ReaderEither[E, L, A]) func(ReaderEither[E, L, func(A) B]) ReaderEither[E, L, B]
- func ApS[R, E, S1, S2, T any](setter func(T) func(S1) S2, fa ReaderEither[R, E, T]) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
- func ApSL[R, E, S, T any](lens L.Lens[S, T], fa ReaderEither[R, E, T]) func(ReaderEither[R, E, S]) ReaderEither[R, E, S]
- func BiMap[E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderEither[E, E1, A]) ReaderEither[E, E2, B]
- func Bind[R, E, S1, S2, T any](setter func(T) func(S1) S2, f func(S1) ReaderEither[R, E, T]) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
- func BindEitherK[R, E, S1, S2, T any](setter func(T) func(S1) S2, f func(S1) Either[E, T]) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
- func BindL[R, E, S, T any](lens L.Lens[S, T], f func(T) ReaderEither[R, E, T]) func(ReaderEither[R, E, S]) ReaderEither[R, E, S]
- func BindReaderK[R, E, S1, S2, T any](setter func(T) func(S1) S2, f func(S1) Reader[R, T]) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
- func BindTo[R, E, S1, T any](setter func(T) S1) func(ReaderEither[R, E, T]) ReaderEither[R, E, S1]
- func BindToEither[R, E, S1, T any](setter func(T) S1) func(ET.Either[E, T]) ReaderEither[R, E, S1]
- func BindToReader[R, E, S1, T any](setter func(T) S1) func(Reader[R, T]) ReaderEither[R, E, S1]
- func Chain[E, L, A, B any](f func(A) ReaderEither[E, L, B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B]
- func ChainEitherK[E, L, A, B any](f func(A) Either[L, B]) func(ma ReaderEither[E, L, A]) ReaderEither[E, L, B]
- func ChainLeft[R, EA, EB, A any](f Kleisli[R, EB, EA, A]) func(ReaderEither[R, EA, A]) ReaderEither[R, EB, A]
- func ChainOptionK[E, A, B, L any](onNone func() L) func(func(A) Option[B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B]
- func ChainReaderK[L, E, A, B any](f reader.Kleisli[E, A, B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B]
- func Curry1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderEither[R, error, A]
- func Curry2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1) func(T2) ReaderEither[R, error, A]
- func Curry3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderEither[R, error, A]
- func Flap[L, E, B, A any](a A) func(ReaderEither[L, E, func(A) B]) ReaderEither[L, E, B]
- func Fold[E, L, A, B any](onLeft func(L) Reader[E, B], onRight func(A) Reader[E, B]) func(ReaderEither[E, L, A]) Reader[E, B]
- func From0[R, A any](f func(R) (A, error)) func() ReaderEither[R, error, A]
- func From1[R, T1, A any](f func(R, T1) (A, error)) func(T1) ReaderEither[R, error, A]
- func From2[R, T1, T2, A any](f func(R, T1, T2) (A, error)) func(T1, T2) ReaderEither[R, error, A]
- func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderEither[R, error, A]
- func FromPredicate[E, L, A any](pred func(A) bool, onFalse func(A) L) func(A) ReaderEither[E, L, A]
- func GetOrElse[E, L, A any](onLeft func(L) Reader[E, A]) func(ReaderEither[E, L, A]) Reader[E, A]
- func Let[R, E, S1, S2, T any](setter func(T) func(S1) S2, f func(S1) T) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
- func LetL[R, E, S, T any](lens L.Lens[S, T], f func(T) T) func(ReaderEither[R, E, S]) ReaderEither[R, E, S]
- func LetTo[R, E, S1, S2, T any](setter func(T) func(S1) S2, b T) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
- func LetToL[R, E, S, T any](lens L.Lens[S, T], b T) func(ReaderEither[R, E, S]) ReaderEither[R, E, S]
- func Local[E, A, R1, R2 any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A]
- func Map[E, L, A, B any](f func(A) B) func(ReaderEither[E, L, A]) ReaderEither[E, L, B]
- func MapLeft[C, E1, E2, A any](f func(E1) E2) func(ReaderEither[C, E1, A]) ReaderEither[C, E2, A]
- func OrLeft[A, L1, E, L2 any](onLeft func(L1) Reader[E, L2]) func(ReaderEither[E, L1, A]) ReaderEither[E, L2, A]
- func Read[E1, A, E any](e E) func(ReaderEither[E, E1, A]) Either[E1, A]
- func ReadEither[E1, A, E any](e Either[E1, E]) func(ReaderEither[E, E1, A]) Either[E1, A]
- func Traverse[R2, R1, E, A, B any](f Kleisli[R1, E, A, B]) func(ReaderEither[R2, E, A]) Kleisli[R2, E, R1, B]
- func TraverseArray[E, L, A, B any](f func(A) ReaderEither[E, L, B]) func([]A) ReaderEither[E, L, []B]
- func TraverseArrayWithIndex[E, L, A, B any](f func(int, A) ReaderEither[E, L, B]) func([]A) ReaderEither[E, L, []B]
- func TraverseReader[R2, R1, E, A, B any](f reader.Kleisli[R1, A, B]) func(ReaderEither[R2, E, A]) Kleisli[R2, E, R1, B]
- func Uncurry1[R, T1, A any](f func(T1) ReaderEither[R, error, A]) func(R, T1) (A, error)
- func Uncurry2[R, T1, T2, A any](f func(T1) func(T2) ReaderEither[R, error, A]) func(R, T1, T2) (A, error)
- func Uncurry3[R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderEither[R, error, A]) func(R, T1, T2, T3) (A, error)
- type Either
- type Kleisli
- func Contramap[E, A, R1, R2 any](f func(R2) R1) Kleisli[R2, E, ReaderEither[R1, E, A], A]
- func OrElse[R, E1, E2, A any](onLeft Kleisli[R, E2, E1, A]) Kleisli[R, E2, ReaderEither[R, E1, A], A]
- func Promap[R, E, A, D, B any](f func(D) R, g func(A) B) Kleisli[D, E, ReaderEither[R, E, A], B]
- func Sequence[R1, R2, E, A any](ma ReaderEither[R2, E, ReaderEither[R1, E, A]]) Kleisli[R2, E, R1, A]
- func SequenceReader[R1, R2, E, A any](ma ReaderEither[R2, E, Reader[R1, A]]) Kleisli[R2, E, R1, A]
- func TailRec[R, E, A, B any](f Kleisli[R, E, A, tailrec.Trampoline[A, B]]) Kleisli[R, E, A, B]
- type Operator
- func ApEitherS[R, E, S1, S2, T any](setter func(T) func(S1) S2, fa ET.Either[E, T]) Operator[R, E, S1, S2]
- func ApReaderS[R, E, S1, S2, T any](setter func(T) func(S1) S2, fa Reader[R, T]) Operator[R, E, S1, S2]
- func ChainFirstLeft[A, R, EA, EB, B any](f Kleisli[R, EB, EA, B]) Operator[R, EA, A, A]
- func TapLeft[A, R, EA, EB, B any](f Kleisli[R, EB, EA, B]) Operator[R, EA, A, A]
- type Option
- type Reader
- type ReaderEither
- func Ask[E, L any]() ReaderEither[E, L, E]
- func Asks[L, E, A any](r Reader[E, A]) ReaderEither[E, L, A]
- func Curry0[R, A any](f func(R) (A, error)) ReaderEither[R, error, A]
- func Do[R, E, S any](empty S) ReaderEither[R, E, S]
- func Flatten[E, L, A any](mma ReaderEither[E, L, ReaderEither[E, L, A]]) ReaderEither[E, L, A]
- func FromEither[E, L, A any](e Either[L, A]) ReaderEither[E, L, A]
- func FromReader[L, E, A any](r Reader[E, A]) ReaderEither[E, L, A]
- func Left[E, A, L any](l L) ReaderEither[E, L, A]
- func LeftReader[A, E, L any](l Reader[E, L]) ReaderEither[E, L, A]
- func MonadAp[B, E, L, A any](fab ReaderEither[E, L, func(A) B], fa ReaderEither[E, L, A]) ReaderEither[E, L, B]
- func MonadBiMap[E, E1, E2, A, B any](fa ReaderEither[E, E1, A], f func(E1) E2, g func(A) B) ReaderEither[E, E2, B]
- func MonadChain[E, L, A, B any](ma ReaderEither[E, L, A], f func(A) ReaderEither[E, L, B]) ReaderEither[E, L, B]
- func MonadChainEitherK[E, L, A, B any](ma ReaderEither[E, L, A], f func(A) Either[L, B]) ReaderEither[E, L, B]
- func MonadChainFirstLeft[A, R, EA, EB, B any](ma ReaderEither[R, EA, A], f Kleisli[R, EB, EA, B]) ReaderEither[R, EA, A]
- func MonadChainLeft[R, EA, EB, A any](fa ReaderEither[R, EA, A], f Kleisli[R, EB, EA, A]) ReaderEither[R, EB, A]
- func MonadChainReaderK[L, E, A, B any](ma ReaderEither[E, L, A], f reader.Kleisli[E, A, B]) ReaderEither[E, L, B]
- func MonadFlap[L, E, A, B any](fab ReaderEither[L, E, func(A) B], a A) ReaderEither[L, E, B]
- func MonadMap[E, L, A, B any](fa ReaderEither[E, L, A], f func(A) B) ReaderEither[E, L, B]
- func MonadMapLeft[C, E1, E2, A any](fa ReaderEither[C, E1, A], f func(E1) E2) ReaderEither[C, E2, A]
- func MonadTapLeft[A, R, EA, EB, B any](ma ReaderEither[R, EA, A], f Kleisli[R, EB, EA, B]) ReaderEither[R, EA, A]
- func Of[E, L, A any](a A) ReaderEither[E, L, A]
- func Right[E, L, A any](r A) ReaderEither[E, L, A]
- func RightReader[L, E, A any](r Reader[E, A]) ReaderEither[E, L, A]
- func SequenceArray[E, L, A any](ma []ReaderEither[E, L, A]) ReaderEither[E, L, []A]
- func SequenceT1[L, E, A any](a ReaderEither[E, L, A]) ReaderEither[E, L, T.Tuple1[A]]
- func SequenceT2[L, E, A, B any](a ReaderEither[E, L, A], b ReaderEither[E, L, B]) ReaderEither[E, L, T.Tuple2[A, B]]
- func SequenceT3[L, E, A, B, C any](a ReaderEither[E, L, A], b ReaderEither[E, L, B], c ReaderEither[E, L, C]) ReaderEither[E, L, T.Tuple3[A, B, C]]
- func SequenceT4[L, E, A, B, C, D any](a ReaderEither[E, L, A], b ReaderEither[E, L, B], c ReaderEither[E, L, C], ...) ReaderEither[E, L, T.Tuple4[A, B, C, D]]
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Ap ¶
func Ap[B, E, L, A any](fa ReaderEither[E, L, A]) func(ReaderEither[E, L, func(A) B]) ReaderEither[E, L, B]
func ApS ¶
func ApS[R, E, S1, S2, T any]( setter func(T) func(S1) S2, fa ReaderEither[R, E, T], ) func(ReaderEither[R, E, S1]) ReaderEither[R, E, 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, E, S, T any]( lens L.Lens[S, T], fa ReaderEither[R, E, T], ) func(ReaderEither[R, E, S]) ReaderEither[R, E, 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[E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderEither[E, E1, A]) ReaderEither[E, E2, B]
BiMap maps a pair of functions over the two type arguments of the bifunctor.
func Bind ¶
func Bind[R, E, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) ReaderEither[R, E, T], ) func(ReaderEither[R, E, S1]) ReaderEither[R, E, 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.ReaderEither[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.ReaderEither[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 BindEitherK ¶
func BindEitherK[R, E, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) Either[E, T], ) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
func BindL ¶
func BindL[R, E, S, T any]( lens L.Lens[S, T], f func(T) ReaderEither[R, E, T], ) func(ReaderEither[R, E, S]) ReaderEither[R, E, 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.ReaderEither[Env, error, User] {
return readereither.Asks(func(env Env) either.Either[error, User] {
return env.UserService.GetUser()
})
}),
)
func BindReaderK ¶
func BindReaderK[R, E, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) Reader[R, T], ) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
func BindTo ¶
func BindTo[R, E, S1, T any]( setter func(T) S1, ) func(ReaderEither[R, E, T]) ReaderEither[R, E, S1]
BindTo initializes a new state [S1] from a value [T]
func BindToEither ¶
func BindToReader ¶
func BindToReader[ R, E, S1, T any]( setter func(T) S1, ) func(Reader[R, T]) ReaderEither[R, E, S1]
func Chain ¶
func Chain[E, L, A, B any](f func(A) ReaderEither[E, L, B]) func(ReaderEither[E, L, A]) ReaderEither[E, L, B]
func ChainEitherK ¶
func ChainEitherK[E, L, A, B any](f func(A) Either[L, B]) func(ma ReaderEither[E, L, A]) ReaderEither[E, L, B]
func ChainLeft ¶ added in v2.1.0
func ChainLeft[R, EA, EB, A any](f Kleisli[R, EB, EA, A]) func(ReaderEither[R, EA, A]) ReaderEither[R, EB, A]
ChainLeft is the curried version of MonadChainLeft. It returns a function that chains a computation on the left (error) side of a ReaderEither.
This is particularly useful in functional composition pipelines where you want to handle errors by performing another computation that may also fail, with access to configuration context.
Note: This is functionally identical to [OrElseW]. They are different names for the same operation. Use ChainLeft when emphasizing the monadic chaining perspective on the error channel, and [OrElseW] when emphasizing error recovery/fallback semantics.
Parameters:
- f: A Kleisli function that takes an error of type EA and returns a ReaderEither with error type EB
Returns:
- A function that transforms a ReaderEither with error type EA to one with error type EB
Example:
type Config struct{ retryLimit int }
// Create a reusable error handler with config access
recoverFromError := ChainLeft(func(err string) readereither.ReaderEither[Config, int, string] {
if strings.Contains(err, "retryable") {
return Asks[int](func(cfg Config) either.Either[int, string] {
if cfg.retryLimit > 0 {
return either.Right[int]("recovered")
}
return either.Left[string](500)
})
}
return Left[Config, string](404)
})
result := F.Pipe1(
Left[Config, string]("retryable error"),
recoverFromError,
)(Config{retryLimit: 3})
func ChainOptionK ¶
func ChainReaderK ¶
func Flap ¶
func Flap[L, E, B, A any](a A) func(ReaderEither[L, E, func(A) B]) ReaderEither[L, E, B]
func Fold ¶
func Fold[E, L, A, B any](onLeft func(L) Reader[E, B], onRight func(A) Reader[E, B]) func(ReaderEither[E, L, A]) Reader[E, B]
func FromPredicate ¶
func GetOrElse ¶
func GetOrElse[E, L, A any](onLeft func(L) Reader[E, A]) func(ReaderEither[E, L, A]) Reader[E, A]
func Let ¶
func Let[R, E, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) T, ) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
Let attaches the result of a computation to a context [S1] to produce a context [S2]
func LetL ¶
func LetL[R, E, S, T any]( lens L.Lens[S, T], f func(T) T, ) func(ReaderEither[R, E, S]) ReaderEither[R, E, 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, E, S1, S2, T any]( setter func(T) func(S1) S2, b T, ) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2]
LetTo attaches the a value to a context [S1] to produce a context [S2]
func LetToL ¶
func LetToL[R, E, S, T any]( lens L.Lens[S, T], b T, ) func(ReaderEither[R, E, S]) ReaderEither[R, E, 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 Local ¶
func Local[E, A, R1, R2 any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A]
Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s `contramap`).
func MapLeft ¶
func MapLeft[C, E1, E2, A any](f func(E1) E2) func(ReaderEither[C, E1, A]) ReaderEither[C, E2, A]
MapLeft applies a mapping function to the error channel
func OrLeft ¶
func OrLeft[A, L1, E, L2 any](onLeft func(L1) Reader[E, L2]) func(ReaderEither[E, L1, A]) ReaderEither[E, L2, A]
func Read ¶
func Read[E1, A, E any](e E) func(ReaderEither[E, E1, A]) Either[E1, A]
Read applies a context to a reader to obtain its value
func ReadEither ¶ added in v2.1.10
func ReadEither[E1, A, E any](e Either[E1, E]) func(ReaderEither[E, E1, A]) Either[E1, A]
ReadEither applies a context wrapped in an Either to a ReaderEither to obtain its result. This function is useful when the context itself may be absent or invalid (represented as Left), allowing you to conditionally execute a ReaderEither computation based on the availability of the required context.
If the context Either is Left, it short-circuits and returns Left without executing the ReaderEither. If the context Either is Right, it extracts the context value and applies it to the ReaderEither, returning the resulting Either.
This is particularly useful in scenarios where:
- Configuration or dependencies may be missing or invalid
- You want to chain context validation with computation execution
- You need to propagate context errors through your computation pipeline
Type Parameters:
- E1: The error type (Left value) of both the input Either and the ReaderEither result
- A: The success type (Right value) of the ReaderEither result
- E: The context/environment type that the ReaderEither depends on
Parameters:
- e: An Either[E1, E] representing the context that may or may not be available
Returns:
- A function that takes a ReaderEither[E, E1, A] and returns Either[E1, A]
Example:
type Config struct{ apiKey string }
type ConfigError struct{ msg string }
// A computation that needs config
fetchData := func(cfg Config) either.Either[ConfigError, string] {
if cfg.apiKey == "" {
return either.Left[string](ConfigError{"missing API key"})
}
return either.Right[ConfigError]("data from API")
}
// Context may be invalid
validConfig := either.Right[ConfigError](Config{apiKey: "secret"})
invalidConfig := either.Left[Config](ConfigError{"config not found"})
computation := readereither.FromReader[ConfigError](fetchData)
// With valid config - executes computation
result1 := readereither.ReadEither(validConfig)(computation)
// result1 = Right("data from API")
// With invalid config - short-circuits without executing
result2 := readereither.ReadEither(invalidConfig)(computation)
// result2 = Left(ConfigError{"config not found"})
func Traverse ¶
func Traverse[R2, R1, E, A, B any]( f Kleisli[R1, E, A, B], ) func(ReaderEither[R2, E, A]) Kleisli[R2, E, R1, B]
Traverse transforms a ReaderEither computation by applying a function that produces another ReaderEither, effectively swapping the order of environment parameters.
This function is useful when you have a computation that depends on environment R2 and produces a value of type A, and you want to transform it using a function that takes A and produces a computation depending on environment R1. The result is a curried function that takes R2 first, then R1, and produces an Either[E, B].
Type Parameters:
- R2: The outer environment type (provided first)
- R1: The inner environment type (provided second)
- E: The error type
- A: The input value type
- B: The output value type
Parameters:
- f: A Kleisli arrow that transforms A into a ReaderEither[R1, E, B]
Returns:
- A function that takes a ReaderEither[R2, E, A] and returns a Kleisli[R2, E, R1, B], which is func(R2) ReaderEither[R1, E, B]
The function preserves error handling at both levels while reordering the environment dependencies.
Example:
type Database struct {
ConnectionString string
}
type Config struct {
Timeout int
}
// Original: ReaderEither[Config, error, int] - takes Config, may fail, produces int
original := func(cfg Config) either.Either[error, int] {
if cfg.Timeout <= 0 {
return either.Left[int](errors.New("invalid timeout"))
}
return either.Right[error](cfg.Timeout * 10)
}
// Kleisli function: transforms int to ReaderEither[Database, error, string]
kleisli := func(value int) ReaderEither[Database, error, string] {
return func(db Database) either.Either[error, string] {
if S.IsEmpty(db.ConnectionString) {
return either.Left[string](errors.New("empty connection string"))
}
return either.Right[error](fmt.Sprintf("%s:%d", db.ConnectionString, value))
}
}
// Apply Traverse to get: func(ReaderEither[Config, error, int]) func(Database) ReaderEither[Config, error, string]
traversed := Traverse[Config, Database, error, int, string](kleisli)
result := traversed(original)
db := Database{ConnectionString: "localhost:5432"}
cfg := Config{Timeout: 30}
// Apply database first to get a function that takes config
configReader := result(db)
// Then apply config to get the final result
finalResult := configReader(cfg)
// finalResult is Either[error, string] = Right("localhost:5432:300")
func TraverseArray ¶
func TraverseArray[E, L, A, B any](f func(A) ReaderEither[E, L, B]) func([]A) ReaderEither[E, L, []B]
TraverseArray transforms each element of an array using a function that returns a ReaderEither, then collects the results into a single ReaderEither containing an array.
If any transformation fails, the entire operation fails with the first error encountered. All transformations are executed sequentially.
Type parameters:
- E: The context type
- L: The error type
- A: The input element type
- B: The output element type
Parameters:
- f: A function that transforms each element into a ReaderEither
Returns:
A function that takes an array and returns a ReaderEither of an array
Example:
fetchUsers := TraverseArray(func(id int) ReaderEither[Config, error, User] {
return fetchUser(id)
})
result := fetchUsers([]int{1, 2, 3})
// result(cfg) returns Right([user1, user2, user3]) or Left(error)
func TraverseArrayWithIndex ¶
func TraverseArrayWithIndex[E, L, A, B any](f func(int, A) ReaderEither[E, L, B]) func([]A) ReaderEither[E, L, []B]
TraverseArrayWithIndex is like TraverseArray but the transformation function also receives the index.
This is useful when the transformation depends on the element's position in the array.
Type parameters:
- E: The context type
- L: The error type
- A: The input element type
- B: The output element type
Parameters:
- f: A function that transforms each element and its index into a ReaderEither
Returns:
A function that takes an array and returns a ReaderEither of an array
Example:
processWithIndex := TraverseArrayWithIndex(func(i int, val string) ReaderEither[Config, error, string] {
return Of[Config, error](fmt.Sprintf("%d: %s", i, val))
})
func TraverseReader ¶
Types ¶
type Kleisli ¶
type Kleisli[R, E, A, B any] = Reader[A, ReaderEither[R, E, B]]
Kleisli represents a Kleisli arrow for the ReaderEither monad. It's a function from A to ReaderEither[R, E, B], used for composing operations that depend on an environment and may fail.
func Contramap ¶ added in v2.1.6
func Contramap[E, A, R1, R2 any](f func(R2) R1) Kleisli[R2, E, ReaderEither[R1, E, A], A]
Contramap changes the value of the local environment during the execution of a ReaderEither. This is the contravariant functor operation that transforms the input environment.
See: https://github.com/fantasyland/fantasy-land?tab=readme-ov-file#profunctor
Contramap is useful for adapting a ReaderEither to work with a different environment type by providing a function that converts the new environment to the expected one.
Type Parameters:
- E: The error type (unchanged)
- A: The success type (unchanged)
- R2: The new input environment type
- R1: The original environment type expected by the ReaderEither
Parameters:
- f: Function to transform the environment from R2 to R1
Returns:
- A Kleisli arrow that takes a ReaderEither[R1, E, A] and returns a ReaderEither[R2, E, A]
func OrElse ¶
func OrElse[R, E1, E2, A any](onLeft Kleisli[R, E2, E1, A]) Kleisli[R, E2, ReaderEither[R, E1, A], A]
OrElse recovers from a Left (error) by providing an alternative computation with access to the reader context. If the ReaderEither is Right, it returns the value unchanged. If the ReaderEither is Left, it applies the provided function to the error value, which returns a new ReaderEither that replaces the original.
This is useful for error recovery, fallback logic, or chaining alternative computations that need access to configuration or dependencies. The error type can be widened from E1 to E2.
Example:
type Config struct{ fallbackValue int }
// Recover using config-dependent fallback
recover := readereither.OrElse(func(err error) readereither.ReaderEither[Config, error, int] {
if err.Error() == "not found" {
return readereither.Asks[error](func(cfg Config) either.Either[error, int] {
return either.Right[error](cfg.fallbackValue)
})
}
return readereither.Left[Config, int](err)
})
result := recover(readereither.Left[Config, int](errors.New("not found")))(Config{fallbackValue: 42}) // Right(42)
func Promap ¶ added in v2.1.6
func Promap[R, E, A, D, B any](f func(D) R, g func(A) B) Kleisli[D, E, ReaderEither[R, E, A], B]
Promap is the profunctor map operation that transforms both the input and output of a ReaderEither. It applies f to the input environment (contravariantly) and g to the output value (covariantly).
See: https://github.com/fantasyland/fantasy-land?tab=readme-ov-file#profunctor
This operation allows you to:
- Adapt the environment type before passing it to the ReaderEither (via f)
- Transform the success value after the computation completes (via g)
The error type E remains unchanged through the transformation.
Type Parameters:
- R: The original environment type expected by the ReaderEither
- E: The error type (unchanged)
- A: The original success type produced by the ReaderEither
- D: The new input environment type
- B: The new output success type
Parameters:
- f: Function to transform the input environment from D to R (contravariant)
- g: Function to transform the output success value from A to B (covariant)
Returns:
- A Kleisli arrow that takes a ReaderEither[R, E, A] and returns a ReaderEither[D, E, B]
func Sequence ¶
func Sequence[R1, R2, E, A any](ma ReaderEither[R2, E, ReaderEither[R1, E, A]]) Kleisli[R2, E, R1, A]
Sequence swaps the order of nested environment parameters in a ReaderEither computation.
This function takes a ReaderEither that produces another ReaderEither and returns a reader.Kleisli that reverses the order of the environment parameters. The result is a curried function that takes R2 first, then R1, and produces an Either[E, A].
Type Parameters:
- R1: The first environment type (becomes inner after flip)
- R2: The second environment type (becomes outer after flip)
- E: The error type
- A: The success value type
Parameters:
- ma: A ReaderEither that takes R2 and may produce a ReaderEither[R1, E, A]
Returns:
- A reader.Kleisli[R2, R1, Either[E, A]], which is func(R2) func(R1) Either[E, A]
The function preserves error handling at both levels. Errors from the outer computation become errors in the inner Either result.
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 ReaderEither[Database, error, string]
original := func(cfg Config) either.Either[error, ReaderEither[Database, error, string]] {
if cfg.Timeout <= 0 {
return either.Left[ReaderEither[Database, error, string]](errors.New("invalid timeout"))
}
return either.Right[error](func(db Database) either.Either[error, string] {
if S.IsEmpty(db.ConnectionString) {
return either.Left[string](errors.New("empty connection string"))
}
return either.Right[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 Either[error, string]
func SequenceReader ¶
func SequenceReader[R1, R2, E, A any](ma ReaderEither[R2, E, Reader[R1, A]]) Kleisli[R2, E, R1, 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 ReaderEither. It takes a ReaderEither that produces a Reader and returns a Reader that produces a ReaderEither.
Type Parameters:
- R1: The first environment type (becomes outer after flip)
- R2: The second environment type (becomes inner after flip)
- E: The error type (only present in the ReaderEither layer)
- A: The success value type
Parameters:
- ma: A ReaderEither that takes R2 and may produce a Reader[R1, A]
Returns:
- A reader.Kleisli[R2, R1, Either[E, A]], which is func(R2) func(R1) Either[E, A]
The function preserves error handling from the outer ReaderEither layer. If the outer computation fails, the error is propagated to the inner ReaderEither result.
Example:
type Database struct {
ConnectionString string
}
type Config struct {
Timeout int
}
// Original: takes Config, may fail, produces Reader[Database, string]
original := func(cfg Config) either.Either[error, Reader[Database, string]] {
if cfg.Timeout <= 0 {
return either.Left[Reader[Database, string]](errors.New("invalid timeout"))
}
return either.Right[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 Either[error, string]
func TailRec ¶
func TailRec[R, E, A, B any](f Kleisli[R, E, A, tailrec.Trampoline[A, B]]) Kleisli[R, E, A, B]
type Operator ¶
type Operator[R, E, A, B any] = Kleisli[R, E, ReaderEither[R, E, A], B]
Operator represents a function that transforms one ReaderEither into another. It takes a ReaderEither[R, E, A] and produces a ReaderEither[R, E, B].
func ApReaderS ¶
func ApReaderS[ R, E, S1, S2, T any]( setter func(T) func(S1) S2, fa Reader[R, T], ) Operator[R, E, S1, S2]
func ChainFirstLeft ¶ added in v2.1.0
func ChainFirstLeft[A, R, EA, EB, B any](f Kleisli[R, EB, EA, B]) Operator[R, EA, A, A]
ChainFirstLeft is the curried version of MonadChainFirstLeft. It returns a function that chains a computation on the left (error) side while always preserving the original error.
This is particularly useful for adding error handling side effects (like logging, metrics, or notifications) in a functional pipeline. The original error is always returned regardless of what f returns (Left or Right), ensuring the error path is preserved.
Parameters:
- f: A function that takes an error of type EA and returns a ReaderEither (typically for side effects)
Returns:
- A function that performs the side effect but always returns the original error if input was Left
Example:
type Config struct{ metricsEnabled bool }
// Create a reusable error logger
logError := ChainFirstLeft(func(err string) readereither.ReaderEither[Config, any, int] {
return Asks[any](func(cfg Config) either.Either[any, int] {
if cfg.metricsEnabled {
metrics.RecordError(err)
}
return either.Right[any](0)
})
})
result := F.Pipe1(
Left[Config, int]("validation failed"),
logError, // records the error in metrics
)
// result is always Left("validation failed")
type Reader ¶
Reader represents a computation that depends on an environment R and produces a value A.
func MonadFold ¶ added in v2.1.0
func MonadFold[E, L, A, B any](ma ReaderEither[E, L, A], onLeft func(L) Reader[E, B], onRight func(A) Reader[E, B]) Reader[E, B]
MonadFold applies one of two functions depending on the Either value. If Left, applies onLeft function. If Right, applies onRight function. Both functions return a Reader[E, B].
type ReaderEither ¶
ReaderEither represents a computation that depends on an environment R and can fail with an error E or succeed with a value A. It combines Reader (dependency injection) with Either (error handling).
func Do ¶
func Do[R, E, S any]( empty S, ) ReaderEither[R, E, S]
Do creates an empty context of type [S] to be used with the Bind operation. This is the starting point for do-notation style composition.
Example:
type State struct {
User User
Config Config
}
type Env struct {
UserService UserService
ConfigService ConfigService
}
result := readereither.Do[Env, error](State{})
func Flatten ¶
func Flatten[E, L, A any](mma ReaderEither[E, L, ReaderEither[E, L, A]]) ReaderEither[E, L, A]
func FromEither ¶
func FromEither[E, L, A any](e Either[L, A]) ReaderEither[E, L, A]
func FromReader ¶
func FromReader[L, E, A any](r Reader[E, A]) ReaderEither[E, L, A]
func LeftReader ¶
func LeftReader[A, E, L any](l Reader[E, L]) ReaderEither[E, L, A]
func MonadAp ¶
func MonadAp[B, E, L, A any](fab ReaderEither[E, L, func(A) B], fa ReaderEither[E, L, A]) ReaderEither[E, L, B]
func MonadBiMap ¶
func MonadBiMap[E, E1, E2, A, B any](fa ReaderEither[E, E1, A], f func(E1) E2, g func(A) B) ReaderEither[E, E2, B]
func MonadChain ¶
func MonadChain[E, L, A, B any](ma ReaderEither[E, L, A], f func(A) ReaderEither[E, L, B]) ReaderEither[E, L, B]
func MonadChainEitherK ¶
func MonadChainEitherK[E, L, A, B any](ma ReaderEither[E, L, A], f func(A) Either[L, B]) ReaderEither[E, L, B]
func MonadChainFirstLeft ¶ added in v2.1.0
func MonadChainFirstLeft[A, R, EA, EB, B any](ma ReaderEither[R, EA, A], f Kleisli[R, EB, EA, B]) ReaderEither[R, EA, A]
MonadChainFirstLeft chains a computation on the left (error) side but always returns the original error. If the input is a Left value, it applies the function f to the error and executes the resulting computation, but always returns the original Left error regardless of what f returns (Left or Right). If the input is a Right value, it passes through unchanged without calling f.
This is useful for side effects on errors (like logging or metrics) where you want to perform an action when an error occurs but always propagate the original error, ensuring the error path is preserved.
Parameters:
- ma: The input ReaderEither that may contain an error of type EA
- f: A function that takes an error of type EA and returns a ReaderEither (typically for side effects)
Returns:
- A ReaderEither with the original error preserved if input was Left, or the original Right value
Example:
type Config struct{ loggingEnabled bool }
// Log errors but preserve the original error
result := MonadChainFirstLeft(
Left[Config, int]("database error"),
func(err string) readereither.ReaderEither[Config, string, int] {
return Asks[string](func(cfg Config) either.Either[string, int] {
if cfg.loggingEnabled {
log.Printf("Error: %s", err)
}
return either.Right[string](0)
})
},
)
// result will always be Left("database error")
func MonadChainLeft ¶ added in v2.1.0
func MonadChainLeft[R, EA, EB, A any](fa ReaderEither[R, EA, A], f Kleisli[R, EB, EA, A]) ReaderEither[R, EB, A]
MonadChainLeft chains a computation on the left (error) side of a ReaderEither. If the input is a Left value, it applies the function f to transform the error and potentially change the error type from EA to EB. If the input is a Right value, it passes through unchanged.
This is useful for error recovery or error transformation scenarios where you want to handle errors by performing another computation that may also fail, with access to configuration context.
Note: This is functionally identical to the uncurried form of [OrElseW]. Use ChainLeft when emphasizing the monadic chaining perspective, and [OrElseW] for error recovery semantics.
Parameters:
- fa: The input ReaderEither that may contain an error of type EA
- f: A Kleisli function that takes an error of type EA and returns a ReaderEither with error type EB
Returns:
- A ReaderEither with the potentially transformed error type EB
Example:
type Config struct{ fallbackValue int }
type ValidationError struct{ field string }
type SystemError struct{ code int }
// Recover from validation errors using config
result := MonadChainLeft(
Left[Config, int](ValidationError{"username"}),
func(ve ValidationError) readereither.ReaderEither[Config, SystemError, int] {
if ve.field == "username" {
return Asks[SystemError](func(cfg Config) either.Either[SystemError, int] {
return either.Right[SystemError](cfg.fallbackValue)
})
}
return Left[Config, int](SystemError{400})
},
)
func MonadChainReaderK ¶
func MonadFlap ¶
func MonadFlap[L, E, A, B any](fab ReaderEither[L, E, func(A) B], a A) ReaderEither[L, E, B]
func MonadMap ¶
func MonadMap[E, L, A, B any](fa ReaderEither[E, L, A], f func(A) B) ReaderEither[E, L, B]
func MonadMapLeft ¶
func MonadMapLeft[C, E1, E2, A any](fa ReaderEither[C, E1, A], f func(E1) E2) ReaderEither[C, E2, A]
func MonadTapLeft ¶ added in v2.1.0
func MonadTapLeft[A, R, EA, EB, B any](ma ReaderEither[R, EA, A], f Kleisli[R, EB, EA, B]) ReaderEither[R, EA, A]
func RightReader ¶
func RightReader[L, E, A any](r Reader[E, A]) ReaderEither[E, L, A]
func SequenceArray ¶
func SequenceArray[E, L, A any](ma []ReaderEither[E, L, A]) ReaderEither[E, L, []A]
SequenceArray converts an array of ReaderEither into a ReaderEither of an array.
This is useful when you have multiple independent computations and want to execute them all and collect their results. If any computation fails, the entire operation fails with the first error.
Type parameters:
- E: The context type
- L: The error type
- A: The element type
Parameters:
- ma: An array of ReaderEither computations
Returns:
A ReaderEither that produces an array of results
Example:
computations := []ReaderEither[Config, error, int]{
fetchCount("users"),
fetchCount("posts"),
fetchCount("comments"),
}
result := SequenceArray(computations)
// result(cfg) returns Right([userCount, postCount, commentCount]) or Left(error)