readerresult

package
v2.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2025 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package readerresult provides logging utilities for the ReaderResult monad, which combines the Reader monad (for dependency injection via context.Context) with the Result monad (for error handling).

The logging functions in this package allow you to log Result values (both successes and errors) while preserving the functional composition style.

Package readerresult implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error

Package readerresult implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error.

Pure vs Effectful Functions

This package distinguishes between pure (side-effect free) and effectful (side-effectful) functions:

EFFECTFUL FUNCTIONS (depend on context.Context):

  • ReaderResult[A]: func(context.Context) (A, error) - Effectful computation that needs context
  • These functions are effectful because context.Context is effectful (can be cancelled, has deadlines, carries values)
  • Use for: operations that need cancellation, timeouts, context values, or any context-dependent behavior
  • Examples: database queries, HTTP requests, operations that respect cancellation

PURE FUNCTIONS (side-effect free):

  • func(State) (Value, error) - Pure computation that only depends on state, not context
  • func(State) Value - Pure transformation without errors
  • These functions are pure because they only read from their input state and don't depend on external context
  • Use for: parsing, validation, calculations, data transformations that don't need context
  • Examples: JSON parsing, input validation, mathematical computations

The package provides different bind operations for each:

  • Bind: For effectful ReaderResult computations (State -> ReaderResult[Value])
  • BindResultK: For pure functions with errors (State -> (Value, error))
  • Let: For pure functions without errors (State -> Value)
  • BindReaderK: For context-dependent pure functions (State -> Reader[Context, Value])
  • BindEitherK: For pure Result/Either values (State -> Result[Value])

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Ap

func Ap[A, B any](fa ReaderResult[A]) func(ReaderResult[func(A) B]) ReaderResult[B]

func ChainEitherK

func ChainEitherK[A, B any](f func(A) Either[B]) func(ma ReaderResult[A]) ReaderResult[B]

func ChainOptionK

func ChainOptionK[A, B any](onNone func() error) func(option.Kleisli[A, B]) Operator[A, B]

func Curry2

func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) Kleisli[T2, A]

func Curry3

func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) Kleisli[T3, A]

func From0

func From0[A any](f func(context.Context) (A, error)) func() ReaderResult[A]

func From2

func From2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1, T2) ReaderResult[A]

func From3

func From3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1, T2, T3) ReaderResult[A]

func Read

func Read[A any](r context.Context) func(ReaderResult[A]) Result[A]

func SequenceReader

func SequenceReader[R, A any](ma ReaderResult[Reader[R, A]]) reader.Kleisli[context.Context, R, Result[A]]

SequenceReader swaps the order of environment parameters when the inner computation is a Reader.

This function is specialized for the context.Context-based ReaderResult monad. It takes a ReaderResult that produces a Reader and returns a reader.Kleisli that produces Results. The context.Context is implicitly used as the outer environment type.

Type Parameters:

  • R: The inner environment type (becomes outer after flip)
  • A: The success value type

Parameters:

  • ma: A ReaderResult that takes context.Context and may produce a Reader[R, A]

Returns:

  • A reader.Kleisli[context.Context, R, Result[A]], which is func(context.Context) func(R) Result[A]

The function preserves error handling from the outer ReaderResult layer. If the outer computation fails, the error is propagated to the inner Result.

Note: This is an inline wrapper around readerresult.SequenceReader, specialized for context.Context as the outer environment type.

Example:

type Database struct {
    ConnectionString string
}

// Original: takes context, may fail, produces Reader[Database, string]
original := func(ctx context.Context) result.Result[reader.Reader[Database, string]] {
    if ctx.Err() != nil {
        return result.Error[reader.Reader[Database, string]](ctx.Err())
    }
    return result.Ok[error](func(db Database) string {
        return fmt.Sprintf("Query on %s", db.ConnectionString)
    })
}

// Sequenced: takes context first, then Database
sequenced := SequenceReader(original)

ctx := context.Background()
db := Database{ConnectionString: "localhost:5432"}

// Apply context first to get a function that takes database
dbReader := sequenced(ctx)
// Then apply database to get the final result
result := dbReader(db)
// result is Result[string]

Use Cases:

  • Dependency injection: Flip parameter order to inject context first, then dependencies
  • Testing: Separate context handling from business logic for easier testing
  • Composition: Enable point-free style by fixing the context parameter first

func TraverseReader

func TraverseReader[R, A, B any](
	f reader.Kleisli[R, A, B],
) func(ReaderResult[A]) Kleisli[R, B]

TraverseReader transforms a value using a Reader function and swaps environment parameter order.

This function combines mapping and parameter flipping in a single operation. It takes a Reader function (pure computation without error handling) and returns a function that: 1. Maps a ReaderResult[A] to ReaderResult[B] using the provided Reader function 2. Flips the parameter order so R comes before context.Context

Type Parameters:

  • R: The inner environment type (becomes outer after flip)
  • A: The input value type
  • B: The output value type

Parameters:

  • f: A reader.Kleisli[R, A, B], which is func(R) func(A) B - a pure Reader function

Returns:

  • A function that takes ReaderResult[A] and returns Kleisli[R, B]
  • Kleisli[R, B] is func(R) ReaderResult[B], which is func(R) func(context.Context) Result[B]

The function preserves error handling from the input ReaderResult. If the input computation fails, the error is propagated without applying the transformation function.

Note: This is a wrapper around readerresult.TraverseReader, specialized for context.Context.

Example:

type Config struct {
    MaxRetries int
}

// A pure Reader function that depends on Config
formatMessage := func(cfg Config) func(int) string {
    return func(value int) string {
        return fmt.Sprintf("Value: %d, MaxRetries: %d", value, cfg.MaxRetries)
    }
}

// Original computation that may fail
computation := func(ctx context.Context) result.Result[int] {
    if ctx.Err() != nil {
        return result.Error[int](ctx.Err())
    }
    return result.Ok[error](42)
}

// Create a traversal that applies formatMessage and flips parameters
traverse := TraverseReader[Config, int, string](formatMessage)

// Apply to the computation
flipped := traverse(computation)

// Now we can provide Config first, then context
cfg := Config{MaxRetries: 3}
ctx := context.Background()

result := flipped(cfg)(ctx)
// result is Result[string] containing "Value: 42, MaxRetries: 3"

Use Cases:

  • Dependency injection: Inject configuration/dependencies before context
  • Testing: Separate pure business logic from context handling
  • Composition: Build pipelines where dependencies are fixed before execution
  • Point-free style: Enable partial application by fixing dependencies first

func Uncurry1

func Uncurry1[T1, A any](f Kleisli[T1, A]) func(context.Context, T1) (A, error)

func Uncurry2

func Uncurry2[T1, T2, A any](f func(T1) Kleisli[T2, A]) func(context.Context, T1, T2) (A, error)

func Uncurry3

func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) Kleisli[T3, A]) func(context.Context, T1, T2, T3) (A, error)

Types

type Either

type Either[A any] = either.Either[error, A]

type Endomorphism

type Endomorphism[A any] = endomorphism.Endomorphism[A]

type Kleisli

type Kleisli[A, B any] = reader.Reader[A, ReaderResult[B]]

func ApS

func ApS[S1, S2, T any](
	setter func(T) func(S1) S2,
	fa ReaderResult[T],
) Kleisli[ReaderResult[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 EFFECTFUL computations to be combined without one depending on the result of the other.

IMPORTANT: ApS is for EFFECTFUL FUNCTIONS that depend on context.Context. The ReaderResult parameter is effectful because it depends on context.Context.

Unlike Bind, which sequences operations, ApS can be used when operations are independent and can conceptually run in parallel.

Example:

type State struct {
    UserID   string
    TenantID string
}

// These operations are independent and can be combined with ApS
getUserID := func(ctx context.Context) either.Either[error, string] {
    return either.Right[error](ctx.Value("userID").(string))
}
getTenantID := func(ctx context.Context) either.Either[error, string] {
    return either.Right[error](ctx.Value("tenantID").(string))
}

result := F.Pipe2(
    readereither.Do(State{}),
    readereither.ApS(
        func(uid string) func(State) State {
            return func(s State) State { s.UserID = uid; return s }
        },
        getUserID,
    ),
    readereither.ApS(
        func(tid string) func(State) State {
            return func(s State) State { s.TenantID = tid; return s }
        },
        getTenantID,
    ),
)

func ApSL

func ApSL[S, T any](
	lens Lens[S, T],
	fa ReaderResult[T],
) Kleisli[ReaderResult[S], S]

ApSL is a variant of ApS that uses a lens to focus on a specific field in the state. Instead of providing a setter function, you provide a lens that knows how to get and set the field. This is more convenient when working with nested structures.

Parameters:

  • lens: A lens that focuses on a field of type T within state S
  • fa: A ReaderResult computation that produces a value of type T

Returns:

  • A function that transforms ReaderResult[S] to ReaderResult[S] by setting the focused field

Example:

type Person struct {
    Name string
    Age  int
}

ageLens := lens.MakeLens(
    func(p Person) int { return p.Age },
    func(p Person, a int) Person { p.Age = a; return p },
)

getAge := func(ctx context.Context) either.Either[error, int] {
    return either.Right[error](30)
}

result := F.Pipe1(
    readereither.Do(Person{Name: "Alice", Age: 25}),
    readereither.ApSL(ageLens, getAge),
)

func Bind

func Bind[S1, S2, T any](
	setter func(T) func(S1) S2,
	f Kleisli[S1, T],
) Kleisli[ReaderResult[S1], S2]

Bind attaches the result of an EFFECTFUL 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 context.Context from the environment.

IMPORTANT: Bind is for EFFECTFUL FUNCTIONS that depend on context.Context. The function parameter takes state and returns a ReaderResult[T], which is effectful because it depends on context.Context (can be cancelled, has deadlines, carries values).

For PURE FUNCTIONS (side-effect free), use:

  • BindResultK: For pure functions with errors (State -> (Value, error))
  • Let: For pure functions without errors (State -> Value)

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 {
    UserID   string
    TenantID string
}

result := F.Pipe2(
    readereither.Do(State{}),
    readereither.Bind(
        func(uid string) func(State) State {
            return func(s State) State { s.UserID = uid; return s }
        },
        func(s State) readereither.ReaderResult[string] {
            return func(ctx context.Context) either.Either[error, string] {
                if uid, ok := ctx.Value("userID").(string); ok {
                    return either.Right[error](uid)
                }
                return either.Left[string](errors.New("no userID"))
            }
        },
    ),
    readereither.Bind(
        func(tid string) func(State) State {
            return func(s State) State { s.TenantID = tid; return s }
        },
        func(s State) readereither.ReaderResult[string] {
            // This can access s.UserID from the previous step
            return func(ctx context.Context) either.Either[error, string] {
                return either.Right[error]("tenant-" + s.UserID)
            }
        },
    ),
)

func BindL

func BindL[S, T any](
	lens Lens[S, T],
	f Kleisli[T, T],
) Kleisli[ReaderResult[S], S]

BindL is a variant of Bind that uses a lens to focus on a specific field in the state. It combines the lens-based field access with monadic composition for EFFECTFUL computations.

IMPORTANT: BindL is for EFFECTFUL FUNCTIONS that depend on context.Context. The function parameter returns a ReaderResult, which is effectful.

It allows you to: 1. Extract a field value using the lens 2. Use that value in an effectful computation that may fail 3. Update the field with the result

Parameters:

  • lens: A lens that focuses on a field of type T within state S
  • f: A function that takes the current field value and returns a ReaderResult computation

Returns:

  • A function that transforms ReaderResult[S] to ReaderResult[S]

Example:

type Counter struct {
    Value int
}

valueLens := lens.MakeLens(
    func(c Counter) int { return c.Value },
    func(c Counter, v int) Counter { c.Value = v; return c },
)

increment := func(v int) readereither.ReaderResult[int] {
    return func(ctx context.Context) either.Either[error, int] {
        if v >= 100 {
            return either.Left[int](errors.New("value too large"))
        }
        return either.Right[error](v + 1)
    }
}

result := F.Pipe1(
    readereither.Of[error](Counter{Value: 42}),
    readereither.BindL(valueLens, increment),
)

func Curry1

func Curry1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A]

func From1

func From1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A]

func FromPredicate

func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A]

func Let

func Let[S1, S2, T any](
	setter func(T) func(S1) S2,
	f func(S1) T,
) Kleisli[ReaderResult[S1], S2]

Let attaches the result of a PURE computation to a context [S1] to produce a context [S2].

IMPORTANT: Let is for PURE FUNCTIONS (side-effect free) that don't depend on context.Context. The function parameter takes state and returns a value directly, with no errors or effects.

For EFFECTFUL FUNCTIONS (that need context.Context), use:

  • Bind: For effectful ReaderResult computations (State -> ReaderResult[Value])

For PURE FUNCTIONS with error handling, use:

  • BindResultK: For pure functions with errors (State -> (Value, error))

func LetL

func LetL[S, T any](
	lens Lens[S, T],
	f Endomorphism[T],
) Kleisli[ReaderResult[S], S]

LetL is a variant of Let that uses a lens to focus on a specific field in the state. It applies a PURE transformation to the focused field without any effects.

IMPORTANT: LetL is for PURE FUNCTIONS (side-effect free) that don't depend on context.Context. The function parameter is a pure endomorphism (T -> T) with no errors or effects.

Parameters:

  • lens: A lens that focuses on a field of type T within state S
  • f: A pure function that transforms the field value

Returns:

  • A function that transforms ReaderResult[S] to ReaderResult[S]

Example:

type Counter struct {
    Value int
}

valueLens := lens.MakeLens(
    func(c Counter) int { return c.Value },
    func(c Counter, v int) Counter { c.Value = v; return c },
)

double := func(v int) int { return v * 2 }

result := F.Pipe1(
    readereither.Of[error](Counter{Value: 21}),
    readereither.LetL(valueLens, double),
)
// result when executed will be Right(Counter{Value: 42})

func LetTo

func LetTo[S1, S2, T any](
	setter func(T) func(S1) S2,
	b T,
) Kleisli[ReaderResult[S1], S2]

LetTo attaches a constant value to a context [S1] to produce a context [S2]. This is a PURE operation (side-effect free) that simply sets a field to a constant value.

func LetToL

func LetToL[S, T any](
	lens Lens[S, T],
	b T,
) Kleisli[ReaderResult[S], S]

LetToL is a variant of LetTo that uses a lens to focus on a specific field in the state. It sets the focused field to a constant value. This is a PURE operation (side-effect free).

Parameters:

  • lens: A lens that focuses on a field of type T within state S
  • b: The constant value to set

Returns:

  • A function that transforms ReaderResult[S] to ReaderResult[S]

Example:

type Config struct {
    Debug   bool
    Timeout int
}

debugLens := lens.MakeLens(
    func(c Config) bool { return c.Debug },
    func(c Config, d bool) Config { c.Debug = d; return c },
)

result := F.Pipe1(
    readereither.Of[error](Config{Debug: true, Timeout: 30}),
    readereither.LetToL(debugLens, false),
)
// result when executed will be Right(Config{Debug: false, Timeout: 30})

func OrElse

func OrElse[A any](onLeft Kleisli[error, A]) Kleisli[ReaderResult[A], A]

func SLog

func SLog[A any](message string) Kleisli[Result[A], A]

SLog creates a Kleisli arrow that logs a Result value at INFO level using the logger from the context. This is a convenience function that uses SLogWithCallback with default settings.

The Result value is logged and then returned unchanged, making this function suitable for use in functional pipelines for debugging or monitoring purposes.

This function logs both successful values and errors:

  • Success values are logged with the key "value"
  • Error values are logged with the key "error"

Type Parameters:

  • A: The type of the success value in the Result

Parameters:

  • message: The log message to display

Returns:

  • A Kleisli arrow that takes a Result[A] and returns a ReaderResult[A] The returned ReaderResult, when executed with a context, logs the Result at INFO level and returns it unchanged

Example - Logging a successful computation:

ctx := context.Background()

// Simple value logging
res := result.Of(42)
logged := SLog[int]("Processing number")(res)(ctx)
// Logs: level=INFO msg="Processing number" value=42
// logged == result.Of(42)

Example - Logging in a pipeline:

type User struct {
    ID   int
    Name string
}

fetchUser := func(id int) result.Result[User] {
    return result.Of(User{ID: id, Name: "Alice"})
}

processUser := func(user User) result.Result[string] {
    return result.Of(fmt.Sprintf("Processed: %s", user.Name))
}

ctx := context.Background()

// Log at each step
userResult := fetchUser(123)
logged1 := SLog[User]("Fetched user")(userResult)(ctx)
// Logs: level=INFO msg="Fetched user" value={ID:123 Name:Alice}

processed := result.Chain(processUser)(logged1)
logged2 := SLog[string]("Processed user")(processed)(ctx)
// Logs: level=INFO msg="Processed user" value="Processed: Alice"

Example - Logging errors:

err := errors.New("database connection failed")
errResult := result.Left[User](err)
logged := SLog[User]("Database operation")(errResult)(ctx)
// Logs: level=INFO msg="Database operation" error="database connection failed"
// logged still contains the error

Example - Using with context logger:

// Set up a custom logger in the context
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
ctx := logging.WithLogger(logger)(context.Background())

res := result.Of("important data")
logged := SLog[string]("Critical operation")(res)(ctx)
// Uses the logger from context to log the message

Note: The function uses logging.GetLoggerFromContext to retrieve the logger, which falls back to the global logger if no logger is found in the context.

func SLogWithCallback

func SLogWithCallback[A any](
	logLevel slog.Level,
	cb func(context.Context) *slog.Logger,
	message string) Kleisli[Result[A], A]

SLogWithCallback creates a Kleisli arrow that logs a Result value using a custom logger callback and log level. The Result value is logged and then returned unchanged, making this function suitable for use in functional pipelines.

This function logs both successful values and errors:

  • Success values are logged with the key "value"
  • Error values are logged with the key "error"

The logging is performed as a side effect while preserving the Result value, allowing it to be used in the middle of a computation pipeline without interrupting the flow.

Type Parameters:

  • A: The type of the success value in the Result

Parameters:

  • logLevel: The slog.Level at which to log (e.g., LevelInfo, LevelDebug, LevelError)
  • cb: A callback function that retrieves a *slog.Logger from the context
  • message: The log message to display

Returns:

  • A Kleisli arrow that takes a Result[A] and returns a ReaderResult[A] The returned ReaderResult, when executed with a context, logs the Result and returns it unchanged

Example:

type User struct {
    ID   int
    Name string
}

// Custom logger callback
getLogger := func(ctx context.Context) *slog.Logger {
    return slog.Default()
}

// Create a logging function for debug level
logDebug := SLogWithCallback[User](slog.LevelDebug, getLogger, "User data")

// Use in a pipeline
ctx := context.Background()
user := result.Of(User{ID: 123, Name: "Alice"})
logged := logDebug(user)(ctx) // Logs: level=DEBUG msg="User data" value={ID:123 Name:Alice}
// logged still contains the User value

Example with error:

err := errors.New("user not found")
userResult := result.Left[User](err)
logged := logDebug(userResult)(ctx) // Logs: level=DEBUG msg="User data" error="user not found"
// logged still contains the error

func TailRec

func TailRec[A, B any](f Kleisli[A, Trampoline[A, B]]) Kleisli[A, B]

TailRec implements tail-recursive computation for ReaderResult with context cancellation support.

TailRec takes a Kleisli function that returns Trampoline[A, B] and converts it into a stack-safe, tail-recursive computation. The function repeatedly applies the Kleisli until it produces a Land value.

The implementation includes a short-circuit mechanism that checks for context cancellation on each iteration. If the context is canceled (ctx.Err() != nil), the computation immediately returns a Left result containing the context's cause error, preventing unnecessary computation.

Type Parameters:

  • A: The input type for the recursive step
  • B: The final result type

Parameters:

  • f: A Kleisli function that takes an A and returns a ReaderResult containing Trampoline[A, B]. When the result is Bounce(a), recursion continues with the new value 'a'. When the result is Land(b), recursion terminates with the final value 'b'.

Returns:

  • A Kleisli function that performs the tail-recursive computation in a stack-safe manner.

Behavior:

  • On each iteration, checks if the context has been canceled (short circuit)
  • If canceled, returns result.Left[B](context.Cause(ctx))
  • If the step returns Left[B](error), propagates the error
  • If the step returns Right[A](Bounce(a)), continues recursion with new value 'a'
  • If the step returns Right[A](Land(b)), terminates with success value 'b'

Example - Factorial computation with context:

type State struct {
    n   int
    acc int
}

factorialStep := func(state State) ReaderResult[tailrec.Trampoline[State, int]] {
    return func(ctx context.Context) result.Result[tailrec.Trampoline[State, int]] {
        if state.n <= 0 {
            return result.Of(tailrec.Land[State](state.acc))
        }
        return result.Of(tailrec.Bounce[int](State{state.n - 1, state.acc * state.n}))
    }
}

factorial := TailRec(factorialStep)
result := factorial(State{5, 1})(ctx) // Returns result.Of(120)

Example - Context cancellation:

ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel immediately

computation := TailRec(someStep)
result := computation(initialValue)(ctx)
// Returns result.Left[B](context.Cause(ctx)) without executing any steps

func TraverseArray

func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B]

TraverseArray transforms an array

func TraverseArrayWithIndex

func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderResult[B]) Kleisli[[]A, []B]

TraverseArrayWithIndex transforms an array

func WithContextK

func WithContextK[A, B any](f Kleisli[A, B]) Kleisli[A, B]

type Lens

type Lens[S, T any] = lens.Lens[S, T]

type Operator

type Operator[A, B any] = Kleisli[ReaderResult[A], B]

func BindTo

func BindTo[S1, T any](
	setter func(T) S1,
) Operator[T, S1]

BindTo initializes a new state [S1] from a value [T]

func BindToP

func BindToP[S1, T any](
	setter Prism[S1, T],
) Operator[T, S1]

func Chain

func Chain[A, B any](f Kleisli[A, B]) Operator[A, B]

func ChainFirst

func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A]

func ChainTo

func ChainTo[A, B any](b ReaderResult[B]) Operator[A, B]

ChainTo creates an operator that sequences two ReaderResult computations where the second ignores the first's success value. This is the curried version where the second ReaderResult is provided first, returning a function that can be applied to any first ReaderResult.

IMPORTANT: ReaderResult represents a side-effectful computation because it depends on context.Context, which is effectful (can be cancelled, has deadlines, carries values). For this reason, ChainTo WILL execute the first ReaderResult to allow any side effects to occur, then discard the success result and execute the second ReaderResult with the same context. If the first computation fails, the error is returned immediately without executing the second computation.

Type Parameters:

  • A: The success type of the first ReaderResult (will be discarded if successful)
  • B: The success type of the second ReaderResult

Parameters:

  • b: The second ReaderResult to execute after the first succeeds

Returns:

  • An Operator that executes the first ReaderResult, then b if successful

Example:

logEnd := func(ctx context.Context) result.Result[string] {
    fmt.Println("ending")
    return result.Of("done")
}
thenLogEnd := readerresult.ChainTo[int, string](logEnd)

logStart := func(ctx context.Context) result.Result[int] {
    fmt.Println("starting")
    return result.Of(1)
}
pipeline := thenLogEnd(logStart)
result := pipeline(context.Background()) // Prints "starting" then "ending", returns Right("done")

Example - In a functional pipeline:

step1 := func(ctx context.Context) result.Result[int] {
    fmt.Println("step 1")
    return result.Of(1)
}
step2 := func(ctx context.Context) result.Result[string] {
    fmt.Println("step 2")
    return result.Of("complete")
}
pipeline := F.Pipe1(
    step1,
    readerresult.ChainTo[int, string](step2),
)
output := pipeline(context.Background()) // Prints "step 1" then "step 2", returns Right("complete")

func Flap

func Flap[B, A any](a A) Operator[func(A) B, B]

func Map

func Map[A, B any](f func(A) B) Operator[A, B]

func MapTo

func MapTo[A, B any](b B) Operator[A, B]

MapTo creates an operator that executes a ReaderResult computation, discards its success value, and returns a constant value. This is the curried version where the constant value is provided first, returning a function that can be applied to any ReaderResult.

IMPORTANT: ReaderResult represents a side-effectful computation because it depends on context.Context, which is effectful (can be cancelled, has deadlines, carries values). For this reason, MapTo WILL execute the input ReaderResult to allow any side effects to occur, then discard the success result and return the constant value. If the computation fails, the error is preserved.

Type Parameters:

  • A: The success type of the input ReaderResult (will be discarded if successful)
  • B: The type of the constant value to return on success

Parameters:

  • b: The constant value to return on success

Returns:

  • An Operator that executes a ReaderResult[A], preserves errors, but replaces success with b

Example:

logStep := func(ctx context.Context) result.Result[int] {
    fmt.Println("step executed")
    return result.Of(42)
}
toDone := readerresult.MapTo[int, string]("done")
pipeline := toDone(logStep)
result := pipeline(context.Background()) // Prints "step executed", returns Right("done")

Example - In a functional pipeline:

step1 := func(ctx context.Context) result.Result[int] {
    fmt.Println("processing")
    return result.Of(1)
}
pipeline := F.Pipe1(
    step1,
    readerresult.MapTo[int, string]("complete"),
)
output := pipeline(context.Background()) // Prints "processing", returns Right("complete")

func TapSLog

func TapSLog[A any](message string) Operator[A, A]

type Option

type Option[A any] = option.Option[A]

type Prism

type Prism[S, T any] = prism.Prism[S, T]

type Reader

type Reader[R, A any] = reader.Reader[R, A]

type ReaderResult

type ReaderResult[A any] = readereither.ReaderEither[context.Context, error, A]

ReaderResult is a specialization of the Reader monad for the typical golang scenario

func Ask

func Ask() ReaderResult[context.Context]

func Curry0

func Curry0[A any](f func(context.Context) (A, error)) ReaderResult[A]

func Do

func Do[S any](
	empty S,
) ReaderResult[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 {
    UserID   string
    TenantID string
}
result := readereither.Do(State{})

func FromEither

func FromEither[A any](e Either[A]) ReaderResult[A]

func FromReader

func FromReader[A any](r Reader[context.Context, A]) ReaderResult[A]

func Left

func Left[A any](l error) ReaderResult[A]

func MonadAp

func MonadAp[A, B any](fab ReaderResult[func(A) B], fa ReaderResult[A]) ReaderResult[B]

func MonadChain

func MonadChain[A, B any](ma ReaderResult[A], f Kleisli[A, B]) ReaderResult[B]

func MonadChainEitherK

func MonadChainEitherK[A, B any](ma ReaderResult[A], f func(A) Either[B]) ReaderResult[B]

func MonadChainFirst

func MonadChainFirst[A, B any](ma ReaderResult[A], f Kleisli[A, B]) ReaderResult[A]

func MonadChainTo

func MonadChainTo[A, B any](ma ReaderResult[A], b ReaderResult[B]) ReaderResult[B]

MonadChainTo sequences two ReaderResult computations where the second ignores the first's success value. This is the monadic version that takes both ReaderResults as parameters.

IMPORTANT: ReaderResult represents a side-effectful computation because it depends on context.Context, which is effectful (can be cancelled, has deadlines, carries values). For this reason, MonadChainTo WILL execute the first ReaderResult to allow any side effects to occur, then discard the success result and execute the second ReaderResult with the same context. If the first computation fails, the error is returned immediately without executing the second computation.

Type Parameters:

  • A: The success type of the first ReaderResult (will be discarded if successful)
  • B: The success type of the second ReaderResult

Parameters:

  • ma: The first ReaderResult to execute (side effects will occur, success value discarded)
  • b: The second ReaderResult to execute if ma succeeds

Returns:

  • A ReaderResult that executes ma, then b if ma succeeds, returning b's result

Example:

logStart := func(ctx context.Context) result.Result[int] {
    fmt.Println("starting")
    return result.Of(1)
}
logEnd := func(ctx context.Context) result.Result[string] {
    fmt.Println("ending")
    return result.Of("done")
}
r := readerresult.MonadChainTo(logStart, logEnd)
result := r(context.Background()) // Prints "starting" then "ending", returns Right("done")

func MonadFlap

func MonadFlap[B, A any](fab ReaderResult[func(A) B], a A) ReaderResult[B]

func MonadMap

func MonadMap[A, B any](fa ReaderResult[A], f func(A) B) ReaderResult[B]

func MonadMapTo

func MonadMapTo[A, B any](ma ReaderResult[A], b B) ReaderResult[B]

MonadMapTo executes a ReaderResult computation, discards its success value, and returns a constant value. This is the monadic version that takes both the ReaderResult and the constant value as parameters.

IMPORTANT: ReaderResult represents a side-effectful computation because it depends on context.Context, which is effectful (can be cancelled, has deadlines, carries values). For this reason, MonadMapTo WILL execute the original ReaderResult to allow any side effects to occur, then discard the success result and return the constant value. If the original computation fails, the error is preserved.

Type Parameters:

  • A: The success type of the first ReaderResult (will be discarded if successful)
  • B: The type of the constant value to return on success

Parameters:

  • ma: The ReaderResult to execute (side effects will occur, success value discarded)
  • b: The constant value to return if ma succeeds

Returns:

  • A ReaderResult that executes ma, preserves errors, but replaces success values with b

Example:

type Config struct { Counter int }
increment := func(ctx context.Context) result.Result[int] {
    // Side effect: log the operation
    fmt.Println("incrementing")
    return result.Of(5)
}
r := readerresult.MonadMapTo(increment, "done")
result := r(context.Background()) // Prints "incrementing", returns Right("done")

func Of

func Of[A any](a A) ReaderResult[A]

func Retrying

func Retrying[A any](
	policy R.RetryPolicy,
	action Kleisli[R.RetryStatus, A],
	check func(Result[A]) bool,
) ReaderResult[A]
func Right[A any](r A) ReaderResult[A]

func SequenceArray

func SequenceArray[A any](ma []ReaderResult[A]) ReaderResult[[]A]

SequenceArray converts a homogeneous sequence of either into an either of sequence

func SequenceT1

func SequenceT1[A any](a ReaderResult[A]) ReaderResult[tuple.Tuple1[A]]

func SequenceT2

func SequenceT2[A, B any](a ReaderResult[A], b ReaderResult[B]) ReaderResult[tuple.Tuple2[A, B]]

func SequenceT3

func SequenceT3[A, B, C any](a ReaderResult[A], b ReaderResult[B], c ReaderResult[C]) ReaderResult[tuple.Tuple3[A, B, C]]

func SequenceT4

func SequenceT4[A, B, C, D any](a ReaderResult[A], b ReaderResult[B], c ReaderResult[C], d ReaderResult[D]) ReaderResult[tuple.Tuple4[A, B, C, D]]

func WithContext

func WithContext[A any](ma ReaderResult[A]) ReaderResult[A]

withContext wraps an existing ReaderResult and performs a context check for cancellation before deletating

type Result

type Result[A any] = result.Result[A]

type Trampoline

type Trampoline[A, B any] = tailrec.Trampoline[A, B]

Directories

Path Synopsis
package readerresult implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error
package readerresult implements a specialization of the Reader monad assuming a golang context as the context of the monad and a standard golang error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL