Documentation
¶
Overview ¶
Package reader provides a specialization of the Reader monad for context.Context.
This package offers a context-aware Reader monad that simplifies working with Go's context.Context in a functional programming style. It eliminates the need to explicitly thread context through function calls while maintaining type safety and composability.
Core Concept ¶
The Reader monad represents computations that depend on a shared environment. In this package, that environment is fixed to context.Context, making it particularly useful for:
- Request-scoped data propagation
- Cancellation and timeout handling
- Dependency injection via context values
- Avoiding explicit context parameter threading
Type Definitions ¶
- Reader[A]: A computation that depends on context.Context and produces A
- Kleisli[A, B]: A function from A to Reader[B] for composing computations
- Operator[A, B]: A transformation from Reader[A] to Reader[B]
Usage Pattern ¶
Instead of passing context explicitly through every function:
func processUser(ctx context.Context, userID string) (User, error) {
user := fetchUser(ctx, userID)
profile := fetchProfile(ctx, user.ProfileID)
return enrichUser(ctx, user, profile), nil
}
You can use Reader to compose context-dependent operations:
fetchUser := func(userID string) Reader[User] {
return func(ctx context.Context) User {
// Use ctx for database access, cancellation, etc.
return queryDatabase(ctx, userID)
}
}
processUser := func(userID string) Reader[User] {
return F.Pipe2(
fetchUser(userID),
reader.Chain(func(user User) Reader[Profile] {
return fetchProfile(user.ProfileID)
}),
reader.Map(func(profile Profile) User {
return enrichUser(user, profile)
}),
)
}
// Execute with context
ctx := context.Background()
user := processUser("user123")(ctx)
Integration with Standard Library ¶
This package works seamlessly with Go's standard context package:
- Context cancellation and deadlines are preserved
- Context values can be accessed within Reader computations
- Readers can be composed with context-aware libraries
Relationship to Other Packages ¶
This package is a specialization of github.com/IBM/fp-go/v2/reader where the environment type R is fixed to context.Context. For more general Reader operations, see the base reader package.
For combining Reader with other monads:
- github.com/IBM/fp-go/v2/context/readerio: Reader + IO effects
- github.com/IBM/fp-go/v2/readeroption: Reader + Option
- github.com/IBM/fp-go/v2/readerresult: Reader + Result (Either)
Example: HTTP Request Handler ¶
type RequestContext struct {
UserID string
RequestID string
}
// Extract request context from context.Context
getRequestContext := func(ctx context.Context) RequestContext {
return RequestContext{
UserID: ctx.Value("userID").(string),
RequestID: ctx.Value("requestID").(string),
}
}
// A Reader that logs with request context
logInfo := func(message string) Reader[function.Void] {
return func(ctx context.Context) function.Void {
reqCtx := getRequestContext(ctx)
log.Printf("[%s] User %s: %s", reqCtx.RequestID, reqCtx.UserID, message)
return function.VOID
}
}
// Compose operations
handleRequest := func(data string) Reader[Response] {
return F.Pipe2(
logInfo("Processing request"),
reader.Chain(func(_ function.Void) Reader[Result] {
return processData(data)
}),
reader.Map(func(result Result) Response {
return Response{Data: result}
}),
)
}
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Kleisli ¶
Kleisli represents a Kleisli arrow for the context-based Reader monad.
It's a function from A to Reader[B], used for composing Reader computations that all depend on the same context.Context.
Type Parameters:
- A: The input type
- B: The output type wrapped in Reader
Kleisli[A, B] is equivalent to func(A) func(context.Context) B
Kleisli arrows are fundamental for monadic composition, allowing you to chain operations that depend on context without explicitly passing the context through each function call.
Example:
// A Kleisli arrow that creates a greeting Reader from a name
greet := func(name string) Reader[string] {
return func(ctx context.Context) string {
if deadline, ok := ctx.Deadline(); ok {
return fmt.Sprintf("Hello %s (deadline: %v)", name, deadline)
}
return fmt.Sprintf("Hello %s", name)
}
}
// Use the Kleisli arrow
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
greeting := greet("Alice")(ctx) // "Hello Alice (deadline: ...)"
type Operator ¶
Operator represents a transformation from one Reader to another.
It takes a Reader[A] and produces a Reader[B], where both readers depend on the same context.Context. This type is commonly used for operations like Map, Chain, and other transformations that convert readers while preserving the context dependency.
Type Parameters:
- A: The input Reader's result type
- B: The output Reader's result type
Operator[A, B] is equivalent to func(Reader[A]) func(context.Context) B
Operators enable building pipelines of context-dependent computations where each step can transform the result of the previous computation while maintaining access to the shared context.
Example:
// An operator that transforms int readers to string readers
intToString := func(r Reader[int]) Reader[string] {
return func(ctx context.Context) string {
value := r(ctx)
return strconv.Itoa(value)
}
}
// A Reader that extracts a timeout value from context
getTimeout := func(ctx context.Context) int {
if deadline, ok := ctx.Deadline(); ok {
return int(time.Until(deadline).Seconds())
}
return 0
}
// Transform the Reader
getTimeoutStr := intToString(getTimeout)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
result := getTimeoutStr(ctx) // "30" (approximately)
type Reader ¶
Reader represents a computation that depends on a context.Context and produces a value of type A.
This is a specialization of the generic Reader monad where the environment type is fixed to context.Context. This is particularly useful for Go applications that need to thread context through computations for cancellation, deadlines, and request-scoped values.
Type Parameters:
- A: The result type produced by the computation
Reader[A] is equivalent to func(context.Context) A
The Reader monad enables:
- Dependency injection using context values
- Cancellation and timeout handling
- Request-scoped data propagation
- Avoiding explicit context parameter threading
Example:
// A Reader that extracts a user ID from context
getUserID := func(ctx context.Context) string {
if userID, ok := ctx.Value("userID").(string); ok {
return userID
}
return "anonymous"
}
// A Reader that checks if context is cancelled
isCancelled := func(ctx context.Context) bool {
select {
case <-ctx.Done():
return true
default:
return false
}
}
// Use the readers with a context
ctx := context.WithValue(context.Background(), "userID", "user123")
userID := getUserID(ctx) // "user123"
cancelled := isCancelled(ctx) // false