Documentation
¶
Overview ¶
Package readeriooption provides a monad transformer that combines the Reader, IO, and Option monads.
Overview ¶
ReaderIOOption[R, A] represents a computation that:
- Depends on a shared environment of type R (Reader monad)
- Performs side effects (IO monad)
- May fail to produce a value of type A (Option monad)
This is particularly useful for computations that need:
- Dependency injection or configuration access
- Side effects like I/O operations
- Optional results without using error types
The ReaderIOOption monad is defined as: Reader[R, IOOption[A]]
Fantasy Land Specification ¶
This package implements the following 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
- Profunctor: https://github.com/fantasyland/fantasy-land#profunctor
Core Operations ¶
Creating ReaderIOOption values:
- Of/Some: Wraps a value in a successful ReaderIOOption
- None: Creates a ReaderIOOption representing no value
- FromOption: Lifts an Option into ReaderIOOption
- FromReader: Lifts a Reader into ReaderIOOption
- Ask/Asks: Accesses the environment
Transforming values:
- Map: Transforms the value inside a ReaderIOOption
- Chain: Sequences ReaderIOOption computations
- Ap: Applies a function wrapped in ReaderIOOption
- Alt: Provides alternative computation on failure
Extracting values:
- Fold: Extracts value by providing handlers for both cases
- GetOrElse: Returns value or default
- Read: Executes the computation with an environment
Basic Example ¶
type Config struct {
DatabaseURL string
Timeout int
}
// A computation that may or may not find a user
func findUser(id int) readeriooption.ReaderIOOption[Config, User] {
return readeriooption.Asks(func(cfg Config) iooption.IOOption[User] {
return func() option.Option[User] {
// Use cfg.DatabaseURL to query database
// Return Some(user) if found, None() if not found
user, found := queryDB(cfg.DatabaseURL, id)
if found {
return option.Some(user)
}
return option.None[User]()
}
})
}
// Chain multiple operations
result := F.Pipe2(
findUser(123),
readeriooption.Chain(func(user User) readeriooption.ReaderIOOption[Config, Profile] {
return loadProfile(user.ProfileID)
}),
readeriooption.Map(func(profile Profile) string {
return profile.DisplayName
}),
)
// Execute with config
config := Config{DatabaseURL: "localhost:5432", Timeout: 30}
displayName := result(config)() // Returns Option[string]
Do-Notation Style ¶
The package supports do-notation style composition for building complex computations:
type State struct {
User User
Profile Profile
Posts []Post
}
result := F.Pipe3(
readeriooption.Do[Config](State{}),
readeriooption.Bind(
func(user User) func(State) State {
return func(s State) State { s.User = user; return s }
},
func(s State) readeriooption.ReaderIOOption[Config, User] {
return findUser(123)
},
),
readeriooption.Bind(
func(profile Profile) func(State) State {
return func(s State) State { s.Profile = profile; return s }
},
func(s State) readeriooption.ReaderIOOption[Config, Profile] {
return loadProfile(s.User.ProfileID)
},
),
readeriooption.Bind(
func(posts []Post) func(State) State {
return func(s State) State { s.Posts = posts; return s }
},
func(s State) readeriooption.ReaderIOOption[Config, []Post] {
return loadPosts(s.User.ID)
},
),
)
Alternative Computations ¶
Use Alt to provide fallback behavior when computations fail:
// Try cache first, fall back to database
result := F.Pipe1(
findUserInCache(123),
readeriooption.Alt(func() readeriooption.ReaderIOOption[Config, User] {
return findUserInDB(123)
}),
)
Array Operations ¶
Transform arrays where each element may fail:
userIDs := []int{1, 2, 3, 4, 5}
users := F.Pipe1(
readeriooption.Of[Config](userIDs),
readeriooption.Chain(readeriooption.TraverseArray[Config](findUser)),
)
// Returns Some([]User) if all users found, None otherwise
Monoid Operations ¶
Combine multiple ReaderIOOption computations:
import N "github.com/IBM/fp-go/v2/number"
// Applicative monoid - all must succeed
intAdd := N.MonoidSum[int]()
roMonoid := readeriooption.ApplicativeMonoid[Config](intAdd)
combined := roMonoid.Concat(
readeriooption.Of[Config](5),
readeriooption.Of[Config](3),
)
// Returns Some(8)
// Alternative monoid - provides fallback
altMonoid := readeriooption.AlternativeMonoid[Config](intAdd)
withFallback := altMonoid.Concat(
readeriooption.None[Config, int](),
readeriooption.Of[Config](10),
)
// Returns Some(10)
Profunctor Operations ¶
Transform both input and output:
type GlobalConfig struct {
DB DBConfig
}
type DBConfig struct {
Host string
}
// Adapt environment and transform result
adapted := F.Pipe1(
queryDB, // ReaderIOOption[DBConfig, User]
readeriooption.Promap(
func(g GlobalConfig) DBConfig { return g.DB },
func(u User) string { return u.Name },
),
)
// Now: ReaderIOOption[GlobalConfig, string]
Tail Recursion ¶
For recursive computations, use TailRec to avoid stack overflow:
func factorial(n int) readeriooption.ReaderIOOption[Config, int] {
return readeriooption.TailRec(func(acc int) readeriooption.ReaderIOOption[Config, tailrec.Trampoline[int, int]] {
if n <= 1 {
return readeriooption.Of[Config](tailrec.Done[int](acc))
}
return readeriooption.Of[Config](tailrec.Continue[int](acc * n))
})(1)
}
Relationship to Other Monads ¶
ReaderIOOption is related to other monads in the fp-go library:
- reader: ReaderIOOption adds IO and Option capabilities
- readerio: ReaderIOOption adds Option capability
- readeroption: ReaderIOOption adds IO capability
- iooption: ReaderIOOption adds Reader capability
- option: ReaderIOOption adds Reader and IO capabilities
Type Safety ¶
The type system ensures:
- Environment dependencies are explicit in the type signature
- Side effects are tracked through the IO layer
- Optional results are handled explicitly
- Composition maintains type safety
Performance Considerations ¶
ReaderIOOption computations are lazy and only execute when:
- An environment is provided (Reader layer)
- The IO action is invoked (IO layer)
This allows for efficient composition without premature execution.
package readeriooption 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
ReaderIOOption[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 ReaderIOOption monad is defined as: Reader[R, Option[A]]
Key operations:
- Of: Wraps a value in a ReaderIOOption
- None: Creates a ReaderIOOption representing no value
- Map: Transforms the value inside a ReaderIOOption
- Chain: Sequences ReaderIOOption 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) readeriooption.ReaderIOOption[Config, User] {
return readeriooption.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),
readeriooption.Chain(func(user User) readeriooption.ReaderIOOption[Config, Profile] {
return loadProfile(user.ProfileID)
}),
readeriooption.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 AlternativeMonoid[R, A any](m monoid.Monoid[A]) monoid.Monoid[ReaderIOOption[R, A]]
- func ApplicativeMonoid[R, A any](m monoid.Monoid[A]) monoid.Monoid[ReaderIOOption[R, A]]
- func Fold[R, A, B any](onNone Reader[R, B], onRight reader.Kleisli[R, A, B]) reader.Operator[R, Option[A], B]
- func GetOrElse[R, A any](onNone Reader[R, A]) reader.Operator[R, Option[A], A]
- func Local[A, R1, R2 any](f func(R2) R1) func(ReaderIOOption[R1, A]) ReaderIOOption[R2, A]
- func Read[A, R any](e R) func(ReaderIOOption[R, A]) IOOption[A]
- func TraverseArrayWithIndex[E, A, B any](f func(int, A) ReaderIOOption[E, B]) func([]A) ReaderIOOption[E, []B]
- type Either
- type IOOption
- type Kleisli
- func Contramap[A, R1, R2 any](f func(R2) R1) Kleisli[R2, ReaderIOOption[R1, A], A]
- func FromPredicate[R, A any](pred Predicate[A]) Kleisli[R, A, A]
- func Promap[R, A, D, B any](f func(D) R, g func(A) B) Kleisli[D, ReaderIOOption[R, A], B]
- 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[R, A any](second Lazy[ReaderIOOption[R, A]]) Operator[R, A, A]
- func Ap[B, R, A any](fa ReaderIOOption[R, A]) Operator[R, func(A) B, B]
- func ApS[R, S1, S2, T any](setter func(T) func(S1) S2, fa ReaderIOOption[R, T]) Operator[R, S1, S2]
- func ApSL[R, S, T any](lens L.Lens[S, T], fa ReaderIOOption[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[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, B]
- func ChainOptionK[R, A, B any](f O.Kleisli[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 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[R, A, B any](f func(A) B) Operator[R, A, B]
- type Option
- type Predicate
- type Reader
- type ReaderIO
- type ReaderIOOption
- func Ask[R any]() ReaderIOOption[R, R]
- func Asks[R, A any](r Reader[R, A]) ReaderIOOption[R, A]
- func Do[R, S any](empty S) ReaderIOOption[R, S]
- func Flatten[R, A any](mma ReaderIOOption[R, ReaderIOOption[R, A]]) ReaderIOOption[R, A]
- func FromOption[R, A any](t Option[A]) ReaderIOOption[R, A]
- func FromReader[R, A any](r Reader[R, A]) ReaderIOOption[R, A]
- func MonadAlt[R, A any](first ReaderIOOption[R, A], second Lazy[ReaderIOOption[R, A]]) ReaderIOOption[R, A]
- func MonadAp[R, A, B any](fab ReaderIOOption[R, func(A) B], fa ReaderIOOption[R, A]) ReaderIOOption[R, B]
- func MonadChain[R, A, B any](ma ReaderIOOption[R, A], f Kleisli[R, A, B]) ReaderIOOption[R, B]
- func MonadChainOptionK[R, A, B any](ma ReaderIOOption[R, A], f O.Kleisli[A, B]) ReaderIOOption[R, B]
- func MonadFlap[R, A, B any](fab ReaderIOOption[R, func(A) B], a A) ReaderIOOption[R, B]
- func MonadMap[R, A, B any](fa ReaderIOOption[R, A], f func(A) B) ReaderIOOption[R, B]
- func None[R, A any]() ReaderIOOption[R, A]
- func Of[R, A any](a A) ReaderIOOption[R, A]
- func SequenceT1[R, A any](a ReaderIOOption[R, A]) ReaderIOOption[R, T.Tuple1[A]]
- func SequenceT2[R, A, B any](a ReaderIOOption[R, A], b ReaderIOOption[R, B]) ReaderIOOption[R, T.Tuple2[A, B]]
- func SequenceT3[R, A, B, C any](a ReaderIOOption[R, A], b ReaderIOOption[R, B], c ReaderIOOption[R, C]) ReaderIOOption[R, T.Tuple3[A, B, C]]
- func SequenceT4[R, A, B, C, D any](a ReaderIOOption[R, A], b ReaderIOOption[R, B], c ReaderIOOption[R, C], ...) ReaderIOOption[R, T.Tuple4[A, B, C, D]]
- func Some[R, A any](r A) ReaderIOOption[R, A]
- func SomeReader[R, A any](r Reader[R, A]) ReaderIOOption[R, A]
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AlternativeMonoid ¶
AlternativeMonoid creates a Monoid for ReaderIOOption 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 (returns None), then combines successful values using the underlying monoid.
This is particularly useful when you want to:
- Try multiple computations and accumulate their results
- Provide fallback behavior when computations fail
- Combine results from computations that may or may not succeed
The behavior differs from ApplicativeMonoid in that it provides fallback semantics:
- If the first computation succeeds, use its value
- If the first fails but the second succeeds, use the second's value
- If both succeed, combine their values using the underlying monoid
- If both fail, the result is None
The resulting monoid satisfies the monoid laws:
- Left identity: Concat(Empty(), x) = x
- Right identity: Concat(x, Empty()) = x
- Associativity: Concat(Concat(x, y), z) = Concat(x, Concat(y, z))
Parameters:
- m: The underlying monoid for combining success values of type A
Returns:
- A Monoid[ReaderIOOption[R, A]] that combines ReaderIOOption computations with fallback
Example:
import (
N "github.com/IBM/fp-go/v2/number"
RO "github.com/IBM/fp-go/v2/readeroption"
)
// Create a monoid for integer addition with alternative behavior
intAdd := N.MonoidSum[int]()
roMonoid := RO.AlternativeMonoid[Config](intAdd)
// Combine successful computations
ro1 := RO.Of[Config](5)
ro2 := RO.Of[Config](3)
combined := roMonoid.Concat(ro1, ro2)
// combined(cfg) returns option.Some(8)
// Fallback when first fails
ro3 := RO.None[Config, int]()
ro4 := RO.Of[Config](10)
withFallback := roMonoid.Concat(ro3, ro4)
// withFallback(cfg) returns option.Some(10)
// Use first success when available
withFirst := roMonoid.Concat(ro1, ro3)
// withFirst(cfg) returns option.Some(5)
// Accumulate multiple values with some failures
result := roMonoid.Concat(
roMonoid.Concat(ro3, ro1), // None + 5 = 5
ro2, // 5 + 3 = 8
)
// result(cfg) returns option.Some(8)
func ApplicativeMonoid ¶
ApplicativeMonoid creates a Monoid for ReaderIOOption 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 (return Some) for the result to succeed.
This is useful for accumulating results from multiple independent computations that all need to succeed. If any computation returns None, the entire result is None.
The resulting monoid satisfies the monoid laws:
- Left identity: Concat(Empty(), x) = x
- Right identity: Concat(x, Empty()) = x
- Associativity: Concat(Concat(x, y), z) = Concat(x, Concat(y, z))
Parameters:
- m: The underlying monoid for combining success values of type A
Returns:
- A Monoid[ReaderIOOption[R, A]] that combines ReaderIOOption computations
Example:
import (
N "github.com/IBM/fp-go/v2/number"
RO "github.com/IBM/fp-go/v2/readeroption"
)
// Create a monoid for integer addition
intAdd := N.MonoidSum[int]()
roMonoid := RO.ApplicativeMonoid[Config](intAdd)
// Combine successful computations
ro1 := RO.Of[Config](5)
ro2 := RO.Of[Config](3)
combined := roMonoid.Concat(ro1, ro2)
// combined(cfg) returns option.Some(8)
// If either fails, the whole computation fails
ro3 := RO.None[Config, int]()
failed := roMonoid.Concat(ro1, ro3)
// failed(cfg) returns option.None[int]()
// Empty element is the identity
withEmpty := roMonoid.Concat(ro1, roMonoid.Empty())
// withEmpty(cfg) returns option.Some(5)
func Fold ¶
func Fold[R, A, B any](onNone Reader[R, B], onRight reader.Kleisli[R, A, B]) reader.Operator[R, Option[A], B]
Fold extracts the value from a ReaderIOOption 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 GetOrElse ¶
GetOrElse returns the value from a ReaderIOOption, 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, R1, R2 any](f func(R2) R1) func(ReaderIOOption[R1, A]) ReaderIOOption[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, R any](e R) func(ReaderIOOption[R, A]) IOOption[A]
Read applies a context to a reader to obtain its value. This executes the ReaderIOOption computation with the given environment.
Example:
ro := readeroption.Of[Config](42) result := readeroption.Read[int](myConfig)(ro) // Returns option.Some(42)
func TraverseArrayWithIndex ¶
func TraverseArrayWithIndex[E, A, B any](f func(int, A) ReaderIOOption[E, B]) func([]A) ReaderIOOption[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.ReaderIOOption[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)
Types ¶
type Either ¶
Either represents a value of one of two possible types (a disjoint union). An instance of Either is either Left (representing an error) or Right (representing a success).
type IOOption ¶
IOOption represents an IO computation that may produce a value of type A. It combines IO effects with the Option monad for optional values.
type Kleisli ¶
type Kleisli[R, A, B any] = Reader[A, ReaderIOOption[R, B]]
Kleisli represents a function that takes a value A and returns a ReaderIOOption[R, B]. This is the type of functions used with Chain/Bind operations, enabling monadic composition.
func Contramap ¶
func Contramap[A, R1, R2 any](f func(R2) R1) Kleisli[R2, ReaderIOOption[R1, A], A]
Contramap changes the value of the local environment during the execution of a ReaderIOOption. 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 ReaderIOOption to work with a different environment type by providing a function that converts the new environment to the expected one.
Type Parameters:
- A: The value type (unchanged)
- R2: The new input environment type
- R1: The original environment type expected by the ReaderIOOption
Parameters:
- f: Function to transform the environment from R2 to R1
Returns:
- A Kleisli arrow that takes a ReaderIOOption[R1, A] and returns a ReaderIOOption[R2, A]
func FromPredicate ¶
func FromPredicate[R, A any](pred Predicate[A]) Kleisli[R, 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](N.MoreThan(0))
result := F.Pipe1(
readeroption.Of[Config](42),
readeroption.Chain(isPositive),
)
func Promap ¶
func Promap[R, A, D, B any](f func(D) R, g func(A) B) Kleisli[D, ReaderIOOption[R, A], B]
Promap is the profunctor map operation that transforms both the input and output of a ReaderIOOption. 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 ReaderIOOption (via f)
- Transform the Some value after the computation completes (via g)
The None case remains unchanged through the transformation.
Type Parameters:
- R: The original environment type expected by the ReaderIOOption
- A: The original value type produced by the ReaderIOOption
- D: The new input environment type
- B: The new output value type
Parameters:
- f: Function to transform the input environment from D to R (contravariant)
- g: Function to transform the output Some value from A to B (covariant)
Returns:
- A Kleisli arrow that takes a ReaderIOOption[R, A] and returns a ReaderIOOption[D, B]
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 ReaderIOOption 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.ReaderIOOption[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[ReaderIOOption[R, A], ReaderIOOption[R, B]]
Operator represents a function that transforms one ReaderIOOption into another. It takes a ReaderIOOption[R, A] and produces a ReaderIOOption[R, B]. This is commonly used for lifting functions into the ReaderIOOption context.
func Alt ¶
Alt returns a function that provides an alternative ReaderIOOption 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, R, A any](fa ReaderIOOption[R, A]) Operator[R, func(A) B, B]
Ap returns a function that applies a function wrapped in a ReaderIOOption 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 ReaderIOOption[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.ReaderIOOption[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.ReaderIOOption[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 ReaderIOOption 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.ReaderIOOption[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[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, B]
Chain returns a function that sequences ReaderIOOption 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 ReaderIOOption 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[R, B, A any](a A) Operator[R, func(A) B, B]
Flap returns a function that applies a value to a function wrapped in a ReaderIOOption. 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 ReaderIOOption).
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 returns a function that applies a transformation to the value inside a ReaderIOOption. 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 Predicate ¶
Predicate represents a function that tests a value of type A and returns a boolean. It's commonly used for filtering and conditional operations.
type Reader ¶
Reader represents a computation that depends on an environment R and produces a value A.
type ReaderIO ¶
ReaderIO represents a computation that depends on an environment R and performs IO to produce a value A. It combines the Reader monad (for dependency injection) with IO effects.
func MonadFold ¶
func MonadFold[R, A, B any](fa ReaderIOOption[R, A], onNone ReaderIO[R, B], onRight readerio.Kleisli[R, A, B]) ReaderIO[R, B]
MonadFold extracts the value from a ReaderIOOption 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 ReaderIOOption ¶
ReaderIOOption represents a computation that depends on an environment R and may produce a value A. It combines the Reader monad (for dependency injection) with IO effects and the Option monad (for optional values). This is the main type of this package, defined as Reader[R, IOOption[A]].
func Ask ¶
func Ask[R any]() ReaderIOOption[R, R]
Ask retrieves the current environment as a ReaderIOOption. This always succeeds and returns Some(environment).
Example:
getConfig := readeroption.Ask[Config]() result := getConfig(myConfig) // Returns option.Some(myConfig)
func Asks ¶
func Asks[R, A any](r Reader[R, A]) ReaderIOOption[R, A]
Asks creates a ReaderIOOption 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 Do ¶
func Do[R, S any]( empty S, ) ReaderIOOption[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 ReaderIOOption[R, ReaderIOOption[R, A]]) ReaderIOOption[R, A]
Flatten removes one level of nesting from a ReaderIOOption. Converts ReaderIOOption[R, ReaderIOOption[R, A]] to ReaderIOOption[R, A].
Example:
nested := readeroption.Of[Config](readeroption.Of[Config](42)) flattened := readeroption.Flatten(nested)
func FromOption ¶
func FromOption[R, A any](t Option[A]) ReaderIOOption[R, A]
FromOption lifts an Option[A] into a ReaderIOOption[R, A]. The resulting computation ignores the environment and returns the given option.
func FromReader ¶
func FromReader[R, A any](r Reader[R, A]) ReaderIOOption[R, A]
FromReader lifts a Reader[R, A] into a ReaderIOOption[R, A]. The resulting computation always succeeds (returns Some).
func MonadAlt ¶
func MonadAlt[R, A any](first ReaderIOOption[R, A], second Lazy[ReaderIOOption[R, A]]) ReaderIOOption[R, A]
MonadAlt provides an alternative ReaderIOOption 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[R, A, B any](fab ReaderIOOption[R, func(A) B], fa ReaderIOOption[R, A]) ReaderIOOption[R, B]
MonadAp applies a function wrapped in a ReaderIOOption to a value wrapped in a ReaderIOOption. Both computations are executed with the same environment. If either computation returns None, the result is None.
func MonadChain ¶
func MonadChain[R, A, B any](ma ReaderIOOption[R, A], f Kleisli[R, A, B]) ReaderIOOption[R, B]
MonadChain sequences two ReaderIOOption 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.ReaderIOOption[DB, User] { ... }
loadProfile := func(user User) readeroption.ReaderIOOption[DB, Profile] { ... }
result := readeroption.MonadChain(findUser(123), loadProfile)
func MonadChainOptionK ¶
func MonadChainOptionK[R, A, B any](ma ReaderIOOption[R, A], f O.Kleisli[A, B]) ReaderIOOption[R, B]
MonadChainOptionK chains a ReaderIOOption 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[R, A, B any](fab ReaderIOOption[R, func(A) B], a A) ReaderIOOption[R, B]
MonadFlap applies a value to a function wrapped in a ReaderIOOption. This is the reverse of MonadAp.
func MonadMap ¶
func MonadMap[R, A, B any](fa ReaderIOOption[R, A], f func(A) B) ReaderIOOption[R, B]
MonadMap applies a function to the value inside a ReaderIOOption. If the ReaderIOOption contains None, the function is not applied.
Example:
ro := readeroption.Of[Config](42) doubled := readeroption.MonadMap(ro, N.Mul(2))
func None ¶
func None[R, A any]() ReaderIOOption[R, A]
None creates a ReaderIOOption 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[R, A any](a A) ReaderIOOption[R, A]
Of wraps a value in a ReaderIOOption, 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 SequenceT1 ¶
SequenceT1 converts a single ReaderIOOption into a ReaderIOOption 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[R, A, B any]( a ReaderIOOption[R, A], b ReaderIOOption[R, B], ) ReaderIOOption[R, T.Tuple2[A, B]]
SequenceT2 combines two ReaderIOOption values into a ReaderIOOption 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[R, A, B, C any]( a ReaderIOOption[R, A], b ReaderIOOption[R, B], c ReaderIOOption[R, C], ) ReaderIOOption[R, T.Tuple3[A, B, C]]
SequenceT3 combines three ReaderIOOption values into a ReaderIOOption 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[R, A, B, C, D any]( a ReaderIOOption[R, A], b ReaderIOOption[R, B], c ReaderIOOption[R, C], d ReaderIOOption[R, D], ) ReaderIOOption[R, T.Tuple4[A, B, C, D]]
SequenceT4 combines four ReaderIOOption values into a ReaderIOOption 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[R, A any](r A) ReaderIOOption[R, A]
Some wraps a value in a ReaderIOOption, representing a successful computation. This is equivalent to Of but more explicit about the Option semantics.
func SomeReader ¶
func SomeReader[R, A any](r Reader[R, A]) ReaderIOOption[R, A]
SomeReader lifts a Reader[R, A] into a ReaderIOOption[R, A]. The resulting computation always succeeds (returns Some).