Documentation
¶
Overview ¶
Package readerio provides the ReaderIO monad, which combines the Reader and IO monads.
ReaderIO[R, A] represents a computation that:
- Requires an environment of type R (Reader aspect)
- Performs side effects (IO aspect)
- Produces a value of type A
This monad is particularly useful for dependency injection patterns and logging scenarios, where you need to:
- Access configuration or context throughout your application
- Perform side effects like logging, file I/O, or network calls
- Maintain functional composition and testability
# Logging Use Case ReaderIO is especially well-suited for logging because it allows you to:
- Pass a logger through your computation chain without explicit parameter threading
- Compose logging operations with other side effects
- Test logging behavior by providing mock loggers in the environment
Key functions for logging scenarios:
- Ask: Retrieve the entire environment (e.g., a logger instance)
- Asks: Extract a specific value from the environment (e.g., logger.Info method)
- ChainIOK: Chain logging operations that return IO effects
- MonadChain: Sequence multiple logging and computation steps
Example logging usage:
type Env struct {
Logger *log.Logger
}
// Log a message using the environment's logger
logInfo := func(msg string) readerio.ReaderIO[Env, func()] {
return readerio.Asks(func(env Env) io.IO[func()] {
return io.Of(func() { env.Logger.Println(msg) })
})
}
// Compose logging with computation
computation := F.Pipe3(
readerio.Of[Env](42),
readerio.Chain(func(n int) readerio.ReaderIO[Env, int] {
return F.Pipe1(
logInfo(fmt.Sprintf("Processing: %d", n)),
readerio.Map[Env](func(func()) int { return n * 2 }),
)
}),
readerio.ChainIOK(func(result int) io.IO[int] {
return io.Of(result)
}),
)
// Execute with environment
env := Env{Logger: log.New(os.Stdout, "APP: ", log.LstdFlags)}
result := computation(env)() // Logs "Processing: 42" and returns 84
Core Operations ¶
The package provides standard monadic operations:
- Of: Lift a pure value into ReaderIO
- Map: Transform the result value
- Chain: Sequence dependent computations
- Ap: Apply a function in ReaderIO context
Integration ¶
Convert between different contexts:
- FromIO: Lift an IO action into ReaderIO
- FromReader: Lift a Reader into ReaderIO
- ChainIOK: Chain with IO-returning functions
Performance ¶
- Memoize: Cache computation results (use with caution for context-dependent values)
- Defer: Ensure fresh computation on each execution
Copyright (c) 2025 IBM Corp. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Index ¶
- func ApFirst[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, A]
- func ApS[R, S1, S2, T any](setter func(T) func(S1) S2, fa ReaderIO[R, T]) func(ReaderIO[R, S1]) ReaderIO[R, S2]
- func ApSL[R, S, T any](lens L.Lens[S, T], fa ReaderIO[R, T]) func(ReaderIO[R, S]) ReaderIO[R, S]
- func ApSecond[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, B]
- func Bind[R, S1, S2, T any](setter func(T) func(S1) S2, f func(S1) ReaderIO[R, T]) func(ReaderIO[R, S1]) ReaderIO[R, S2]
- func BindL[R, S, T any](lens L.Lens[S, T], f func(T) ReaderIO[R, T]) func(ReaderIO[R, S]) ReaderIO[R, S]
- func BindTo[R, S1, T any](setter func(T) S1) func(ReaderIO[R, T]) ReaderIO[R, S1]
- func Eq[R, A any](e EQ.Eq[A]) func(r R) EQ.Eq[ReaderIO[R, A]]
- func From0[F ~func(R) IO[A], R, A any](f func(R) IO[A]) func() ReaderIO[R, A]
- func From1[F ~func(R, T1) IO[A], R, T1, A any](f func(R, T1) IO[A]) func(T1) ReaderIO[R, A]
- func From2[F ~func(R, T1, T2) IO[A], R, T1, T2, A any](f func(R, T1, T2) IO[A]) func(T1, T2) ReaderIO[R, A]
- func From3[F ~func(R, T1, T2, T3) IO[A], R, T1, T2, T3, A any](f func(R, T1, T2, T3) IO[A]) func(T1, T2, T3) ReaderIO[R, A]
- func Let[R, S1, S2, T any](setter func(T) func(S1) S2, f func(S1) T) func(ReaderIO[R, S1]) ReaderIO[R, S2]
- func LetL[R, S, T any](lens L.Lens[S, T], f func(T) T) func(ReaderIO[R, S]) ReaderIO[R, S]
- func LetTo[R, S1, S2, T any](setter func(T) func(S1) S2, b T) func(ReaderIO[R, S1]) ReaderIO[R, S2]
- func LetToL[R, S, T any](lens L.Lens[S, T], b T) func(ReaderIO[R, S]) ReaderIO[R, S]
- func Read[A, R any](r R) func(ReaderIO[R, A]) IO[A]
- func Traverse[R2, R1, A, B any](f Kleisli[R1, A, B]) func(ReaderIO[R2, A]) Kleisli[R2, R1, B]
- func TraverseArray[R, A, B any](f func(A) ReaderIO[R, B]) func([]A) ReaderIO[R, []B]
- func TraverseArrayWithIndex[R, A, B any](f func(int, A) ReaderIO[R, B]) func([]A) ReaderIO[R, []B]
- func TraverseReader[R2, R1, A, B any](f reader.Kleisli[R1, A, B]) func(ReaderIO[R2, A]) Kleisli[R2, R1, B]
- type Consumer
- type Either
- type IO
- type Kleisli
- func LogGo[R, A any](prefix string) Kleisli[R, A, A]
- func Logf[R, A any](prefix string) Kleisli[R, A, A]
- func PrintGo[R, A any](prefix string) Kleisli[R, A, A]
- func Printf[R, A any](prefix string) Kleisli[R, A, A]
- func Sequence[R1, R2, A any](ma ReaderIO[R2, ReaderIO[R1, A]]) Kleisli[R2, R1, A]
- func SequenceReader[R1, R2, A any](ma ReaderIO[R2, Reader[R1, A]]) Kleisli[R2, R1, A]
- func TailRec[R, A, B any](f Kleisli[R, A, Trampoline[A, B]]) Kleisli[R, A, B]
- type Operator
- func After[R, A any](timestamp time.Time) Operator[R, A, A]
- func Ap[B, R, A any](fa ReaderIO[R, A]) Operator[R, func(A) B, B]
- func Chain[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, B]
- func ChainConsumer[R, A any](c Consumer[A]) Operator[R, A, struct{}]
- func ChainFirst[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, A]
- func ChainFirstConsumer[R, A any](c Consumer[A]) Operator[R, A, A]
- func ChainFirstIOK[R, A, B any](f io.Kleisli[A, B]) Operator[R, A, A]
- func ChainFirstReaderK[R, A, B any](f reader.Kleisli[R, A, B]) Operator[R, A, A]
- func ChainIOK[R, A, B any](f io.Kleisli[A, B]) Operator[R, A, B]
- func ChainReaderK[R, A, B any](f reader.Kleisli[R, A, B]) Operator[R, A, B]
- func Delay[R, A any](delay time.Duration) Operator[R, A, A]
- func Flap[R, B, A any](a A) Operator[R, func(A) B, B]
- func Map[R, A, B any](f func(A) B) Operator[R, A, B]
- func MapTo[R, A, B any](b B) Operator[R, A, B]
- func Tap[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, A]
- func TapIOK[R, A, B any](f io.Kleisli[A, B]) Operator[R, A, A]
- func TapReaderK[R, A, B any](f reader.Kleisli[R, A, B]) Operator[R, A, A]
- func WithLock[R, A any](lock func() context.CancelFunc) Operator[R, A, A]
- type Reader
- type ReaderIO
- func Ask[R any]() ReaderIO[R, R]
- func Asks[R, A any](r Reader[R, A]) ReaderIO[R, A]
- func Bracket[R, A, B, ANY any](acquire ReaderIO[R, A], use Kleisli[R, A, B], ...) ReaderIO[R, B]
- func Defer[R, A any](gen func() ReaderIO[R, A]) ReaderIO[R, A]
- func Do[R, S any](empty S) ReaderIO[R, S]
- func Flatten[R, A any](mma ReaderIO[R, ReaderIO[R, A]]) ReaderIO[R, A]
- func FromIO[R, A any](t IO[A]) ReaderIO[R, A]
- func FromReader[R, A any](r Reader[R, A]) ReaderIO[R, A]
- func Memoize[R, A any](rdr ReaderIO[R, A]) ReaderIO[R, A]
- func MonadAp[B, R, A any](fab ReaderIO[R, func(A) B], fa ReaderIO[R, A]) ReaderIO[R, B]
- func MonadApFirst[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, A]
- func MonadApPar[B, R, A any](fab ReaderIO[R, func(A) B], fa ReaderIO[R, A]) ReaderIO[R, B]
- func MonadApSecond[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, B]
- func MonadApSeq[B, R, A any](fab ReaderIO[R, func(A) B], fa ReaderIO[R, A]) ReaderIO[R, B]
- func MonadChain[R, A, B any](ma ReaderIO[R, A], f Kleisli[R, A, B]) ReaderIO[R, B]
- func MonadChainFirst[R, A, B any](ma ReaderIO[R, A], f Kleisli[R, A, B]) ReaderIO[R, A]
- func MonadChainFirstIOK[R, A, B any](ma ReaderIO[R, A], f io.Kleisli[A, B]) ReaderIO[R, A]
- func MonadChainFirstReaderK[R, A, B any](ma ReaderIO[R, A], f reader.Kleisli[R, A, B]) ReaderIO[R, A]
- func MonadChainIOK[R, A, B any](ma ReaderIO[R, A], f io.Kleisli[A, B]) ReaderIO[R, B]
- func MonadChainReaderK[R, A, B any](ma ReaderIO[R, A], f reader.Kleisli[R, A, B]) ReaderIO[R, B]
- func MonadFlap[R, B, A any](fab ReaderIO[R, func(A) B], a A) ReaderIO[R, B]
- func MonadMap[R, A, B any](fa ReaderIO[R, A], f func(A) B) ReaderIO[R, B]
- func MonadMapTo[R, A, B any](fa ReaderIO[R, A], b B) ReaderIO[R, B]
- func MonadTap[R, A, B any](ma ReaderIO[R, A], f Kleisli[R, A, B]) ReaderIO[R, A]
- func MonadTapIOK[R, A, B any](ma ReaderIO[R, A], f io.Kleisli[A, B]) ReaderIO[R, A]
- func MonadTapReaderK[R, A, B any](ma ReaderIO[R, A], f reader.Kleisli[R, A, B]) ReaderIO[R, A]
- func Of[R, A any](a A) ReaderIO[R, A]
- func Retrying[R, A any](policy retry.RetryPolicy, action Kleisli[R, retry.RetryStatus, A], ...) ReaderIO[R, A]
- func SequenceArray[R, A any](ma []ReaderIO[R, A]) ReaderIO[R, []A]
- func SequenceT1[R, A any](a ReaderIO[R, A]) ReaderIO[R, T.Tuple1[A]]
- func SequenceT2[R, A, B any](a ReaderIO[R, A], b ReaderIO[R, B]) ReaderIO[R, T.Tuple2[A, B]]
- func SequenceT3[R, A, B, C any](a ReaderIO[R, A], b ReaderIO[R, B], c ReaderIO[R, C]) ReaderIO[R, T.Tuple3[A, B, C]]
- func SequenceT4[R, A, B, C, D any](a ReaderIO[R, A], b ReaderIO[R, B], c ReaderIO[R, C], d ReaderIO[R, D]) ReaderIO[R, T.Tuple4[A, B, C, D]]
- type Trampoline
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ApFirst ¶
func ApFirst[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, A]
ApFirst combines two effectful actions, keeping only the result of the first.
func ApS ¶
func ApS[R, S1, S2, T any]( setter func(T) func(S1) S2, fa ReaderIO[R, T], ) func(ReaderIO[R, S1]) ReaderIO[R, 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 {
Host string
Port int
}
type Config struct {
DefaultHost string
DefaultPort int
}
// These operations are independent and can be combined with ApS
getHost := readerio.Asks(func(c Config) io.IO[string] {
return io.Of(c.DefaultHost)
})
getPort := readerio.Asks(func(c Config) io.IO[int] {
return io.Of(c.DefaultPort)
})
result := F.Pipe2(
readerio.Do[Config](State{}),
readerio.ApS(
func(host string) func(State) State {
return func(s State) State { s.Host = host; return s }
},
getHost,
),
readerio.ApS(
func(port int) func(State) State {
return func(s State) State { s.Port = port; return s }
},
getPort,
),
)
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 {
Host string
Port int
}
type Config struct {
DefaultHost string
DefaultPort int
}
portLens := lens.MakeLens(
func(s State) int { return s.Port },
func(s State, p int) State { s.Port = p; return s },
)
getPort := readerio.Asks(func(c Config) io.IO[int] {
return io.Of(c.DefaultPort)
})
result := F.Pipe2(
readerio.Of[Config](State{Host: "localhost"}),
readerio.ApSL(portLens, getPort),
)
func ApSecond ¶
func ApSecond[A, R, B any](second ReaderIO[R, B]) func(ReaderIO[R, A]) ReaderIO[R, B]
ApSecond combines two effectful actions, keeping only the result of the second.
func Bind ¶
func Bind[R, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) ReaderIO[R, T], ) func(ReaderIO[R, S1]) ReaderIO[R, 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 {
Host string
Port int
}
type Config struct {
DefaultHost string
DefaultPort int
}
result := F.Pipe2(
readerio.Do[Config](State{}),
readerio.Bind(
func(host string) func(State) State {
return func(s State) State { s.Host = host; return s }
},
func(s State) readerio.ReaderIO[Config, string] {
return readerio.Asks(func(c Config) io.IO[string] {
return io.Of(c.DefaultHost)
})
},
),
readerio.Bind(
func(port int) func(State) State {
return func(s State) State { s.Port = port; return s }
},
func(s State) readerio.ReaderIO[Config, int] {
// This can access s.Host from the previous step
return readerio.Asks(func(c Config) io.IO[int] {
return io.Of(c.DefaultPort)
})
},
),
)
func BindL ¶
func BindL[R, S, T any]( lens L.Lens[S, T], f func(T) ReaderIO[R, T], ) func(ReaderIO[R, S]) ReaderIO[R, 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 ReaderIO computation that produces an updated value.
Example:
type State struct {
Host string
Port int
}
type Config struct {
DefaultHost string
DefaultPort int
}
portLens := lens.MakeLens(
func(s State) int { return s.Port },
func(s State, p int) State { s.Port = p; return s },
)
result := F.Pipe2(
readerio.Do[Config](State{Host: "localhost"}),
readerio.BindL(portLens, func(port int) readerio.ReaderIO[Config, int] {
return readerio.Asks(func(c Config) io.IO[int] {
return io.Of(c.DefaultPort)
})
}),
)
func BindTo ¶
func BindTo[R, S1, T any]( setter func(T) S1, ) func(ReaderIO[R, T]) ReaderIO[R, S1]
BindTo initializes a new state [S1] from a value [T]
func Let ¶
func Let[R, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) T, ) func(ReaderIO[R, S1]) ReaderIO[R, 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 ReaderIO).
Example:
type State struct {
Host string
Port int
}
portLens := lens.MakeLens(
func(s State) int { return s.Port },
func(s State, p int) State { s.Port = p; return s },
)
result := F.Pipe2(
readerio.Do[any](State{Host: "localhost", Port: 8080}),
readerio.LetL(portLens, func(port int) int {
return port + 1
}),
)
func LetTo ¶
func LetTo[R, S1, S2, T any]( setter func(T) func(S1) S2, b T, ) func(ReaderIO[R, S1]) ReaderIO[R, 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 {
Host string
Port int
}
portLens := lens.MakeLens(
func(s State) int { return s.Port },
func(s State, p int) State { s.Port = p; return s },
)
result := F.Pipe2(
readerio.Do[any](State{Host: "localhost"}),
readerio.LetToL(portLens, 8080),
)
func Read ¶
Read executes a ReaderIO with a given environment, returning the resulting IO. This is useful for providing the environment dependency and obtaining an IO action that can be executed later.
Type Parameters:
- A: Result type
- R: Reader environment type
Parameters:
- r: The environment to provide to the ReaderIO
Returns:
- A function that converts a ReaderIO into an IO by applying the environment
Example:
rio := readerio.Of[Config](42)
config := Config{Value: 10, Name: "test"}
ioAction := readerio.Read[int](config)(rio)
result := ioAction() // Returns 42
func Traverse ¶
func Traverse[R2, R1, A, B any]( f Kleisli[R1, A, B], ) func(ReaderIO[R2, A]) Kleisli[R2, R1, B]
func TraverseArray ¶
func TraverseArray[R, A, B any](f func(A) ReaderIO[R, B]) func([]A) ReaderIO[R, []B]
TraverseArray transforms each element of an array using a function that returns a ReaderIO, then collects the results into a single ReaderIO containing an array.
All transformations are executed sequentially.
Type parameters:
- R: The context type
- A: The input element type
- B: The output element type
Parameters:
- f: A function that transforms each element into a ReaderIO
Returns:
A function that takes an array and returns a ReaderIO of an array
Example:
fetchUsers := TraverseArray(func(id int) ReaderIO[Config, User] {
return fetchUser(id)
})
result := fetchUsers([]int{1, 2, 3})
// result(cfg)() returns [user1, user2, user3]
func TraverseArrayWithIndex ¶
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:
- R: The context type
- A: The input element type
- B: The output element type
Parameters:
- f: A function that transforms each element and its index into a ReaderIO
Returns:
A function that takes an array and returns a ReaderIO of an array
Example:
processWithIndex := TraverseArrayWithIndex(func(i int, val string) ReaderIO[Config, string] {
return Of[Config](fmt.Sprintf("%d: %s", i, val))
})
func TraverseReader ¶
Types ¶
type IO ¶
IO represents a lazy computation that performs side effects and produces a value of type A. It's an alias for io.IO[A] and encapsulates effectful operations.
type Kleisli ¶
Kleisli represents a Kleisli arrow for the ReaderIO monad. It's a function from A to ReaderIO[R, B], which allows composition of monadic functions. This is the fundamental building block for chaining operations in the ReaderIO context.
func LogGo ¶
LogGo constructs a logger function using Go template syntax for formatting. The prefix string is parsed as a Go template and executed with a context struct containing both the reader context (R) and the value (A) as fields .R and .A. Both successful output and template errors are logged using log.Println.
Type Parameters:
- R: Reader context type
- A: Value type
Parameters:
- prefix: Go template string with access to .R (context) and .A (value)
Returns:
- A Kleisli arrow that logs the formatted output and returns the original value
Example:
type Config struct {
AppName string
}
type User struct {
Name string
Age int
}
result := pipe.Pipe2(
fetchUser(),
readerio.ChainFirst(readerio.LogGo[Config, User]("[{{.R.AppName}}] User: {{.A.Name}}, Age: {{.A.Age}}")),
processUser,
)(Config{AppName: "MyApp"})()
func Logf ¶
Logf constructs a logger function that can be used with ChainFirst or similar operations. The prefix string contains the format string for both the reader context (R) and the value (A). It uses log.Printf to output the formatted message.
Type Parameters:
- R: Reader context type
- A: Value type
Parameters:
- prefix: Format string that accepts two arguments: the reader context and the value
Returns:
- A Kleisli arrow that logs the context and value, then returns the original value
Example:
type Config struct {
AppName string
}
result := pipe.Pipe2(
fetchUser(),
readerio.ChainFirst(readerio.Logf[Config, User]("[%v] User: %+v")),
processUser,
)(Config{AppName: "MyApp"})()
func PrintGo ¶
PrintGo constructs a printer function using Go template syntax for formatting. The prefix string is parsed as a Go template and executed with a context struct containing both the reader context (R) and the value (A) as fields .R and .A. Successful output is printed to stdout using fmt.Println, while template errors are printed to stderr using fmt.Fprintln.
Type Parameters:
- R: Reader context type
- A: Value type
Parameters:
- prefix: Go template string with access to .R (context) and .A (value)
Returns:
- A Kleisli arrow that prints the formatted output and returns the original value
Example:
type Config struct {
Verbose bool
}
type Data struct {
ID int
Value string
}
result := pipe.Pipe2(
fetchData(),
readerio.ChainFirst(readerio.PrintGo[Config, Data]("{{if .R.Verbose}}[VERBOSE] {{end}}Data: {{.A.ID}} - {{.A.Value}}")),
processData,
)(Config{Verbose: true})()
func Printf ¶
Printf constructs a printer function that can be used with ChainFirst or similar operations. The prefix string contains the format string for both the reader context (R) and the value (A). Unlike Logf, this prints to stdout without log prefixes.
Type Parameters:
- R: Reader context type
- A: Value type
Parameters:
- prefix: Format string that accepts two arguments: the reader context and the value
Returns:
- A Kleisli arrow that prints the context and value, then returns the original value
Example:
type Config struct {
Debug bool
}
result := pipe.Pipe2(
fetchData(),
readerio.ChainFirst(readerio.Printf[Config, Data]("[%v] Data: %+v\n")),
processData,
)(Config{Debug: true})()
func Sequence ¶
func Sequence[R1, R2, A any](ma ReaderIO[R2, ReaderIO[R1, A]]) Kleisli[R2, R1, A]
Sequence swaps the order of nested environment parameters in a ReaderIO computation.
This function takes a ReaderIO that produces another ReaderIO 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 IO[A].
Type Parameters:
- R1: The first environment type (becomes inner after flip)
- R2: The second environment type (becomes outer after flip)
- A: The result type
Parameters:
- ma: A ReaderIO that takes R2 and produces a ReaderIO[R1, A]
Returns:
- A reader.Kleisli[R2, R1, IO[A]], which is func(R2) func(R1) IO[A]
The function preserves IO effects at both levels. The transformation can be visualized as:
Before: R2 -> IO[R1 -> IO[A]] After: R2 -> (R1 -> IO[A])
Example:
type Database struct {
ConnectionString string
}
type Config struct {
Timeout int
}
// Original: takes Config with IO, produces ReaderIO[Database, string]
original := func(cfg Config) io.IO[ReaderIO[Database, string]] {
return io.Of(func(db Database) io.IO[string] {
return io.Of(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 IO result
result := configReader(cfg)
// result is IO[string]
func SequenceReader ¶
func SequenceReader[R1, R2, A any](ma ReaderIO[R2, Reader[R1, A]]) Kleisli[R2, 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 IO effects) rather than another ReaderIO. It takes a ReaderIO that produces a Reader and returns a reader.Kleisli that produces IO effects.
This function is useful when you have a computation that depends on two environments where the outer environment is wrapped in IO (ReaderIO) and the inner is pure (Reader), and you need to change the order in which they are applied. The result moves the IO effect to the inner level.
Type Parameters:
- R1: The first environment type (from inner Reader, becomes outer after flip)
- R2: The second environment type (from outer ReaderIO, becomes inner after flip)
- A: The result type
Parameters:
- ma: A ReaderIO[R2, Reader[R1, A]] - a computation that takes R2 with IO effects and produces a pure Reader[R1, A]
Returns:
- A reader.Kleisli[R2, R1, IO[A]], which is func(R2) func(R1) IO[A]
The transformation can be visualized as:
Before: R2 -> IO[R1 -> A] After: R2 -> (R1 -> IO[A])
Note the key difference from Sequence:
- Sequence: Both levels have IO effects (ReaderIO[R2, ReaderIO[R1, A]] -> ReaderIO[R1, ReaderIO[R2, A]])
- SequenceReader: IO moves from outer to inner (ReaderIO[R2, Reader[R1, A]] -> Reader[R1, ReaderIO[R2, A]])
Example:
type Database struct { ConnectionString string }
type Config struct { Timeout int }
// Original: takes Database with IO, returns pure Reader[Config, string]
query := func(db Database) io.IO[reader.Reader[Config, string]] {
return io.Of(func(cfg Config) string {
return fmt.Sprintf("Query on %s with timeout %d", db.ConnectionString, cfg.Timeout)
})
}
// Sequenced: takes Config first, then Database
sequenced := readerio.SequenceReader(query)
db := Database{ConnectionString: "localhost:5432"}
cfg := Config{Timeout: 30}
// Apply config first to get a function that takes database
dbReader := sequenced(cfg)
// Then apply database to get the final IO result
result := dbReader(db)
// result is IO[string]
Use cases:
- Reordering dependencies when you want to defer IO effects
- Adapting functions where the IO effect should be associated with a different parameter
- Building pipelines that need pure outer layers with effectful inner computations
- Optimizing by controlling which environment access triggers IO effects
func TailRec ¶
func TailRec[R, A, B any](f Kleisli[R, A, Trampoline[A, B]]) Kleisli[R, A, B]
TailRec implements stack-safe tail recursion for the ReaderIO monad.
This function enables recursive computations that depend on an environment (Reader aspect) and perform side effects (IO aspect) without risking stack overflow. It uses an iterative loop to execute the recursion, making it safe for deep or unbounded recursion.
How It Works ¶
TailRec takes a Kleisli arrow that returns Trampoline[A, B]:
- Bounce(A): Continue recursion with the new state A
- Land(B): Terminate recursion and return the final result B
The function iteratively applies the Kleisli arrow, passing the environment R to each iteration, until a Land(B) value is produced. This combines:
- Environment dependency (Reader monad): Access to configuration, context, or dependencies
- Side effects (IO monad): Logging, file I/O, network calls, etc.
- Stack safety: Iterative execution prevents stack overflow
Type Parameters ¶
- R: The environment type (Reader context) - e.g., Config, Logger, Database connection
- A: The state type that changes during recursion
- B: The final result type when recursion terminates
Parameters ¶
- f: A Kleisli arrow (A => ReaderIO[R, Either[A, B]]) that:
- Takes the current state A
- Returns a ReaderIO that depends on environment R
- Produces Either[A, B] to control recursion flow
Returns ¶
A Kleisli arrow (A => ReaderIO[R, B]) that:
- Takes an initial state A
- Returns a ReaderIO that requires environment R
- Produces the final result B after recursion completes
Comparison with Other Monads ¶
Unlike IOEither and IOOption tail recursion:
- No error channel (like IOEither's Left error case)
- No failure case (like IOOption's None case)
- Adds environment dependency that's available throughout recursion
- Environment R is passed to every recursive step
Use Cases ¶
Environment-dependent recursive algorithms: - Recursive computations that need configuration at each step - Algorithms that log progress using an environment-provided logger - Recursive operations that access shared resources from environment
Stateful computations with context: - Tree traversals that need environment context - Graph algorithms with configuration-dependent behavior - Recursive parsers with environment-based rules
Recursive operations with side effects: - File system traversals with logging - Network operations with retry configuration - Database operations with connection pooling
Example: Factorial with Logging ¶
type Env struct {
Logger func(string)
}
// Factorial that logs each step
factorialStep := func(state struct{ n, acc int }) readerio.ReaderIO[Env, either.Either[struct{ n, acc int }, int]] {
return func(env Env) io.IO[either.Either[struct{ n, acc int }, int]] {
return func() either.Either[struct{ n, acc int }, int] {
if state.n <= 0 {
env.Logger(fmt.Sprintf("Factorial complete: %d", state.acc))
return either.Right[struct{ n, acc int }](state.acc)
}
env.Logger(fmt.Sprintf("Computing: %d * %d", state.n, state.acc))
return either.Left[int](struct{ n, acc int }{state.n - 1, state.acc * state.n})
}
}
}
factorial := readerio.TailRec(factorialStep)
env := Env{Logger: func(msg string) { fmt.Println(msg) }}
result := factorial(struct{ n, acc int }{5, 1})(env)() // Returns 120, logs each step
Example: Countdown with Configuration ¶
type Config struct {
MinValue int
Step int
}
countdownStep := func(n int) readerio.ReaderIO[Config, either.Either[int, int]] {
return func(cfg Config) io.IO[either.Either[int, int]] {
return func() either.Either[int, int] {
if n <= cfg.MinValue {
return either.Right[int](n)
}
return either.Left[int](n - cfg.Step)
}
}
}
countdown := readerio.TailRec(countdownStep)
config := Config{MinValue: 0, Step: 2}
result := countdown(10)(config)() // Returns 0 (10 -> 8 -> 6 -> 4 -> 2 -> 0)
Stack Safety ¶
The iterative implementation ensures that even deeply recursive computations (thousands or millions of iterations) will not cause stack overflow:
// Safe for very large inputs
sumToZero := readerio.TailRec(func(n int) readerio.ReaderIO[Env, tailrec.Trampoline[int, int]] {
return func(env Env) io.IO[tailrec.Trampoline[int, int]] {
return func() tailrec.Trampoline[int, int] {
if n <= 0 {
return tailrec.Land[int](0)
}
return tailrec.Bounce[int](n - 1)
}
}
})
result := sumToZero(1000000)(env)() // Safe, no stack overflow
Performance Considerations ¶
- Each iteration creates a new IO action by calling f(a)(r)()
- The environment R is passed to every iteration
- For performance-critical code, consider if the environment access is necessary
- Memoization of environment-derived values may improve performance
See Also ¶
type Operator ¶
Operator is a specialized Kleisli arrow that operates on ReaderIO values. It transforms a ReaderIO[R, A] into a ReaderIO[R, B], making it useful for building pipelines of ReaderIO operations. This is commonly used for middleware-style transformations and operation composition.
func Ap ¶
func Ap[B, R, A any](fa ReaderIO[R, A]) Operator[R, func(A) B, B]
Ap creates a function that applies a ReaderIO value to a ReaderIO function. This is the curried version suitable for use in pipelines.
Type Parameters:
- B: Result type
- R: Reader environment type
- A: Input value type
Parameters:
- fa: ReaderIO containing a value of type A
Returns:
- An Operator that applies the value to a function
Example:
result := F.Pipe1(
readerio.Of[Config](N.Mul(2)),
readerio.Ap[int](readerio.Of[Config](5)),
)(config)() // Returns 10
func Chain ¶
func Chain[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, B]
Chain creates a function that sequences ReaderIO computations. This is the curried version suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- f: Function that takes a value and returns a ReaderIO
Returns:
- An Operator that chains ReaderIO computations
Example:
result := F.Pipe1(
readerio.Of[Config](5),
readerio.Chain(func(n int) readerio.ReaderIO[Config, int] {
return readerio.Of[Config](n * 2)
}),
)(config)() // Returns 10
func ChainConsumer ¶
func ChainConsumer[R, A any](c Consumer[A]) Operator[R, A, struct{}]
func ChainFirst ¶
func ChainFirst[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, A]
ChainFirst creates a function that sequences ReaderIO computations but returns the first result. This is the curried version of MonadChainFirst, suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Intermediate value type (discarded)
Parameters:
- f: Function that produces a side-effect ReaderIO
Returns:
- An Operator that sequences computations while preserving the original value
Example:
result := F.Pipe1(
readerio.Of[Config](42),
readerio.ChainFirst(func(n int) readerio.ReaderIO[Config, string] {
return readerio.Of[Config](fmt.Sprintf("Logged: %d", n))
}),
)(config)() // Returns 42
func ChainFirstConsumer ¶
func ChainFirstConsumer[R, A any](c Consumer[A]) Operator[R, A, A]
func ChainFirstIOK ¶
ChainFirstIOK creates a function that chains a ReaderIO with an IO operation but keeps the original value. This is the curried version of MonadChainFirstIOK, suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: IO result type (discarded)
Parameters:
- f: Function that takes a value and returns an IO
Returns:
- An Operator that chains with IO while preserving the original value
Example:
result := F.Pipe1(
readerio.Of[Config](42),
readerio.ChainFirstIOK(func(n int) io.IO[string] {
return io.Of(fmt.Sprintf("Logged: %d", n))
}),
)(config)() // Returns 42
func ChainFirstReaderK ¶
ChainFirstReaderK creates a function that chains a Reader but keeps the original value. This is the curried version of MonadChainFirstReaderK, suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Reader result type (discarded)
Parameters:
- f: Function that produces a Reader
Returns:
- An Operator that chains Reader-returning functions while preserving the original value
Example:
result := F.Pipe1(
readerio.Of[Config](42),
readerio.ChainFirstReaderK(func(n int) reader.Reader[Config, string] {
return func(c Config) string { return fmt.Sprintf("Logged: %d", n) }
}),
)(config)() // Returns 42
func ChainIOK ¶
ChainIOK creates a function that chains a ReaderIO with an IO operation. This is the curried version suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- f: Function that takes a value and returns an IO
Returns:
- An Operator that chains ReaderIO with IO
Example:
result := F.Pipe1(
readerio.Of[Config](5),
readerio.ChainIOK(func(n int) io.IO[int] {
return io.Of(n * 2)
}),
)(config)() // Returns 10
func ChainReaderK ¶
ChainReaderK creates a function that chains a ReaderIO with a Reader-returning function. This is the curried version of MonadChainReaderK, suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- f: Function that produces a Reader
Returns:
- An Operator that chains Reader-returning functions
Example:
result := F.Pipe1(
readerio.Of[Config](5),
readerio.ChainReaderK(func(n int) reader.Reader[Config, int] {
return func(c Config) int { return n + c.Value }
}),
)(config)()
func Flap ¶
func Flap[R, B, A any](a A) Operator[R, func(A) B, B]
Flap creates a function that applies a value to a ReaderIO function. This is the curried version of MonadFlap, suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Result type
Parameters:
- a: The value to apply
Returns:
- An Operator that applies the value to a ReaderIO function
Example:
result := F.Pipe1(
readerio.Of[Config](N.Mul(2)),
readerio.Flap[Config](5),
)(config)() // Returns 10
func Map ¶
func Map[R, A, B any](f func(A) B) Operator[R, A, B]
Map creates a function that applies a transformation to a ReaderIO value. This is the curried version suitable for use in pipelines.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- f: The transformation function
Returns:
- An Operator that transforms ReaderIO[R, A] to ReaderIO[R, B]
Example:
result := F.Pipe1(
readerio.Of[Config](5),
readerio.Map[Config](N.Mul(2)),
)(config)() // Returns 10
func MapTo ¶
func MapTo[R, A, B any](b B) Operator[R, A, B]
MapTo creates an operator that executes a ReaderIO computation, discards its result, and returns a constant value. This is the curried version of MonadMapTo, suitable for use in pipelines.
IMPORTANT: ReaderIO represents a side-effectful computation (IO effects). For this reason, MapTo WILL execute the input ReaderIO to allow any side effects to occur (such as logging, file I/O, network calls, etc.), then discard the result and return the constant value. The side effects are preserved even though the result value is discarded.
Type Parameters:
- R: Reader environment type
- A: Input value type (result will be discarded after execution)
- B: Output value type (constant to return)
Parameters:
- b: The constant value to return after executing the ReaderIO
Returns:
- An Operator that executes a ReaderIO for its side effects, then returns b
Example:
logStep := func(r Config) io.IO[int] {
return io.Of(func() int {
fmt.Println("Step executed") // Side effect
return 42
})
}
result := F.Pipe1(
logStep,
readerio.MapTo[Config, int]("complete"),
)(config)() // Prints "Step executed", returns "complete"
func Tap ¶
func Tap[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, A]
Tap creates a function that executes a side-effect computation but returns the original value. This is the curried version of MonadTap, an alias for ChainFirst.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Side effect value type (discarded)
Parameters:
- f: Function that produces a side-effect ReaderIO
Returns:
- An Operator that taps ReaderIO computations
Example:
result := F.Pipe1(
readerio.Of[Config](42),
readerio.Tap(func(n int) readerio.ReaderIO[Config, func()] {
return readerio.FromIO[Config](io.Of(func() { fmt.Println(n) }))
}),
)(config)() // Returns 42, prints 42
func TapIOK ¶
TapIOK creates a function that chains a ReaderIO with an IO operation but keeps the original value. This is the curried version of MonadTapIOK, an alias for ChainFirstIOK.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: IO result type (discarded)
Parameters:
- f: Function that takes a value and returns an IO for side effects
Returns:
- An Operator that taps with IO-returning functions
Example:
result := F.Pipe1(
readerio.Of[Config](42),
readerio.TapIOK(func(n int) io.IO[func()] {
return io.Of(func() { fmt.Println(n) })
}),
)(config)() // Returns 42, prints 42
func TapReaderK ¶
TapReaderK creates a function that chains a Reader but keeps the original value. This is the curried version of MonadTapReaderK, an alias for ChainFirstReaderK.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Reader result type (discarded)
Parameters:
- f: Function that produces a Reader for side effects
Returns:
- An Operator that taps with Reader-returning functions
Example:
result := F.Pipe1(
readerio.Of[Config](42),
readerio.TapReaderK(func(n int) reader.Reader[Config, func()] {
return func(c Config) func() { return func() { fmt.Println(n) } }
}),
)(config)() // Returns 42, prints 42
func WithLock ¶
func WithLock[R, A any](lock func() context.CancelFunc) Operator[R, A, A]
WithLock executes the provided IO operation in the scope of a lock
type Reader ¶
Reader represents a computation that depends on an environment of type R and produces a value of type A. It's an alias for reader.Reader[R, A] and is used for dependency injection patterns.
type ReaderIO ¶
ReaderIO combines Reader and IO monads. It represents a computation that: 1. Depends on an environment of type R (Reader aspect) 2. Performs side effects and produces a value of type A (IO aspect) This is useful for operations that need both dependency injection and effect management.
func Ask ¶
func Ask[R any]() ReaderIO[R, R]
Ask retrieves the current environment. This is the fundamental operation for accessing the Reader context.
Type Parameters:
- R: Reader environment type
Returns:
- A ReaderIO that returns the environment
Example:
type Config struct { Port int }
rio := readerio.Ask[Config]()
config := Config{Port: 8080}
result := rio(config)() // Returns Config{Port: 8080}
func Asks ¶
func Asks[R, A any](r Reader[R, A]) ReaderIO[R, A]
Asks retrieves a value derived from the environment using a Reader function. This allows you to extract specific information from the environment.
Type Parameters:
- R: Reader environment type
- A: Result type
Parameters:
- r: Function that extracts a value from the environment
Returns:
- A ReaderIO that applies the function to the environment
Example:
type Config struct { Port int }
rio := readerio.Asks(func(c Config) io.IO[int] {
return io.Of(c.Port)
})
result := rio(Config{Port: 8080})() // Returns 8080
func Bracket ¶
func Bracket[ R, A, B, ANY any]( acquire ReaderIO[R, A], use Kleisli[R, A, B], release func(A, B) ReaderIO[R, ANY], ) ReaderIO[R, B]
func Defer ¶
func Defer[R, A any](gen func() ReaderIO[R, A]) ReaderIO[R, A]
Defer creates a ReaderIO by calling a generator function each time it's executed. This allows for lazy evaluation and ensures a fresh computation on each invocation. Useful for operations that should not be cached or memoized.
Type Parameters:
- R: Reader environment type
- A: Result type
Parameters:
- gen: Generator function that creates a new ReaderIO on each call
Returns:
- A ReaderIO that calls the generator function on each execution
Example:
counter := 0
rio := readerio.Defer(func() readerio.ReaderIO[Config, int] {
counter++
return readerio.Of[Config](counter)
})
result1 := rio(config)() // Returns 1
result2 := rio(config)() // Returns 2 (fresh computation)
func Do ¶
func Do[R, S any]( empty S, ) ReaderIO[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 {
Host string
Port int
}
type Config struct {
DefaultHost string
DefaultPort int
}
result := readerio.Do[Config](State{})
func Flatten ¶
func Flatten[R, A any](mma ReaderIO[R, ReaderIO[R, A]]) ReaderIO[R, A]
Flatten removes one level of nesting from a ReaderIO structure. Converts ReaderIO[R, ReaderIO[R, A]] to ReaderIO[R, A]. This is also known as "join" in monad terminology.
Type Parameters:
- R: Reader environment type
- A: Result type
Parameters:
- mma: A nested ReaderIO structure
Returns:
- A flattened ReaderIO with one less level of nesting
Example:
nested := readerio.Of[Config](readerio.Of[Config](42)) flattened := readerio.Flatten(nested) result := flattened(config)() // Returns 42
func FromIO ¶
FromIO converts an IO action to a ReaderIO that ignores the environment. This lifts a pure IO computation into the ReaderIO context.
Type Parameters:
- R: Reader environment type
- A: Result type
Parameters:
- t: The IO action to lift
Returns:
- A ReaderIO that executes the IO action regardless of the environment
Example:
ioAction := io.Of(42) readerIO := readerio.FromIO[Config](ioAction) result := readerIO(config)() // Returns 42
func FromReader ¶
func FromReader[R, A any](r Reader[R, A]) ReaderIO[R, A]
FromReader converts a Reader to a ReaderIO by lifting the pure computation into IO. This allows you to use Reader computations in a ReaderIO context.
Type Parameters:
- R: Reader environment type
- A: Result type
Parameters:
- r: The Reader to convert
Returns:
- A ReaderIO that wraps the Reader computation in IO
Example:
reader := func(config Config) int { return config.Port }
readerIO := readerio.FromReader(reader)
result := readerIO(config)() // Returns config.Port
func Memoize ¶
func Memoize[R, A any](rdr ReaderIO[R, A]) ReaderIO[R, A]
Memoize computes the value of the provided ReaderIO monad lazily but exactly once. The first execution caches the result, and subsequent executions return the cached value.
IMPORTANT: The context used to compute the value is the context of the first call. Do not use this method if the value has a functional dependency on the content of the context, as subsequent calls with different contexts will still return the memoized result from the first call.
Type Parameters:
- R: Reader environment type
- A: Result type
Parameters:
- rdr: The ReaderIO to memoize
Returns:
- A ReaderIO that caches its result after the first execution
Example:
expensive := readerio.Of[Config](computeExpensiveValue()) memoized := readerio.Memoize(expensive) result1 := memoized(config)() // Computes the value result2 := memoized(config)() // Returns cached value (no recomputation)
func MonadAp ¶
func MonadAp[B, R, A any](fab ReaderIO[R, func(A) B], fa ReaderIO[R, A]) ReaderIO[R, B]
MonadAp applies a function wrapped in a ReaderIO to a value wrapped in a ReaderIO. This is the applicative apply operation for ReaderIO.
Type Parameters:
- B: Result type
- R: Reader environment type
- A: Input value type
Parameters:
- fab: ReaderIO containing a function from A to B
- fa: ReaderIO containing a value of type A
Returns:
- A ReaderIO containing the result of applying the function to the value
Example:
fabIO := readerio.Of[Config](N.Mul(2)) faIO := readerio.Of[Config](5) result := readerio.MonadAp(fabIO, faIO)(config)() // Returns 10
func MonadApFirst ¶
func MonadApFirst[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, A]
MonadApFirst combines two effectful actions, keeping only the result of the first.
func MonadApPar ¶
func MonadApPar[B, R, A any](fab ReaderIO[R, func(A) B], fa ReaderIO[R, A]) ReaderIO[R, B]
MonadApPar is like MonadAp but allows parallel execution of effects where possible.
Type Parameters:
- B: Result type
- R: Reader environment type
- A: Input value type
Parameters:
- fab: ReaderIO containing a function from A to B
- fa: ReaderIO containing a value of type A
Returns:
- A ReaderIO containing the result, with potential parallel execution
func MonadApSecond ¶
func MonadApSecond[A, R, B any](first ReaderIO[R, A], second ReaderIO[R, B]) ReaderIO[R, B]
MonadApSecond combines two effectful actions, keeping only the result of the second.
func MonadApSeq ¶
func MonadApSeq[B, R, A any](fab ReaderIO[R, func(A) B], fa ReaderIO[R, A]) ReaderIO[R, B]
MonadApSeq is like MonadAp but ensures sequential execution of effects.
Type Parameters:
- B: Result type
- R: Reader environment type
- A: Input value type
Parameters:
- fab: ReaderIO containing a function from A to B
- fa: ReaderIO containing a value of type A
Returns:
- A ReaderIO containing the result, with sequential execution guaranteed
func MonadChain ¶
func MonadChain[R, A, B any](ma ReaderIO[R, A], f Kleisli[R, A, B]) ReaderIO[R, B]
MonadChain sequences two ReaderIO computations, where the second depends on the result of the first. This is the monadic bind operation for ReaderIO.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- ma: The first ReaderIO computation
- f: Function that takes the result of ma and returns a new ReaderIO
Returns:
- A ReaderIO that sequences both computations
Example:
rio1 := readerio.Of[Config](5)
result := readerio.MonadChain(rio1, func(n int) readerio.ReaderIO[Config, int] {
return readerio.Of[Config](n * 2)
})
func MonadChainFirst ¶
func MonadChainFirst[R, A, B any](ma ReaderIO[R, A], f Kleisli[R, A, B]) ReaderIO[R, A]
MonadChainFirst sequences two ReaderIO computations but returns the result of the first. The second computation is executed for its side effects only (e.g., logging, validation).
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Intermediate value type (discarded)
Parameters:
- ma: The first ReaderIO computation
- f: Function that produces the second ReaderIO (for side effects)
Returns:
- A ReaderIO with the result of the first computation
Example:
rio := readerio.Of[Config](42)
result := readerio.MonadChainFirst(rio, func(n int) readerio.ReaderIO[Config, string] {
// Log the value but don't change the result
return readerio.Of[Config](fmt.Sprintf("Logged: %d", n))
})
value := result(config)() // Returns 42, but logging happened
func MonadChainFirstIOK ¶
MonadChainFirstIOK chains a ReaderIO with an IO-returning function but keeps the original value. The IO computation is executed for its side effects only.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: IO result type (discarded)
Parameters:
- ma: The ReaderIO computation
- f: Function that takes a value and returns an IO
Returns:
- A ReaderIO with the original value after executing the IO
Example:
rio := readerio.Of[Config](42)
result := readerio.MonadChainFirstIOK(rio, func(n int) io.IO[string] {
return io.Of(fmt.Sprintf("Logged: %d", n))
})
value := result(config)() // Returns 42
func MonadChainFirstReaderK ¶
func MonadChainFirstReaderK[R, A, B any](ma ReaderIO[R, A], f reader.Kleisli[R, A, B]) ReaderIO[R, A]
MonadChainFirstReaderK chains a function that returns a Reader but keeps the original value. The Reader computation is executed for its side effects only.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Reader result type (discarded)
Parameters:
- ma: The ReaderIO to chain from
- f: Function that produces a Reader
Returns:
- A ReaderIO with the original value after executing the Reader
Example:
rio := readerio.Of[Config](42)
result := readerio.MonadChainFirstReaderK(rio, func(n int) reader.Reader[Config, string] {
return func(c Config) string { return fmt.Sprintf("Logged: %d", n) }
})
value := result(config)() // Returns 42
func MonadChainIOK ¶
MonadChainIOK chains a ReaderIO with a function that returns an IO. This is useful for integrating IO operations into a ReaderIO pipeline.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- ma: The ReaderIO computation
- f: Function that takes a value and returns an IO
Returns:
- A ReaderIO that sequences the computation with the IO operation
Example:
rio := readerio.Of[Config](5)
result := readerio.MonadChainIOK(rio, func(n int) io.IO[int] {
return io.Of(n * 2)
})
func MonadChainReaderK ¶
MonadChainReaderK chains a ReaderIO with a function that returns a Reader. The Reader is lifted into the ReaderIO context, allowing composition of Reader and ReaderIO operations.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- ma: The ReaderIO to chain from
- f: Function that produces a Reader
Returns:
- A new ReaderIO with the chained Reader computation
Example:
rio := readerio.Of[Config](5)
result := readerio.MonadChainReaderK(rio, func(n int) reader.Reader[Config, int] {
return func(c Config) int { return n + c.Value }
})
func MonadFlap ¶
func MonadFlap[R, B, A any](fab ReaderIO[R, func(A) B], a A) ReaderIO[R, B]
MonadFlap applies a value to a function wrapped in a ReaderIO. This is the "flipped" version of MonadAp, where the value comes second.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Result type
Parameters:
- fab: ReaderIO containing a function from A to B
- a: The value to apply to the function
Returns:
- A ReaderIO containing the result of applying the value to the function
Example:
fabIO := readerio.Of[Config](N.Mul(2)) result := readerio.MonadFlap(fabIO, 5)(config)() // Returns 10
func MonadMap ¶
func MonadMap[R, A, B any](fa ReaderIO[R, A], f func(A) B) ReaderIO[R, B]
MonadMap applies a function to the value inside a ReaderIO context. This is the monadic version that takes the ReaderIO as the first parameter.
Type Parameters:
- R: Reader environment type
- A: Input value type
- B: Output value type
Parameters:
- fa: The ReaderIO containing the value to transform
- f: The transformation function
Returns:
- A new ReaderIO with the transformed value
Example:
rio := readerio.Of[Config](5) doubled := readerio.MonadMap(rio, N.Mul(2)) result := doubled(config)() // Returns 10
func MonadMapTo ¶
func MonadMapTo[R, A, B any](fa ReaderIO[R, A], b B) ReaderIO[R, B]
MonadMapTo executes a ReaderIO computation, discards its result, and returns a constant value. This is the monadic version that takes both the ReaderIO and the constant value as parameters.
IMPORTANT: ReaderIO represents a side-effectful computation (IO effects). For this reason, MonadMapTo WILL execute the original ReaderIO to allow any side effects to occur (such as logging, file I/O, network calls, etc.), then discard the result and return the constant value. The side effects are preserved even though the result value is discarded.
Type Parameters:
- R: Reader environment type
- A: Input value type (result will be discarded after execution)
- B: Output value type (constant to return)
Parameters:
- fa: The ReaderIO to execute (side effects will occur, result discarded)
- b: The constant value to return after executing fa
Returns:
- A new ReaderIO that executes fa for its side effects, then returns b
Example:
logAndCompute := func(r Config) io.IO[int] {
return io.Of(func() int {
fmt.Println("Computing...") // Side effect
return 42
})
}
replaced := readerio.MonadMapTo(logAndCompute, "done")
result := replaced(config)() // Prints "Computing...", returns "done"
func MonadTap ¶
func MonadTap[R, A, B any](ma ReaderIO[R, A], f Kleisli[R, A, B]) ReaderIO[R, A]
MonadTap executes a side-effect computation but returns the original value. This is an alias for MonadChainFirst and is useful for operations like logging or validation that should not affect the main computation flow.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Side effect value type (discarded)
Parameters:
- ma: The ReaderIO to tap
- f: Function that produces a side-effect ReaderIO
Returns:
- A ReaderIO with the original value after executing the side effect
Example:
result := readerio.MonadTap(
readerio.Of[Config](42),
func(n int) readerio.ReaderIO[Config, func()] {
return readerio.FromIO[Config](io.Of(func() { fmt.Println(n) }))
},
)
func MonadTapIOK ¶
MonadTapIOK chains a ReaderIO with an IO-returning function but keeps the original value. This is an alias for MonadChainFirstIOK and is useful for side effects like logging.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: IO result type (discarded)
Parameters:
- ma: The ReaderIO to tap
- f: Function that takes a value and returns an IO for side effects
Returns:
- A ReaderIO with the original value after executing the IO
Example:
result := readerio.MonadTapIOK(
readerio.Of[Config](42),
func(n int) io.IO[func()] {
return io.Of(func() { fmt.Println(n) })
},
)
func MonadTapReaderK ¶
MonadTapReaderK chains a function that returns a Reader but keeps the original value. This is an alias for MonadChainFirstReaderK and is useful for side effects.
Type Parameters:
- R: Reader environment type
- A: Input and output value type
- B: Reader result type (discarded)
Parameters:
- ma: The ReaderIO to tap
- f: Function that produces a Reader for side effects
Returns:
- A ReaderIO with the original value after executing the Reader
Example:
result := readerio.MonadTapReaderK(
readerio.Of[Config](42),
func(n int) reader.Reader[Config, func()] {
return func(c Config) func() { return func() { fmt.Println(n) } }
},
)
func Of ¶
func Of[R, A any](a A) ReaderIO[R, A]
Of creates a ReaderIO that returns a pure value, ignoring the environment. This is the monadic return/pure operation for ReaderIO.
Type Parameters:
- R: Reader environment type
- A: Value type
Parameters:
- a: The value to wrap
Returns:
- A ReaderIO that always returns the given value
Example:
rio := readerio.Of[Config](42) result := rio(config)() // Returns 42
func Retrying ¶
func Retrying[R, A any]( policy retry.RetryPolicy, action Kleisli[R, retry.RetryStatus, A], check func(A) bool, ) ReaderIO[R, A]
func SequenceArray ¶
func SequenceArray[R, A any](ma []ReaderIO[R, A]) ReaderIO[R, []A]
SequenceArray converts an array of ReaderIO into a ReaderIO of an array.
This is useful when you have multiple independent computations and want to execute them all and collect their results.
Type parameters:
- R: The context type
- A: The element type
Parameters:
- ma: An array of ReaderIO computations
Returns:
A ReaderIO that produces an array of results
Example:
computations := []ReaderIO[Config, int]{
fetchCount("users"),
fetchCount("posts"),
fetchCount("comments"),
}
result := SequenceArray(computations)
// result(cfg)() returns [userCount, postCount, commentCount]
func SequenceT1 ¶
func SequenceT2 ¶
func SequenceT3 ¶
func SequenceT4 ¶
type Trampoline ¶
type Trampoline[B, L any] = tailrec.Trampoline[B, L]