Documentation
¶
Overview ¶
Package readeroption provides a monad transformer that combines the Reader and Option monads.
Fantasy Land Specification ¶
This is a monad transformer combining:
- Reader monad: https://github.com/fantasyland/fantasy-land
- Maybe (Option) monad: https://github.com/fantasyland/fantasy-land#maybe
Implemented Fantasy Land algebras:
- Functor: https://github.com/fantasyland/fantasy-land#functor
- 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
ReaderOption[R, A] represents a computation that:
- Depends on a shared environment of type R (Reader monad)
- May fail to produce a value of type A (Option monad)
This is useful for computations that need access to configuration, context, or dependencies while also being able to represent the absence of a value without using errors.
The ReaderOption monad is defined as: Reader[R, Option[A]]
Key operations:
- Of: Wraps a value in a ReaderOption
- None: Creates a ReaderOption representing no value
- Map: Transforms the value inside a ReaderOption
- Chain: Sequences ReaderOption computations
- Ask/Asks: Accesses the environment
Example:
type Config struct {
DatabaseURL string
Timeout int
}
// A computation that may or may not find a user
func findUser(id int) readeroption.ReaderOption[Config, User] {
return readeroption.Asks(func(cfg Config) option.Option[User] {
// Use cfg.DatabaseURL to query database
// Return Some(user) if found, None() if not found
})
}
// Chain multiple operations
result := F.Pipe2(
findUser(123),
readeroption.Chain(func(user User) readeroption.ReaderOption[Config, Profile] {
return loadProfile(user.ProfileID)
}),
readeroption.Map(func(profile Profile) string {
return profile.DisplayName
}),
)
// Execute with config
config := Config{DatabaseURL: "localhost:5432", Timeout: 30}
displayName := result(config) // Returns Option[string]
Index ¶
- func Curry2[R, T1, T2, A any](f func(R, T1, T2) (A, bool)) func(T1) func(T2) ReaderOption[R, A]
- func Curry3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, bool)) func(T1) func(T2) func(T3) ReaderOption[R, A]
- func Fold[E, A, B any](onNone Reader[E, B], onRight reader.Kleisli[E, A, B]) reader.Operator[E, Option[A], B]
- func From0[R, A any](f func(R) (A, bool)) func() ReaderOption[R, A]
- func From2[R, T1, T2, A any](f func(R, T1, T2) (A, bool)) func(T1, T2) ReaderOption[R, A]
- func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, bool)) func(T1, T2, T3) ReaderOption[R, A]
- func GetOrElse[E, A any](onNone Reader[E, A]) reader.Operator[E, Option[A], A]
- func Local[A, R2, R1 any](f func(R2) R1) func(ReaderOption[R1, A]) ReaderOption[R2, A]
- func Read[A, E any](e E) func(ReaderOption[E, A]) Option[A]
- func Sequence[R1, R2, A any](ma ReaderOption[R2, ReaderOption[R1, A]]) reader.Kleisli[R2, R1, Option[A]]
- func SequenceReader[R1, R2, A any](ma ReaderOption[R2, Reader[R1, A]]) reader.Kleisli[R2, R1, Option[A]]
- func Traverse[R2, R1, A, B any](f Kleisli[R1, A, B]) func(ReaderOption[R2, A]) Kleisli[R2, R1, B]
- func TraverseArrayWithIndex[E, A, B any](f func(int, A) ReaderOption[E, B]) func([]A) ReaderOption[E, []B]
- func TraverseReader[R2, R1, A, B any](f reader.Kleisli[R1, A, B]) func(ReaderOption[R2, A]) Kleisli[R2, R1, B]
- func Uncurry1[R, T1, A any](f func(T1) ReaderOption[R, A]) func(R, T1) (A, bool)
- func Uncurry2[R, T1, T2, A any](f func(T1) func(T2) ReaderOption[R, A]) func(R, T1, T2) (A, bool)
- func Uncurry3[R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderOption[R, A]) func(R, T1, T2, T3) (A, bool)
- type Either
- type Kleisli
- func Curry1[R, T1, A any](f func(R, T1) (A, bool)) Kleisli[R, T1, A]
- func From1[R, T1, A any](f func(R, T1) (A, bool)) Kleisli[R, T1, A]
- func FromPredicate[E, A any](pred Predicate[A]) Kleisli[E, A, A]
- func TailRec[R, A, B any](f Kleisli[R, A, tailrec.Trampoline[A, B]]) Kleisli[R, A, B]
- func TraverseArray[E, A, B any](f Kleisli[E, A, B]) Kleisli[E, []A, []B]
- type Lazy
- type Operator
- func Alt[E, A any](that ReaderOption[E, A]) Operator[E, A, A]
- func Ap[B, E, A any](fa ReaderOption[E, A]) Operator[E, func(A) B, B]
- func ApS[R, S1, S2, T any](setter func(T) func(S1) S2, fa ReaderOption[R, T]) Operator[R, S1, S2]
- func ApSL[R, S, T any](lens L.Lens[S, T], fa ReaderOption[R, T]) Operator[R, S, S]
- func Bind[R, S1, S2, T any](setter func(T) func(S1) S2, f Kleisli[R, S1, T]) Operator[R, S1, S2]
- func BindL[R, S, T any](lens L.Lens[S, T], f Kleisli[R, T, T]) Operator[R, S, S]
- func BindTo[R, S1, T any](setter func(T) S1) Operator[R, T, S1]
- func Chain[E, A, B any](f Kleisli[E, A, B]) Operator[E, A, B]
- func ChainOptionK[E, A, B any](f O.Kleisli[A, B]) Operator[E, A, B]
- func Flap[E, B, A any](a A) Operator[E, 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 func(T) 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[E, A, B any](f func(A) B) Operator[E, A, B]
- type Option
- type Predicate
- type Reader
- type ReaderOption
- func Ask[E any]() ReaderOption[E, E]
- func Asks[E, A any](r Reader[E, A]) ReaderOption[E, A]
- func Curry0[R, A any](f func(R) (A, bool)) ReaderOption[R, A]
- func Do[R, S any](empty S) ReaderOption[R, S]
- func Flatten[E, A any](mma ReaderOption[E, ReaderOption[E, A]]) ReaderOption[E, A]
- func FromOption[E, A any](e Option[A]) ReaderOption[E, A]
- func FromReader[E, A any](r Reader[E, A]) ReaderOption[E, A]
- func MonadAlt[E, A any](fa, that ReaderOption[E, A]) ReaderOption[E, A]
- func MonadAp[E, A, B any](fab ReaderOption[E, func(A) B], fa ReaderOption[E, A]) ReaderOption[E, B]
- func MonadChain[E, A, B any](ma ReaderOption[E, A], f Kleisli[E, A, B]) ReaderOption[E, B]
- func MonadChainOptionK[E, A, B any](ma ReaderOption[E, A], f O.Kleisli[A, B]) ReaderOption[E, B]
- func MonadFlap[E, A, B any](fab ReaderOption[E, func(A) B], a A) ReaderOption[E, B]
- func MonadMap[E, A, B any](fa ReaderOption[E, A], f func(A) B) ReaderOption[E, B]
- func None[E, A any]() ReaderOption[E, A]
- func Of[E, A any](a A) ReaderOption[E, A]
- func SequenceArray[E, A any](ma []ReaderOption[E, A]) ReaderOption[E, []A]
- func SequenceT1[E, A any](a ReaderOption[E, A]) ReaderOption[E, T.Tuple1[A]]
- func SequenceT2[E, A, B any](a ReaderOption[E, A], b ReaderOption[E, B]) ReaderOption[E, T.Tuple2[A, B]]
- func SequenceT3[E, A, B, C any](a ReaderOption[E, A], b ReaderOption[E, B], c ReaderOption[E, C]) ReaderOption[E, T.Tuple3[A, B, C]]
- func SequenceT4[E, A, B, C, D any](a ReaderOption[E, A], b ReaderOption[E, B], c ReaderOption[E, C], ...) ReaderOption[E, T.Tuple4[A, B, C, D]]
- func Some[E, A any](r A) ReaderOption[E, A]
- func SomeReader[E, A any](r Reader[E, A]) ReaderOption[E, A]
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Curry2 ¶
Curry2 converts a function that takes a context and two arguments, returning (A, bool), into a curried function that returns a ReaderOption.
Example:
query := func(ctx context.Context, table string, id int) (Record, bool) {
// Query database using context
return record, found
}
ro := readeroption.Curry2(query)
result := ro("users")(123)(ctx) // Returns option.Some(record) or option.None()
func Curry3 ¶
func Curry3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, bool)) func(T1) func(T2) func(T3) ReaderOption[R, A]
Curry3 converts a function that takes a context and three arguments, returning (A, bool), into a curried function that returns a ReaderOption.
Example:
complexQuery := func(ctx context.Context, db string, table string, id int) (Record, bool) {
// Query database using context
return record, found
}
ro := readeroption.Curry3(complexQuery)
result := ro("mydb")("users")(123)(ctx)
func Fold ¶
func Fold[E, A, B any](onNone Reader[E, B], onRight reader.Kleisli[E, A, B]) reader.Operator[E, Option[A], B]
Fold extracts the value from a ReaderOption by providing handlers for both cases. The onNone handler is called if the computation returns None. The onRight handler is called if the computation returns Some(a).
Example:
result := readeroption.Fold(
func() reader.Reader[Config, string] { return reader.Of[Config]("not found") },
func(user User) reader.Reader[Config, string] { return reader.Of[Config](user.Name) },
)(findUser(123))
func From0 ¶
From0 converts a function that takes only a context and returns (A, bool) into a function that returns a ReaderOption[R, A].
Example:
getConfig := func(ctx context.Context) (Config, bool) {
cfg, ok := ctx.Value("config").(Config)
return cfg, ok
}
roFunc := readeroption.From0(getConfig)
ro := roFunc() // Returns a ReaderOption[context.Context, Config]
result := ro(ctx) // Returns option.Some(config) or option.None()
func From2 ¶
From2 converts a function that takes a context and two arguments, returning (A, bool), into a function that takes two arguments (uncurried) and returns a ReaderOption.
Example:
query := func(ctx context.Context, table string, id int) (Record, bool) {
// Query database using context
return record, found
}
roFunc := readeroption.From2(query)
ro := roFunc("users", 123) // Returns a ReaderOption[context.Context, Record]
result := ro(ctx) // Returns option.Some(record) or option.None()
func From3 ¶
func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, bool)) func(T1, T2, T3) ReaderOption[R, A]
From3 converts a function that takes a context and three arguments, returning (A, bool), into a function that takes three arguments (uncurried) and returns a ReaderOption.
Example:
complexQuery := func(ctx context.Context, db string, table string, id int) (Record, bool) {
// Query database using context
return record, found
}
roFunc := readeroption.From3(complexQuery)
ro := roFunc("mydb", "users", 123) // Returns a ReaderOption[context.Context, Record]
result := ro(ctx) // Returns option.Some(record) or option.None()
func GetOrElse ¶
GetOrElse returns the value from a ReaderOption, or a default value if it's None.
Example:
result := readeroption.GetOrElse(
func() reader.Reader[Config, User] { return reader.Of[Config](defaultUser) },
)(findUser(123))
func Local ¶
func Local[A, R2, R1 any](f func(R2) R1) func(ReaderOption[R1, A]) ReaderOption[R2, A]
Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s `contramap`).
This allows you to transform the environment before passing it to a computation.
Example:
type GlobalConfig struct { DB DBConfig }
type DBConfig struct { Host string }
// A computation that needs DBConfig
query := func(cfg DBConfig) option.Option[User] { ... }
// Transform GlobalConfig to DBConfig
result := readeroption.Local(func(g GlobalConfig) DBConfig { return g.DB })(
readeroption.Asks(query),
)
func Read ¶
func Read[A, E any](e E) func(ReaderOption[E, A]) Option[A]
Read applies a context to a reader to obtain its value. This executes the ReaderOption computation with the given environment.
Example:
ro := readeroption.Of[Config](42) result := readeroption.Read[int](myConfig)(ro) // Returns option.Some(42)
func Sequence ¶
func Sequence[R1, R2, A any](ma ReaderOption[R2, ReaderOption[R1, A]]) reader.Kleisli[R2, R1, Option[A]]
Sequence swaps the order of nested environment parameters in a ReaderOption computation.
This function takes a ReaderOption that produces another ReaderOption 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 Option[A].
Type Parameters:
- R1: The first environment type (becomes inner after flip)
- R2: The second environment type (becomes outer after flip)
- A: The value type
Parameters:
- ma: A ReaderOption that takes R2 and may produce a ReaderOption[R1, A]
Returns:
- A reader.Kleisli[R2, R1, Option[A]], which is func(R2) func(R1) Option[A]
The function preserves optional values at both levels. If either the outer or inner computation produces None, the final result will be None.
Example:
import S "github.com/IBM/fp-go/v2/string"
type Database struct {
ConnectionString string
}
type Config struct {
Timeout int
}
// Original: takes Config, may produce ReaderOption[Database, string]
original := func(cfg Config) option.Option[ReaderOption[Database, string]] {
if cfg.Timeout <= 0 {
return option.None[ReaderOption[Database, string]]()
}
return option.Some(func(db Database) option.Option[string] {
if S.IsEmpty(db.ConnectionString) {
return option.None[string]()
}
return option.Some(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 Option[string]
func SequenceReader ¶
func SequenceReader[R1, R2, A any](ma ReaderOption[R2, Reader[R1, A]]) reader.Kleisli[R2, R1, Option[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 optional values) rather than another ReaderOption. It takes a ReaderOption that produces a Reader and returns a Reader that produces a ReaderOption.
Type Parameters:
- R1: The first environment type (becomes outer after flip)
- R2: The second environment type (becomes inner after flip)
- A: The value type
Parameters:
- ma: A ReaderOption that takes R2 and may produce a Reader[R1, A]
Returns:
- A reader.Kleisli[R2, R1, Option[A]], which is func(R2) func(R1) Option[A]
The function preserves the optional nature from the outer ReaderOption layer. If the outer computation produces None, the inner ReaderOption result will be None.
Example:
type Database struct {
ConnectionString string
}
type Config struct {
Timeout int
}
// Original: takes Config, may produce Reader[Database, string]
original := func(cfg Config) option.Option[Reader[Database, string]] {
if cfg.Timeout <= 0 {
return option.None[Reader[Database, string]]()
}
return option.Some(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 Option[string]
func Traverse ¶
func Traverse[R2, R1, A, B any]( f Kleisli[R1, A, B], ) func(ReaderOption[R2, A]) Kleisli[R2, R1, B]
func TraverseArrayWithIndex ¶
func TraverseArrayWithIndex[E, A, B any](f func(int, A) ReaderOption[E, B]) func([]A) ReaderOption[E, []B]
TraverseArrayWithIndex is like TraverseArray but the function also receives the index of each element.
Example:
type DB struct { ... }
processWithIndex := func(idx int, value string) readeroption.ReaderOption[DB, Result] {
// Use idx in processing
return readeroption.Asks(func(db DB) option.Option[Result] { ... })
}
values := []string{"a", "b", "c"}
result := readeroption.TraverseArrayWithIndex[DB](processWithIndex)(values)
func TraverseReader ¶
func Uncurry1 ¶
Uncurry1 converts a curried ReaderOption function back to a Go function that takes a context and one argument, returning (A, bool).
Example:
ro := func(id int) readeroption.ReaderOption[context.Context, User] { ... }
findUser := readeroption.Uncurry1(ro)
user, found := findUser(ctx, 123)
Types ¶
type Kleisli ¶
type Kleisli[R, A, B any] = Reader[A, ReaderOption[R, B]]
Kleisli represents a function that takes a value A and returns a ReaderOption[R, B]. This is the type of functions used with Chain/Bind operations.
func Curry1 ¶
Curry1 converts a function that takes a context and one argument, returning (A, bool), into a curried function that returns a ReaderOption.
Example:
findUser := func(ctx context.Context, id int) (User, bool) {
// Query database using context
return user, found
}
ro := readeroption.Curry1(findUser)
result := ro(123)(ctx) // Returns option.Some(user) or option.None()
func From1 ¶
From1 converts a function that takes a context and one argument, returning (A, bool), into a function that takes one argument and returns a ReaderOption.
This is equivalent to Curry1 but provided for consistency with the From naming convention.
Example:
findUser := func(ctx context.Context, id int) (User, bool) {
// Query database using context
return user, found
}
roFunc := readeroption.From1(findUser)
ro := roFunc(123) // Returns a ReaderOption[context.Context, User]
result := ro(ctx) // Returns option.Some(user) or option.None()
func FromPredicate ¶
func FromPredicate[E, A any](pred Predicate[A]) Kleisli[E, A, A]
FromPredicate creates a Kleisli arrow that filters a value based on a predicate. If the predicate returns true, the value is wrapped in Some; otherwise, None is returned.
Example:
isPositive := readeroption.FromPredicate[Config](func(x int) bool { return x > 0 })
result := F.Pipe1(
readeroption.Of[Config](42),
readeroption.Chain(isPositive),
)
func TailRec ¶
func TailRec[R, A, B any](f Kleisli[R, A, tailrec.Trampoline[A, B]]) Kleisli[R, A, B]
func TraverseArray ¶
func TraverseArray[E, A, B any](f Kleisli[E, A, B]) Kleisli[E, []A, []B]
TraverseArray transforms an array by applying a function that returns a ReaderOption to each element. If any element results in None, the entire result is None. Otherwise, returns Some containing an array of all the unwrapped values.
This is useful for performing a sequence of operations that may fail on each element of an array, where you want all operations to succeed or the entire computation to fail.
Example:
type DB struct { ... }
findUser := func(id int) readeroption.ReaderOption[DB, User] { ... }
userIDs := []int{1, 2, 3}
result := F.Pipe1(
readeroption.Of[DB](userIDs),
readeroption.Chain(readeroption.TraverseArray[DB](findUser)),
)
// result will be Some([]User) if all users are found, None otherwise
type Operator ¶
type Operator[R, A, B any] = Reader[ReaderOption[R, A], ReaderOption[R, B]]
Operator represents a function that transforms one ReaderOption into another. This is commonly used for lifting functions into the ReaderOption context.
func Alt ¶
func Alt[E, A any](that ReaderOption[E, A]) Operator[E, A, A]
Alt returns a function that provides an alternative ReaderOption if the first one returns None. This is the curried version of MonadAlt, useful for composition with F.Pipe.
Example:
result := F.Pipe1(
findUserInCache(123),
readeroption.Alt(findUserInDB(123)),
)
func Ap ¶
func Ap[B, E, A any](fa ReaderOption[E, A]) Operator[E, func(A) B, B]
Ap returns a function that applies a function wrapped in a ReaderOption to a value. This is the curried version of MonadAp.
func ApS ¶
func ApS[R, S1, S2, T any]( setter func(T) func(S1) S2, fa ReaderOption[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 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.ReaderOption[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.ReaderOption[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 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 ReaderOption 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.ReaderOption[Env, error, User] {
return readereither.Asks(func(env Env) either.Either[error, User] {
return env.UserService.GetUser()
})
}),
)
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[E, A, B any](f Kleisli[E, A, B]) Operator[E, A, B]
Chain returns a function that sequences ReaderOption computations. This is the curried version of MonadChain, useful for composition with F.Pipe.
Example:
result := F.Pipe1(
findUser(123),
readeroption.Chain(loadProfile),
)
func ChainOptionK ¶
ChainOptionK returns a function that chains a ReaderOption with a function returning an Option. This is the curried version of MonadChainOptionK.
Example:
parseAge := func(s string) option.Option[int] { ... }
result := F.Pipe1(
readeroption.Of[Config]("25"),
readeroption.ChainOptionK[Config](parseAge),
)
func Flap ¶
func Flap[E, B, A any](a A) Operator[E, func(A) B, B]
Flap returns a function that applies a value to a function wrapped in a ReaderOption. This 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 ¶
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 ReaderOption).
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[E, A, B any](f func(A) B) Operator[E, A, B]
Map returns a function that applies a transformation to the value inside a ReaderOption. This is the curried version of MonadMap, useful for composition with F.Pipe.
Example:
doubled := F.Pipe1(
readeroption.Of[Config](42),
readeroption.Map[Config](N.Mul(2)),
)
type Reader ¶
Reader represents a computation that depends on an environment R and produces a value A
func MonadFold ¶
func MonadFold[E, A, B any](fa ReaderOption[E, A], onNone Reader[E, B], onRight reader.Kleisli[E, A, B]) Reader[E, B]
MonadFold extracts the value from a ReaderOption by providing handlers for both cases. This is the non-curried version of Fold. The onNone handler is called if the computation returns None. The onRight handler is called if the computation returns Some(a).
Example:
result := readeroption.MonadFold(
findUser(123),
reader.Of[Config]("not found"),
func(user User) reader.Reader[Config, string] { return reader.Of[Config](user.Name) },
)
type ReaderOption ¶
ReaderOption represents a computation that depends on an environment R and may produce a value A. It combines the Reader monad (for dependency injection) with the Option monad (for optional values).
func Ask ¶
func Ask[E any]() ReaderOption[E, E]
Ask retrieves the current environment as a ReaderOption. This always succeeds and returns Some(environment).
Example:
getConfig := readeroption.Ask[Config]() result := getConfig(myConfig) // Returns option.Some(myConfig)
func Asks ¶
func Asks[E, A any](r Reader[E, A]) ReaderOption[E, A]
Asks creates a ReaderOption that applies a function to the environment. This always succeeds and returns Some(f(environment)).
Example:
getTimeout := readeroption.Asks(func(cfg Config) int { return cfg.Timeout })
result := getTimeout(myConfig) // Returns option.Some(myConfig.Timeout)
func Curry0 ¶
Curry0 converts a function that takes only a context and returns (A, bool) into a ReaderOption[R, A].
Example:
getConfig := func(ctx context.Context) (Config, bool) {
cfg, ok := ctx.Value("config").(Config)
return cfg, ok
}
ro := readeroption.Curry0(getConfig)
result := ro(ctx) // Returns option.Some(config) or option.None()
func Do ¶
func Do[R, S any]( empty S, ) ReaderOption[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[E, A any](mma ReaderOption[E, ReaderOption[E, A]]) ReaderOption[E, A]
Flatten removes one level of nesting from a ReaderOption. Converts ReaderOption[E, ReaderOption[E, A]] to ReaderOption[E, A].
Example:
nested := readeroption.Of[Config](readeroption.Of[Config](42)) flattened := readeroption.Flatten(nested)
func FromOption ¶
func FromOption[E, A any](e Option[A]) ReaderOption[E, A]
FromOption lifts an Option[A] into a ReaderOption[E, A]. The resulting computation ignores the environment and returns the given option.
func FromReader ¶
func FromReader[E, A any](r Reader[E, A]) ReaderOption[E, A]
FromReader lifts a Reader[E, A] into a ReaderOption[E, A]. The resulting computation always succeeds (returns Some).
func MonadAlt ¶
func MonadAlt[E, A any](fa, that ReaderOption[E, A]) ReaderOption[E, A]
MonadAlt provides an alternative ReaderOption if the first one returns None. If fa returns Some(a), that value is returned; otherwise, the alternative computation is executed. This is useful for providing fallback behavior.
Example:
primary := findUserInCache(123) fallback := findUserInDB(123) result := readeroption.MonadAlt(primary, fallback)
func MonadAp ¶
func MonadAp[E, A, B any](fab ReaderOption[E, func(A) B], fa ReaderOption[E, A]) ReaderOption[E, B]
MonadAp applies a function wrapped in a ReaderOption to a value wrapped in a ReaderOption. Both computations are executed with the same environment. If either computation returns None, the result is None.
func MonadChain ¶
func MonadChain[E, A, B any](ma ReaderOption[E, A], f Kleisli[E, A, B]) ReaderOption[E, B]
MonadChain sequences two ReaderOption computations, where the second depends on the result of the first. If the first computation returns None, the second is not executed.
Example:
findUser := func(id int) readeroption.ReaderOption[DB, User] { ... }
loadProfile := func(user User) readeroption.ReaderOption[DB, Profile] { ... }
result := readeroption.MonadChain(findUser(123), loadProfile)
func MonadChainOptionK ¶
MonadChainOptionK chains a ReaderOption with a function that returns an Option. This is useful for integrating functions that return Option directly.
Example:
parseAge := func(s string) option.Option[int] { ... }
result := readeroption.MonadChainOptionK(
readeroption.Of[Config]("25"),
parseAge,
)
func MonadFlap ¶
func MonadFlap[E, A, B any](fab ReaderOption[E, func(A) B], a A) ReaderOption[E, B]
MonadFlap applies a value to a function wrapped in a ReaderOption. This is the reverse of MonadAp.
func MonadMap ¶
func MonadMap[E, A, B any](fa ReaderOption[E, A], f func(A) B) ReaderOption[E, B]
MonadMap applies a function to the value inside a ReaderOption. If the ReaderOption contains None, the function is not applied.
Example:
ro := readeroption.Of[Config](42) doubled := readeroption.MonadMap(ro, N.Mul(2))
func None ¶
func None[E, A any]() ReaderOption[E, A]
None creates a ReaderOption representing a failed computation. The resulting computation ignores the environment and returns None.
Example:
ro := readeroption.None[Config, int]() result := ro(config) // Returns option.None[int]()
func Of ¶
func Of[E, A any](a A) ReaderOption[E, A]
Of wraps a value in a ReaderOption, representing a successful computation. The resulting computation ignores the environment and returns Some(a).
Example:
ro := readeroption.Of[Config](42) result := ro(config) // Returns option.Some(42)
func SequenceArray ¶
func SequenceArray[E, A any](ma []ReaderOption[E, A]) ReaderOption[E, []A]
SequenceArray converts an array of ReaderOption values into a ReaderOption of an array. If any element is None, the entire result is None. Otherwise, returns Some containing an array of all the unwrapped values.
This is useful when you have multiple independent ReaderOption computations and want to combine their results into a single array.
Example:
type Config struct { ... }
user1 := readeroption.Of[Config](User{ID: 1, Name: "Alice"})
user2 := readeroption.Of[Config](User{ID: 2, Name: "Bob"})
user3 := readeroption.None[Config, User]()
result := readeroption.SequenceArray([]readeroption.ReaderOption[Config, User]{
user1, user2, user3,
})
// result(config) will be option.None[[]User]() because user3 is None
result2 := readeroption.SequenceArray([]readeroption.ReaderOption[Config, User]{
user1, user2,
})
// result2(config) will be option.Some([]User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}})
func SequenceT1 ¶
SequenceT1 converts a single ReaderOption into a ReaderOption of a 1-tuple. This is mainly useful for consistency with the other SequenceT functions.
Example:
type Config struct { ... }
user := readeroption.Of[Config](User{Name: "Alice"})
result := readeroption.SequenceT1(user)
// result(config) returns option.Some(tuple.MakeTuple1(User{Name: "Alice"}))
func SequenceT2 ¶
func SequenceT2[E, A, B any]( a ReaderOption[E, A], b ReaderOption[E, B], ) ReaderOption[E, T.Tuple2[A, B]]
SequenceT2 combines two ReaderOption values into a ReaderOption of a 2-tuple. If either input is None, the result is None.
Example:
type Config struct { ... }
user := readeroption.Of[Config](User{Name: "Alice"})
count := readeroption.Of[Config](42)
result := readeroption.SequenceT2(user, count)
// result(config) returns option.Some(tuple.MakeTuple2(User{Name: "Alice"}, 42))
noneUser := readeroption.None[Config, User]()
result2 := readeroption.SequenceT2(noneUser, count)
// result2(config) returns option.None[tuple.Tuple2[User, int]]()
func SequenceT3 ¶
func SequenceT3[E, A, B, C any]( a ReaderOption[E, A], b ReaderOption[E, B], c ReaderOption[E, C], ) ReaderOption[E, T.Tuple3[A, B, C]]
SequenceT3 combines three ReaderOption values into a ReaderOption of a 3-tuple. If any input is None, the result is None.
Example:
type Config struct { ... }
user := readeroption.Of[Config](User{Name: "Alice"})
count := readeroption.Of[Config](42)
active := readeroption.Of[Config](true)
result := readeroption.SequenceT3(user, count, active)
// result(config) returns option.Some(tuple.MakeTuple3(User{Name: "Alice"}, 42, true))
func SequenceT4 ¶
func SequenceT4[E, A, B, C, D any]( a ReaderOption[E, A], b ReaderOption[E, B], c ReaderOption[E, C], d ReaderOption[E, D], ) ReaderOption[E, T.Tuple4[A, B, C, D]]
SequenceT4 combines four ReaderOption values into a ReaderOption of a 4-tuple. If any input is None, the result is None.
Example:
type Config struct { ... }
user := readeroption.Of[Config](User{Name: "Alice"})
count := readeroption.Of[Config](42)
active := readeroption.Of[Config](true)
score := readeroption.Of[Config](95.5)
result := readeroption.SequenceT4(user, count, active, score)
// result(config) returns option.Some(tuple.MakeTuple4(User{Name: "Alice"}, 42, true, 95.5))
func Some ¶
func Some[E, A any](r A) ReaderOption[E, A]
Some wraps a value in a ReaderOption, representing a successful computation. This is equivalent to Of but more explicit about the Option semantics.
func SomeReader ¶
func SomeReader[E, A any](r Reader[E, A]) ReaderOption[E, A]
SomeReader lifts a Reader[E, A] into a ReaderOption[E, A]. The resulting computation always succeeds (returns Some).