statereaderioeither

package
v2.0.3 Latest Latest
Warning

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

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

Documentation

Overview

Package statereaderioeither provides a functional programming abstraction that combines four powerful concepts: State, Reader, IO, and Either monads.

Fantasy Land Specification

This is a monad transformer combining:

Implemented Fantasy Land algebras:

StateReaderIOEither

StateReaderIOEither[S, R, E, A] represents a computation that:

  • Manages state of type S (State)
  • Depends on some context/environment of type R (Reader)
  • Performs side effects (IO)
  • Can fail with an error of type E or succeed with a value of type A (Either)

The type is defined as:

StateReaderIOEither[S, R, E, A] = Reader[S, ReaderIOEither[R, E, Pair[S, A]]]

This is particularly useful for:

  • Stateful computations with dependency injection
  • Error handling in effectful computations with state
  • Composing operations that need access to shared configuration, manage state, and can fail

Core Operations

Construction:

  • Of/Right: Create a successful computation with a value
  • Left: Create a failed computation with an error
  • FromState: Lift a State into StateReaderIOEither
  • FromReader: Lift a Reader into StateReaderIOEither
  • FromIO: Lift an IO into StateReaderIOEither
  • FromEither: Lift an Either into StateReaderIOEither
  • FromIOEither: Lift an IOEither into StateReaderIOEither
  • FromReaderEither: Lift a ReaderEither into StateReaderIOEither
  • FromReaderIOEither: Lift a ReaderIOEither into StateReaderIOEither

Transformation:

  • Map: Transform the success value
  • Chain: Sequence dependent computations (monadic bind)
  • Flatten: Flatten nested StateReaderIOEither

Combination:

  • Ap: Apply a function in a context to a value in a context

Context Access:

  • Asks: Get a value derived from the context
  • Local: Run a computation with a modified context

Kleisli Arrows:

  • FromEitherK: Lift an Either-returning function to a Kleisli arrow
  • FromIOK: Lift an IO-returning function to a Kleisli arrow
  • FromIOEitherK: Lift an IOEither-returning function to a Kleisli arrow
  • FromReaderIOEitherK: Lift a ReaderIOEither-returning function to a Kleisli arrow
  • ChainEitherK: Chain with an Either-returning function
  • ChainIOEitherK: Chain with an IOEither-returning function
  • ChainReaderIOEitherK: Chain with a ReaderIOEither-returning function

Do Notation (Monadic Composition):

  • Do: Start a do-notation chain
  • Bind: Bind a value from a computation
  • BindTo: Bind a value to a simple constructor
  • Let: Compute a derived value
  • LetTo: Set a constant value
  • ApS: Apply in sequence (for applicative composition)
  • BindL/ApSL/LetL/LetToL: Lens-based variants for working with nested structures

Example Usage

type Config struct {
    DatabaseURL string
    MaxRetries  int
}

type AppState struct {
    RequestCount int
    LastError    error
}

// A computation that manages state, depends on config, performs IO, and can fail
func processRequest(data string) statereaderioeither.StateReaderIOEither[AppState, Config, error, string] {
    return func(state AppState) readerioeither.ReaderIOEither[Config, error, pair.Pair[AppState, string]] {
        return func(cfg Config) ioeither.IOEither[error, pair.Pair[AppState, string]] {
            return func() either.Either[error, pair.Pair[AppState, string]] {
                // Use cfg.DatabaseURL and cfg.MaxRetries
                // Update state.RequestCount
                // Perform IO operations
                // Return either.Right(pair.MakePair(newState, result)) or either.Left(err)
                newState := AppState{RequestCount: state.RequestCount + 1}
                return either.Right(pair.MakePair(newState, "processed: " + data))
            }
        }
    }
}

// Compose operations using do-notation
result := function.Pipe3(
    statereaderioeither.Do[AppState, Config, error](State{}),
    statereaderioeither.Bind(
        func(result string) func(State) State { return func(s State) State { return State{result: result} } },
        func(s State) statereaderioeither.StateReaderIOEither[AppState, Config, error, string] {
            return processRequest(s.input)
        },
    ),
    statereaderioeither.Map[AppState, Config, error](func(s State) string { return s.result }),
)

// Execute with initial state and config
initialState := AppState{RequestCount: 0}
config := Config{DatabaseURL: "postgres://localhost", MaxRetries: 3}
outcome := result(initialState)(config)() // Returns either.Either[error, pair.Pair[AppState, string]]

Monad Laws

StateReaderIOEither satisfies the monad laws:

  • Left Identity: Of(a) >>= f ≡ f(a)
  • Right Identity: m >>= Of ≡ m
  • Associativity: (m >>= f) >>= g ≡ m >>= (x => f(x) >>= g)

These laws are verified in the testing subpackage.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Applicative

func Applicative[
	S, R, E, A, B any,
]() applicative.Applicative[A, B, StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]]

Applicative implements the applicative.Applicative operations for StateReaderIOEither

func Eq

func Eq[
	S, R, E, A any](eqr eq.Eq[ReaderIOEither[R, E, Pair[S, A]]]) func(S) eq.Eq[StateReaderIOEither[S, R, E, A]]

Eq implements the equals predicate for values contained in the StateReaderIOEither monad

func FromStrictEquals

func FromStrictEquals[
	S comparable, R any, E, A comparable]() func(R) func(S) eq.Eq[StateReaderIOEither[S, R, E, A]]

FromStrictEquals constructs an eq.Eq from the canonical comparison function

func Functor

func Functor[
	S, R, E, A, B any,
]() functor.Functor[A, B, StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]]

Functor implements the functor.Functor operations for StateReaderIOEither

func Local

func Local[S, E, A, B, R1, R2 any](f func(R2) R1) func(StateReaderIOEither[S, R1, E, A]) StateReaderIOEither[S, R2, E, A]

Local runs a computation with a modified context. The function f transforms the context before passing it to the computation.

Example:

// Modify config before running computation
withTimeout := statereaderioeither.Local[AppState, error, int](
    func(cfg Config) Config { return Config{...cfg, Timeout: 60} }
)
result := withTimeout(computation)

func Monad

func Monad[
	S, R, E, A, B any,
]() monad.Monad[A, B, StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B], StateReaderIOEither[S, R, E, func(A) B]]

Monad implements the monad.Monad operations for StateReaderIOEither

func Pointed

func Pointed[
	S, R, E, A any,
]() pointed.Pointed[A, StateReaderIOEither[S, R, E, A]]

Pointed implements the pointed.Pointed operations for StateReaderIOEither

Types

type Either

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

Either represents a value that can be either a Left (error) or Right (success).

type Endomorphism

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

Endomorphism represents a function from A to A.

func ApSL

func ApSL[ST, R, E, S, T any](
	lens Lens[S, T],
	fa StateReaderIOEither[ST, R, E, T],
) Endomorphism[StateReaderIOEither[ST, R, E, S]]

ApSL is a lens-based variant of ApS for working with nested structures. It uses a lens to focus on a specific field in the state.

func BindL

func BindL[ST, R, E, S, T any](
	lens Lens[S, T],
	f Kleisli[ST, R, E, T, T],
) Endomorphism[StateReaderIOEither[ST, R, E, S]]

BindL is a lens-based variant of Bind for working with nested structures. It uses a lens to focus on a specific field in the state.

func LetL

func LetL[ST, R, E, S, T any](
	lens Lens[S, T],
	f Endomorphism[T],
) Endomorphism[StateReaderIOEither[ST, R, E, S]]

LetL is a lens-based variant of Let for working with nested structures. It uses a lens to focus on a specific field in the state.

func LetToL

func LetToL[ST, R, E, S, T any](
	lens Lens[S, T],
	b T,
) Endomorphism[StateReaderIOEither[ST, R, E, S]]

LetToL is a lens-based variant of LetTo for working with nested structures. It uses a lens to focus on a specific field in the state.

type IO

type IO[A any] = io.IO[A]

IO represents a computation that performs side effects and produces a value of type A.

type IOEither

type IOEither[E, A any] = ioeither.IOEither[E, A]

IOEither represents a computation that performs side effects and can fail with an error E or succeed with a value A.

type Kleisli

type Kleisli[S, R, E, A, B any] = Reader[A, StateReaderIOEither[S, R, E, B]]

Kleisli represents a Kleisli arrow - a function that takes a value A and returns a StateReaderIOEither computation producing B. This is used for monadic composition via Chain.

func FromEitherK

func FromEitherK[S, R, E, A, B any](f either.Kleisli[E, A, B]) Kleisli[S, R, E, A, B]

FromEitherK lifts an Either-returning function into a Kleisli arrow for StateReaderIOEither.

Example:

validate := func(x int) either.Either[error, int] {
    if x > 0 { return either.Right[error](x) }
    return either.Left[int](errors.New("negative"))
}
kleisli := statereaderioeither.FromEitherK[AppState, Config](validate)

func FromIOEitherK

func FromIOEitherK[
	S, R, E, A, B any,
](f ioeither.Kleisli[E, A, B]) Kleisli[S, R, E, A, B]

FromIOEitherK lifts an IOEither-returning function into a Kleisli arrow for StateReaderIOEither.

func FromIOK

func FromIOK[S, R, E, A, B any](f func(A) IO[B]) Kleisli[S, R, E, A, B]

FromIOK lifts an IO-returning function into a Kleisli arrow for StateReaderIOEither.

func FromReaderIOEitherK

func FromReaderIOEitherK[S, R, E, A, B any](f readerioeither.Kleisli[R, E, A, B]) Kleisli[S, R, E, A, B]

FromReaderIOEitherK lifts a ReaderIOEither-returning function into a Kleisli arrow for StateReaderIOEither.

func WithResource

func WithResource[A, S, R, E, RES, ANY any](
	onCreate StateReaderIOEither[S, R, E, RES],
	onRelease Kleisli[S, R, E, RES, ANY],
) Kleisli[S, R, E, Kleisli[S, R, E, RES, A], A]

WithResource constructs a function that creates a resource with state management, operates on it, and then releases the resource. This ensures proper resource cleanup even in the presence of errors, following the Resource Acquisition Is Initialization (RAII) pattern. The state is threaded through all operations: resource creation, usage, and release.

The resource lifecycle with state management is:

  1. onCreate: Acquires the resource (may modify state)
  2. use: Operates on the resource with current state (provided as argument to the returned function)
  3. onRelease: Releases the resource with current state (called regardless of success or failure)

Type parameters:

  • S: The state type that is threaded through all operations
  • R: The reader/context type
  • E: The error type
  • RES: The resource type
  • A: The type of the result produced by using the resource
  • ANY: The type returned by the release function (typically ignored)

Parameters:

  • onCreate: A stateful computation that acquires the resource
  • onRelease: A stateful function that releases the resource, called with the resource and current state, executed regardless of errors

Returns:

A function that takes a resource-using function and returns a StateReaderIOEither that manages the resource lifecycle with state

Example:

type AppState struct {
    openFiles int
}

withFile := WithResource(
    openFile("data.txt"), // Increments openFiles in state
    func(f *File) StateReaderIOEither[AppState, Config, error, int] {
        return closeFile(f) // Decrements openFiles in state
    },
)
result := withFile(func(f *File) StateReaderIOEither[AppState, Config, error, string] {
    return readContent(f)
})

type Lens

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

Lens is an optic that focuses on a field of type A within a structure of type S.

type Operator

type Operator[S, R, E, A, B any] = Reader[StateReaderIOEither[S, R, E, A], StateReaderIOEither[S, R, E, B]]

Operator represents a function that transforms one StateReaderIOEither into another. This is commonly used for building composable operations via Map, Chain, etc.

func Ap

func Ap[B, S, R, E, A any](fa StateReaderIOEither[S, R, E, A]) Operator[S, R, E, func(A) B, B]

Ap is the curried version of MonadAp. Returns a function that applies a wrapped function to the given wrapped value.

func ApS

func ApS[ST, R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa StateReaderIOEither[ST, R, E, T],
) Operator[ST, R, E, S1, S2]

ApS applies a computation in sequence and binds the result to a field. This is the applicative version of Bind.

func Bind

func Bind[ST, R, E, S1, S2, T any](
	setter func(T) func(S1) S2,
	f Kleisli[ST, R, E, S1, T],
) Operator[ST, R, E, S1, S2]

Bind executes a computation and binds its result to a field in the accumulator state. This is used in do-notation to sequence dependent computations.

Example:

result := function.Pipe2(
    statereaderioeither.Do[AppState, Config, error](State{}),
    statereaderioeither.Bind(
        func(name string) func(State) State {
            return func(s State) State { return State{name: name, age: s.age} }
        },
        func(s State) statereaderioeither.StateReaderIOEither[AppState, Config, error, string] {
            return statereaderioeither.Of[AppState, Config, error]("John")
        },
    ),
)

func BindTo

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

BindTo wraps a value in a simple constructor, typically used to start a do-notation chain after getting an initial value.

Example:

result := function.Pipe2(
    statereaderioeither.Of[AppState, Config, error](42),
    statereaderioeither.BindTo[AppState, Config, error](func(x int) State { return State{value: x} }),
)

func Chain

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

Chain is the curried version of MonadChain. Returns a function that sequences computations.

Example:

stringify := statereaderioeither.Chain(func(x int) statereaderioeither.StateReaderIOEither[AppState, Config, error, string] {
    return statereaderioeither.Of[AppState, Config, error](fmt.Sprintf("%d", x))
})
result := function.Pipe1(statereaderioeither.Of[AppState, Config, error](42), stringify)

func ChainEitherK

func ChainEitherK[S, R, E, A, B any](f either.Kleisli[E, A, B]) Operator[S, R, E, A, B]

ChainEitherK is the curried version of MonadChainEitherK.

func ChainIOEitherK

func ChainIOEitherK[S, R, E, A, B any](f ioeither.Kleisli[E, A, B]) Operator[S, R, E, A, B]

ChainIOEitherK is the curried version of MonadChainIOEitherK.

func ChainReaderIOEitherK

func ChainReaderIOEitherK[S, R, E, A, B any](f readerioeither.Kleisli[R, E, A, B]) Operator[S, R, E, A, B]

ChainReaderIOEitherK is the curried version of MonadChainReaderIOEitherK.

func Let

func Let[ST, R, E, S1, S2, T any](
	key func(T) func(S1) S2,
	f func(S1) T,
) Operator[ST, R, E, S1, S2]

Let computes a derived value and binds it to a field in the accumulator state. Unlike Bind, this does not execute a monadic computation, just a pure function.

Example:

result := function.Pipe2(
    statereaderioeither.Do[AppState, Config, error](State{age: 25}),
    statereaderioeither.Let(
        func(isAdult bool) func(State) State {
            return func(s State) State { return State{age: s.age, isAdult: isAdult} }
        },
        func(s State) bool { return s.age >= 18 },
    ),
)

func LetTo

func LetTo[ST, R, E, S1, S2, T any](
	key func(T) func(S1) S2,
	b T,
) Operator[ST, R, E, S1, S2]

LetTo binds a constant value to a field in the accumulator state.

Example:

result := function.Pipe2(
    statereaderioeither.Do[AppState, Config, error](State{}),
    statereaderioeither.LetTo(
        func(status string) func(State) State {
            return func(s State) State { return State{...s, status: status} }
        },
        "active",
    ),
)

func Map

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

Map is the curried version of MonadMap. Returns a function that transforms a StateReaderIOEither.

Example:

double := statereaderioeither.Map[AppState, Config, error](N.Mul(2))
result := function.Pipe1(statereaderioeither.Of[AppState, Config, error](21), double)

type Pair

type Pair[L, R any] = pair.Pair[L, R]

Pair represents a tuple of two values.

type Reader

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

Reader represents a computation that depends on an environment/context of type R and produces a value of type A.

type ReaderEither

type ReaderEither[R, E, A any] = readereither.ReaderEither[R, E, A]

ReaderEither represents a computation that depends on an environment R and can fail with an error E or succeed with a value A (without side effects).

type ReaderIOEither

type ReaderIOEither[R, E, A any] = readerioeither.ReaderIOEither[R, E, A]

ReaderIOEither represents a computation that depends on an environment R, performs side effects, and can fail with an error E or succeed with a value A.

type State

type State[S, A any] = state.State[S, A]

State represents a stateful computation that takes an initial state S and returns a pair of the new state S and a value A.

type StateReaderIOEither

type StateReaderIOEither[S, R, E, A any] = Reader[S, ReaderIOEither[R, E, Pair[S, A]]]

StateReaderIOEither represents a stateful computation that:

  • Takes an initial state S
  • Depends on an environment/context R
  • Performs side effects (IO)
  • Can fail with an error E or succeed with a value A
  • Returns a pair of the new state S and the result

This is the main type of this package, combining State, Reader, IO, and Either monads.

func Asks

func Asks[
	S, R, E, A any,
](f func(R) StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, A]

Asks creates a computation that derives a value from the context. The function receives the context and returns a StateReaderIOEither.

Example:

getTimeout := statereaderioeither.Asks[AppState, Config, error, int](
    func(cfg Config) statereaderioeither.StateReaderIOEither[AppState, Config, error, int] {
        return statereaderioeither.Of[AppState, Config, error](cfg.Timeout)
    },
)

func Do

func Do[ST, R, E, A any](
	empty A,
) StateReaderIOEither[ST, R, E, A]

Do starts a do-notation chain for building computations in a fluent style. This is typically used with Bind, Let, and other combinators to compose stateful, context-dependent computations that can fail.

Example:

type State struct {
    name string
    age  int
}
result := function.Pipe2(
    statereaderioeither.Do[AppState, Config, error](State{}),
    statereaderioeither.Bind(...),
    statereaderioeither.Let(...),
)

func FromEither

func FromEither[S, R, E, A any](ma Either[E, A]) StateReaderIOEither[S, R, E, A]

FromEither lifts an Either into a StateReaderIOEither. The state is passed through unchanged and the context is ignored.

Example:

result := statereaderioeither.FromEither[AppState, Config](either.Right[error](42))

func FromIO

func FromIO[S, R, E, A any](fa IO[A]) StateReaderIOEither[S, R, E, A]

FromIO lifts an IO computation into a StateReaderIOEither. The state is passed through unchanged and the context is ignored.

func FromIOEither

func FromIOEither[S, R, E, A any](fa IOEither[E, A]) StateReaderIOEither[S, R, E, A]

FromIOEither lifts an IOEither into a StateReaderIOEither. The state is passed through unchanged and the context is ignored.

func FromReader

func FromReader[S, E, R, A any](fa Reader[R, A]) StateReaderIOEither[S, R, E, A]

FromReader lifts a Reader into a StateReaderIOEither. The state is passed through unchanged.

func FromReaderEither

func FromReaderEither[S, R, E, A any](fa ReaderEither[R, E, A]) StateReaderIOEither[S, R, E, A]

FromReaderEither lifts a ReaderEither into a StateReaderIOEither. The state is passed through unchanged.

func FromReaderIOEither

func FromReaderIOEither[S, R, E, A any](fa ReaderIOEither[R, E, A]) StateReaderIOEither[S, R, E, A]

FromReaderIOEither lifts a ReaderIOEither into a StateReaderIOEither. The state is passed through unchanged.

Example:

rioe := readerioeither.Of[Config, error](42)
result := statereaderioeither.FromReaderIOEither[AppState](rioe)

func FromState

func FromState[R, E, S, A any](sa State[S, A]) StateReaderIOEither[S, R, E, A]

FromState lifts a State computation into a StateReaderIOEither. The computation cannot fail (uses the error type parameter).

func Left

func Left[S, R, A, E any](e E) StateReaderIOEither[S, R, E, A]

Left creates a StateReaderIOEither that represents a failed computation with the given error. The error value is immediately available and does not depend on state or context.

Example:

result := statereaderioeither.Left[AppState, Config, string](errors.New("validation failed"))
// Returns a failed computation that ignores state and context

func MonadAp

func MonadAp[B, S, R, E, A any](fab StateReaderIOEither[S, R, E, func(A) B], fa StateReaderIOEither[S, R, E, A]) StateReaderIOEither[S, R, E, B]

MonadAp applies a function wrapped in a StateReaderIOEither to a value wrapped in a StateReaderIOEither. If either the function or the value fails, the error is propagated. The state is threaded through both computations sequentially. This is the applicative apply operation.

Example:

fab := statereaderioeither.Of[AppState, Config, error](N.Mul(2))
fa := statereaderioeither.Of[AppState, Config, error](21)
result := statereaderioeither.MonadAp(fab, fa) // Result contains 42

func MonadChain

func MonadChain[S, R, E, A, B any](fa StateReaderIOEither[S, R, E, A], f Kleisli[S, R, E, A, B]) StateReaderIOEither[S, R, E, B]

MonadChain sequences two computations, passing the result of the first to a function that produces the second computation. This is the monadic bind operation. The state is threaded through both computations.

Example:

result := statereaderioeither.MonadChain(
    statereaderioeither.Of[AppState, Config, error](5),
    func(x int) statereaderioeither.StateReaderIOEither[AppState, Config, error, string] {
        return statereaderioeither.Of[AppState, Config, error](fmt.Sprintf("value: %d", x))
    },
)

func MonadChainEitherK

func MonadChainEitherK[S, R, E, A, B any](ma StateReaderIOEither[S, R, E, A], f either.Kleisli[E, A, B]) StateReaderIOEither[S, R, E, B]

MonadChainEitherK chains a StateReaderIOEither with an Either-returning function.

func MonadChainIOEitherK

func MonadChainIOEitherK[S, R, E, A, B any](ma StateReaderIOEither[S, R, E, A], f ioeither.Kleisli[E, A, B]) StateReaderIOEither[S, R, E, B]

MonadChainIOEitherK chains a StateReaderIOEither with an IOEither-returning function.

func MonadChainReaderIOEitherK

func MonadChainReaderIOEitherK[S, R, E, A, B any](ma StateReaderIOEither[S, R, E, A], f readerioeither.Kleisli[R, E, A, B]) StateReaderIOEither[S, R, E, B]

MonadChainReaderIOEitherK chains a StateReaderIOEither with a ReaderIOEither-returning function.

func MonadMap

func MonadMap[S, R, E, A, B any](fa StateReaderIOEither[S, R, E, A], f func(A) B) StateReaderIOEither[S, R, E, B]

MonadMap transforms the success value of a StateReaderIOEither using the provided function. If the computation fails, the error is propagated unchanged. The state is threaded through the computation. This is the functor map operation.

Example:

result := statereaderioeither.MonadMap(
    statereaderioeither.Of[AppState, Config, error](21),
    N.Mul(2),
) // Result contains 42

func Of

func Of[S, R, E, A any](a A) StateReaderIOEither[S, R, E, A]

Of creates a StateReaderIOEither that represents a successful computation with the given value. This is the monadic return/pure operation for StateReaderIOEither. Equivalent to Right.

Example:

result := statereaderioeither.Of[AppState, Config, error](42)
// Returns a successful computation containing 42
func Right[S, R, E, A any](a A) StateReaderIOEither[S, R, E, A]

Right creates a StateReaderIOEither that represents a successful computation with the given value. The value is wrapped and the state is passed through unchanged.

Example:

result := statereaderioeither.Right[AppState, Config, error](42)
// Returns a successful computation containing 42

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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