readeroption

package
v2.0.0 Latest Latest
Warning

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

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

Documentation

Overview

Package readeroption provides a monad transformer that combines the Reader and Option monads.

Fantasy Land Specification

This is a monad transformer combining:

Implemented Fantasy Land algebras:

ReaderOption[R, A] represents a computation that:

  • Depends on a shared environment of type R (Reader monad)
  • May fail to produce a value of type A (Option monad)

This is useful for computations that need access to configuration, context, or dependencies while also being able to represent the absence of a value without using errors.

The ReaderOption monad is defined as: Reader[R, Option[A]]

Key operations:

  • Of: Wraps a value in a ReaderOption
  • None: Creates a ReaderOption representing no value
  • Map: Transforms the value inside a ReaderOption
  • Chain: Sequences ReaderOption computations
  • Ask/Asks: Accesses the environment

Example:

type Config struct {
    DatabaseURL string
    Timeout     int
}

// A computation that may or may not find a user
func findUser(id int) readeroption.ReaderOption[Config, User] {
    return readeroption.Asks(func(cfg Config) option.Option[User] {
        // Use cfg.DatabaseURL to query database
        // Return Some(user) if found, None() if not found
    })
}

// Chain multiple operations
result := F.Pipe2(
    findUser(123),
    readeroption.Chain(func(user User) readeroption.ReaderOption[Config, Profile] {
        return loadProfile(user.ProfileID)
    }),
    readeroption.Map(func(profile Profile) string {
        return profile.DisplayName
    }),
)

// Execute with config
config := Config{DatabaseURL: "localhost:5432", Timeout: 30}
displayName := result(config) // Returns Option[string]

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Curry2

func Curry2[R, T1, T2, A any](f func(R, T1, T2) (A, bool)) func(T1) func(T2) ReaderOption[R, A]

Curry2 converts a function that takes a context and two arguments, returning (A, bool), into a curried function that returns a ReaderOption.

Example:

query := func(ctx context.Context, table string, id int) (Record, bool) {
    // Query database using context
    return record, found
}
ro := readeroption.Curry2(query)
result := ro("users")(123)(ctx) // Returns option.Some(record) or option.None()

func Curry3

func Curry3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, bool)) func(T1) func(T2) func(T3) ReaderOption[R, A]

Curry3 converts a function that takes a context and three arguments, returning (A, bool), into a curried function that returns a ReaderOption.

Example:

complexQuery := func(ctx context.Context, db string, table string, id int) (Record, bool) {
    // Query database using context
    return record, found
}
ro := readeroption.Curry3(complexQuery)
result := ro("mydb")("users")(123)(ctx)

func Fold

func Fold[E, A, B any](onNone Reader[E, B], onRight reader.Kleisli[E, A, B]) reader.Operator[E, Option[A], B]

Fold extracts the value from a ReaderOption by providing handlers for both cases. The onNone handler is called if the computation returns None. The onRight handler is called if the computation returns Some(a).

Example:

result := readeroption.Fold(
    func() reader.Reader[Config, string] { return reader.Of[Config]("not found") },
    func(user User) reader.Reader[Config, string] { return reader.Of[Config](user.Name) },
)(findUser(123))

func From0

func From0[R, A any](f func(R) (A, bool)) func() ReaderOption[R, A]

From0 converts a function that takes only a context and returns (A, bool) into a function that returns a ReaderOption[R, A].

Example:

getConfig := func(ctx context.Context) (Config, bool) {
    cfg, ok := ctx.Value("config").(Config)
    return cfg, ok
}
roFunc := readeroption.From0(getConfig)
ro := roFunc() // Returns a ReaderOption[context.Context, Config]
result := ro(ctx) // Returns option.Some(config) or option.None()

func From2

func From2[R, T1, T2, A any](f func(R, T1, T2) (A, bool)) func(T1, T2) ReaderOption[R, A]

From2 converts a function that takes a context and two arguments, returning (A, bool), into a function that takes two arguments (uncurried) and returns a ReaderOption.

Example:

query := func(ctx context.Context, table string, id int) (Record, bool) {
    // Query database using context
    return record, found
}
roFunc := readeroption.From2(query)
ro := roFunc("users", 123) // Returns a ReaderOption[context.Context, Record]
result := ro(ctx) // Returns option.Some(record) or option.None()

func From3

func From3[R, T1, T2, T3, A any](f func(R, T1, T2, T3) (A, bool)) func(T1, T2, T3) ReaderOption[R, A]

From3 converts a function that takes a context and three arguments, returning (A, bool), into a function that takes three arguments (uncurried) and returns a ReaderOption.

Example:

complexQuery := func(ctx context.Context, db string, table string, id int) (Record, bool) {
    // Query database using context
    return record, found
}
roFunc := readeroption.From3(complexQuery)
ro := roFunc("mydb", "users", 123) // Returns a ReaderOption[context.Context, Record]
result := ro(ctx) // Returns option.Some(record) or option.None()

func GetOrElse

func GetOrElse[E, A any](onNone Reader[E, A]) reader.Operator[E, Option[A], A]

GetOrElse returns the value from a ReaderOption, or a default value if it's None.

Example:

result := readeroption.GetOrElse(
    func() reader.Reader[Config, User] { return reader.Of[Config](defaultUser) },
)(findUser(123))

func Local

func Local[A, R2, R1 any](f func(R2) R1) func(ReaderOption[R1, A]) ReaderOption[R2, A]

Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s `contramap`).

This allows you to transform the environment before passing it to a computation.

Example:

type GlobalConfig struct { DB DBConfig }
type DBConfig struct { Host string }

// A computation that needs DBConfig
query := func(cfg DBConfig) option.Option[User] { ... }

// Transform GlobalConfig to DBConfig
result := readeroption.Local(func(g GlobalConfig) DBConfig { return g.DB })(
    readeroption.Asks(query),
)

func Read

func Read[A, E any](e E) func(ReaderOption[E, A]) Option[A]

Read applies a context to a reader to obtain its value. This executes the ReaderOption computation with the given environment.

Example:

ro := readeroption.Of[Config](42)
result := readeroption.Read[int](myConfig)(ro) // Returns option.Some(42)

func Sequence

func Sequence[R1, R2, A any](ma ReaderOption[R2, ReaderOption[R1, A]]) reader.Kleisli[R2, R1, Option[A]]

Sequence swaps the order of nested environment parameters in a ReaderOption computation.

This function takes a ReaderOption that produces another ReaderOption 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 Option[A].

Type Parameters:

  • R1: The first environment type (becomes inner after flip)
  • R2: The second environment type (becomes outer after flip)
  • A: The value type

Parameters:

  • ma: A ReaderOption that takes R2 and may produce a ReaderOption[R1, A]

Returns:

  • A reader.Kleisli[R2, R1, Option[A]], which is func(R2) func(R1) Option[A]

The function preserves optional values at both levels. If either the outer or inner computation produces None, the final result will be None.

Example:

import S "github.com/IBM/fp-go/v2/string"

type Database struct {
    ConnectionString string
}
type Config struct {
    Timeout int
}

// Original: takes Config, may produce ReaderOption[Database, string]
original := func(cfg Config) option.Option[ReaderOption[Database, string]] {
    if cfg.Timeout <= 0 {
        return option.None[ReaderOption[Database, string]]()
    }
    return option.Some(func(db Database) option.Option[string] {
        if S.IsEmpty(db.ConnectionString) {
            return option.None[string]()
        }
        return option.Some(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 result
result := configReader(cfg)
// result is Option[string]

func SequenceReader

func SequenceReader[R1, R2, A any](ma ReaderOption[R2, Reader[R1, A]]) reader.Kleisli[R2, R1, Option[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 optional values) rather than another ReaderOption. It takes a ReaderOption that produces a Reader and returns a Reader that produces a ReaderOption.

Type Parameters:

  • R1: The first environment type (becomes outer after flip)
  • R2: The second environment type (becomes inner after flip)
  • A: The value type

Parameters:

  • ma: A ReaderOption that takes R2 and may produce a Reader[R1, A]

Returns:

  • A reader.Kleisli[R2, R1, Option[A]], which is func(R2) func(R1) Option[A]

The function preserves the optional nature from the outer ReaderOption layer. If the outer computation produces None, the inner ReaderOption result will be None.

Example:

type Database struct {
    ConnectionString string
}
type Config struct {
    Timeout int
}

// Original: takes Config, may produce Reader[Database, string]
original := func(cfg Config) option.Option[Reader[Database, string]] {
    if cfg.Timeout <= 0 {
        return option.None[Reader[Database, string]]()
    }
    return option.Some(func(db Database) string {
        return fmt.Sprintf("Query on %s with timeout %d",
            db.ConnectionString, cfg.Timeout)
    })
}

// Sequenced: takes Database first, then Config
sequenced := SequenceReader(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 result
result := configReader(cfg)
// result is Option[string]

func Traverse

func Traverse[R2, R1, A, B any](
	f Kleisli[R1, A, B],
) func(ReaderOption[R2, A]) Kleisli[R2, R1, B]

func TraverseArrayWithIndex

func TraverseArrayWithIndex[E, A, B any](f func(int, A) ReaderOption[E, B]) func([]A) ReaderOption[E, []B]

TraverseArrayWithIndex is like TraverseArray but the function also receives the index of each element.

Example:

type DB struct { ... }

processWithIndex := func(idx int, value string) readeroption.ReaderOption[DB, Result] {
    // Use idx in processing
    return readeroption.Asks(func(db DB) option.Option[Result] { ... })
}

values := []string{"a", "b", "c"}
result := readeroption.TraverseArrayWithIndex[DB](processWithIndex)(values)

func TraverseReader

func TraverseReader[R2, R1, A, B any](
	f reader.Kleisli[R1, A, B],
) func(ReaderOption[R2, A]) Kleisli[R2, R1, B]

func Uncurry1

func Uncurry1[R, T1, A any](f func(T1) ReaderOption[R, A]) func(R, T1) (A, bool)

Uncurry1 converts a curried ReaderOption function back to a Go function that takes a context and one argument, returning (A, bool).

Example:

ro := func(id int) readeroption.ReaderOption[context.Context, User] { ... }
findUser := readeroption.Uncurry1(ro)
user, found := findUser(ctx, 123)

func Uncurry2

func Uncurry2[R, T1, T2, A any](f func(T1) func(T2) ReaderOption[R, A]) func(R, T1, T2) (A, bool)

Uncurry2 converts a curried ReaderOption function back to a Go function that takes a context and two arguments, returning (A, bool).

func Uncurry3

func Uncurry3[R, T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderOption[R, A]) func(R, T1, T2, T3) (A, bool)

Uncurry3 converts a curried ReaderOption function back to a Go function that takes a context and three arguments, returning (A, bool).

Types

type Either

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

type Kleisli

type Kleisli[R, A, B any] = Reader[A, ReaderOption[R, B]]

Kleisli represents a function that takes a value A and returns a ReaderOption[R, B]. This is the type of functions used with Chain/Bind operations.

func Curry1

func Curry1[R, T1, A any](f func(R, T1) (A, bool)) Kleisli[R, T1, A]

Curry1 converts a function that takes a context and one argument, returning (A, bool), into a curried function that returns a ReaderOption.

Example:

findUser := func(ctx context.Context, id int) (User, bool) {
    // Query database using context
    return user, found
}
ro := readeroption.Curry1(findUser)
result := ro(123)(ctx) // Returns option.Some(user) or option.None()

func From1

func From1[R, T1, A any](f func(R, T1) (A, bool)) Kleisli[R, T1, A]

From1 converts a function that takes a context and one argument, returning (A, bool), into a function that takes one argument and returns a ReaderOption.

This is equivalent to Curry1 but provided for consistency with the From naming convention.

Example:

findUser := func(ctx context.Context, id int) (User, bool) {
    // Query database using context
    return user, found
}
roFunc := readeroption.From1(findUser)
ro := roFunc(123) // Returns a ReaderOption[context.Context, User]
result := ro(ctx) // Returns option.Some(user) or option.None()

func FromPredicate

func FromPredicate[E, A any](pred Predicate[A]) Kleisli[E, A, A]

FromPredicate creates a Kleisli arrow that filters a value based on a predicate. If the predicate returns true, the value is wrapped in Some; otherwise, None is returned.

Example:

isPositive := readeroption.FromPredicate[Config](func(x int) bool { return x > 0 })
result := F.Pipe1(
    readeroption.Of[Config](42),
    readeroption.Chain(isPositive),
)

func TailRec

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

func TraverseArray

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

TraverseArray transforms an array by applying a function that returns a ReaderOption to each element. If any element results in None, the entire result is None. Otherwise, returns Some containing an array of all the unwrapped values.

This is useful for performing a sequence of operations that may fail on each element of an array, where you want all operations to succeed or the entire computation to fail.

Example:

type DB struct { ... }

findUser := func(id int) readeroption.ReaderOption[DB, User] { ... }

userIDs := []int{1, 2, 3}
result := F.Pipe1(
    readeroption.Of[DB](userIDs),
    readeroption.Chain(readeroption.TraverseArray[DB](findUser)),
)
// result will be Some([]User) if all users are found, None otherwise

type Lazy

type Lazy[A any] = lazy.Lazy[A]

Lazy represents a deferred computation

type Operator

type Operator[R, A, B any] = Reader[ReaderOption[R, A], ReaderOption[R, B]]

Operator represents a function that transforms one ReaderOption into another. This is commonly used for lifting functions into the ReaderOption context.

func Alt

func Alt[E, A any](that ReaderOption[E, A]) Operator[E, A, A]

Alt returns a function that provides an alternative ReaderOption if the first one returns None. This is the curried version of MonadAlt, useful for composition with F.Pipe.

Example:

result := F.Pipe1(
    findUserInCache(123),
    readeroption.Alt(findUserInDB(123)),
)

func Ap

func Ap[B, E, A any](fa ReaderOption[E, A]) Operator[E, func(A) B, B]

Ap returns a function that applies a function wrapped in a ReaderOption to a value. This is the curried version of MonadAp.

func ApS

func ApS[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa ReaderOption[R, T],
) Operator[R, S1, S2]

ApS attaches a value to a context [S1] to produce a context [S2] by considering the context and the value concurrently (using Applicative rather than Monad). This allows independent computations to be combined without one depending on the result of the other.

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

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

// These operations are independent and can be combined with ApS
getUser := readereither.Asks(func(env Env) either.Either[error, User] {
    return env.UserService.GetUser()
})
getConfig := readereither.Asks(func(env Env) either.Either[error, Config] {
    return env.ConfigService.GetConfig()
})

result := F.Pipe2(
    readereither.Do[Env, error](State{}),
    readereither.ApS(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        getUser,
    ),
    readereither.ApS(
        func(cfg Config) func(State) State {
            return func(s State) State { s.Config = cfg; return s }
        },
        getConfig,
    ),
)

func ApSL

func ApSL[R, S, T any](
	lens L.Lens[S, T],
	fa ReaderOption[R, T],
) Operator[R, S, S]

ApSL attaches a value to a context using a lens-based setter. This is a convenience function that combines ApS with a lens, allowing you to use optics to update nested structures in a more composable way.

The lens parameter provides both the getter and setter for a field within the structure S. This eliminates the need to manually write setter functions.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

configLens := lens.MakeLens(
    func(s State) Config { return s.Config },
    func(s State, c Config) State { s.Config = c; return s },
)

getConfig := readereither.Asks(func(env Env) either.Either[error, Config] {
    return env.ConfigService.GetConfig()
})
result := F.Pipe2(
    readereither.Of[Env, error](State{}),
    readereither.ApSL(configLens, getConfig),
)

func Bind

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

Bind attaches the result of a computation to a context [S1] to produce a context [S2]. This enables sequential composition where each step can depend on the results of previous steps and access the shared environment.

The setter function takes the result of the computation and returns a function that updates the context from S1 to S2.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

result := F.Pipe2(
    readereither.Do[Env, error](State{}),
    readereither.Bind(
        func(user User) func(State) State {
            return func(s State) State { s.User = user; return s }
        },
        func(s State) readereither.ReaderOption[Env, error, User] {
            return readereither.Asks(func(env Env) either.Either[error, User] {
                return env.UserService.GetUser()
            })
        },
    ),
    readereither.Bind(
        func(cfg Config) func(State) State {
            return func(s State) State { s.Config = cfg; return s }
        },
        func(s State) readereither.ReaderOption[Env, error, Config] {
            // This can access s.User from the previous step
            return readereither.Asks(func(env Env) either.Either[error, Config] {
                return env.ConfigService.GetConfigForUser(s.User.ID)
            })
        },
    ),
)

func BindL

func BindL[R, S, T any](
	lens L.Lens[S, T],
	f Kleisli[R, T, T],
) Operator[R, S, 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 ReaderOption computation that produces an updated value.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}

userLens := lens.MakeLens(
    func(s State) User { return s.User },
    func(s State, u User) State { s.User = u; return s },
)

result := F.Pipe2(
    readereither.Do[Env, error](State{}),
    readereither.BindL(userLens, func(user User) readereither.ReaderOption[Env, error, User] {
        return readereither.Asks(func(env Env) either.Either[error, User] {
            return env.UserService.GetUser()
        })
    }),
)

func BindTo

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

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

func Chain

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

Chain returns a function that sequences ReaderOption computations. This is the curried version of MonadChain, useful for composition with F.Pipe.

Example:

result := F.Pipe1(
    findUser(123),
    readeroption.Chain(loadProfile),
)

func ChainOptionK

func ChainOptionK[E, A, B any](f O.Kleisli[A, B]) Operator[E, A, B]

ChainOptionK returns a function that chains a ReaderOption with a function returning an Option. This is the curried version of MonadChainOptionK.

Example:

parseAge := func(s string) option.Option[int] { ... }
result := F.Pipe1(
    readeroption.Of[Config]("25"),
    readeroption.ChainOptionK[Config](parseAge),
)

func Flap

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

Flap returns a function that applies a value to a function wrapped in a ReaderOption. This is the curried version of MonadFlap.

func Let

func Let[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	f func(S1) T,
) Operator[R, S1, S2]

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

func LetL

func LetL[R, S, T any](
	lens L.Lens[S, T],
	f func(T) T,
) Operator[R, S, S]

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 ReaderOption).

Example:

type State struct {
    User   User
    Config Config
}

configLens := lens.MakeLens(
    func(s State) Config { return s.Config },
    func(s State, c Config) State { s.Config = c; return s },
)

result := F.Pipe2(
    readereither.Do[any, error](State{Config: Config{Host: "localhost"}}),
    readereither.LetL(configLens, func(cfg Config) Config {
        cfg.Port = 8080
        return cfg
    }),
)

func LetTo

func LetTo[R, S1, S2, T any](
	setter func(T) func(S1) S2,
	b T,
) Operator[R, S1, S2]

LetTo attaches the a value to a context [S1] to produce a context [S2]

func LetToL

func LetToL[R, S, T any](
	lens L.Lens[S, T],
	b T,
) Operator[R, S, S]

LetToL is a variant of LetTo that uses a lens to focus on a specific part of the context. This provides a more ergonomic API when working with nested structures, eliminating the need to manually write setter functions.

The lens parameter provides both a getter and setter for a field of type T within the context S. The value b is set directly to the focused field.

Example:

type State struct {
    User   User
    Config Config
}

configLens := lens.MakeLens(
    func(s State) Config { return s.Config },
    func(s State, c Config) State { s.Config = c; return s },
)

newConfig := Config{Host: "localhost", Port: 8080}
result := F.Pipe2(
    readereither.Do[any, error](State{}),
    readereither.LetToL(configLens, newConfig),
)

func Map

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

Map returns a function that applies a transformation to the value inside a ReaderOption. This is the curried version of MonadMap, useful for composition with F.Pipe.

Example:

doubled := F.Pipe1(
    readeroption.Of[Config](42),
    readeroption.Map[Config](N.Mul(2)),
)

type Option

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

Option represents an optional value

type Predicate

type Predicate[A any] = predicate.Predicate[A]

type Reader

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

Reader represents a computation that depends on an environment R and produces a value A

func MonadFold

func MonadFold[E, A, B any](fa ReaderOption[E, A], onNone Reader[E, B], onRight reader.Kleisli[E, A, B]) Reader[E, B]

MonadFold extracts the value from a ReaderOption by providing handlers for both cases. This is the non-curried version of Fold. The onNone handler is called if the computation returns None. The onRight handler is called if the computation returns Some(a).

Example:

result := readeroption.MonadFold(
    findUser(123),
    reader.Of[Config]("not found"),
    func(user User) reader.Reader[Config, string] { return reader.Of[Config](user.Name) },
)

type ReaderOption

type ReaderOption[R, A any] = Reader[R, Option[A]]

ReaderOption represents a computation that depends on an environment R and may produce a value A. It combines the Reader monad (for dependency injection) with the Option monad (for optional values).

func Ask

func Ask[E any]() ReaderOption[E, E]

Ask retrieves the current environment as a ReaderOption. This always succeeds and returns Some(environment).

Example:

getConfig := readeroption.Ask[Config]()
result := getConfig(myConfig) // Returns option.Some(myConfig)

func Asks

func Asks[E, A any](r Reader[E, A]) ReaderOption[E, A]

Asks creates a ReaderOption that applies a function to the environment. This always succeeds and returns Some(f(environment)).

Example:

getTimeout := readeroption.Asks(func(cfg Config) int { return cfg.Timeout })
result := getTimeout(myConfig) // Returns option.Some(myConfig.Timeout)

func Curry0

func Curry0[R, A any](f func(R) (A, bool)) ReaderOption[R, A]

Curry0 converts a function that takes only a context and returns (A, bool) into a ReaderOption[R, A].

Example:

getConfig := func(ctx context.Context) (Config, bool) {
    cfg, ok := ctx.Value("config").(Config)
    return cfg, ok
}
ro := readeroption.Curry0(getConfig)
result := ro(ctx) // Returns option.Some(config) or option.None()

func Do

func Do[R, S any](
	empty S,
) ReaderOption[R, S]

Do creates an empty context of type [S] to be used with the Bind operation. This is the starting point for do-notation style composition.

Example:

type State struct {
    User   User
    Config Config
}
type Env struct {
    UserService   UserService
    ConfigService ConfigService
}
result := readereither.Do[Env, error](State{})

func Flatten

func Flatten[E, A any](mma ReaderOption[E, ReaderOption[E, A]]) ReaderOption[E, A]

Flatten removes one level of nesting from a ReaderOption. Converts ReaderOption[E, ReaderOption[E, A]] to ReaderOption[E, A].

Example:

nested := readeroption.Of[Config](readeroption.Of[Config](42))
flattened := readeroption.Flatten(nested)

func FromOption

func FromOption[E, A any](e Option[A]) ReaderOption[E, A]

FromOption lifts an Option[A] into a ReaderOption[E, A]. The resulting computation ignores the environment and returns the given option.

func FromReader

func FromReader[E, A any](r Reader[E, A]) ReaderOption[E, A]

FromReader lifts a Reader[E, A] into a ReaderOption[E, A]. The resulting computation always succeeds (returns Some).

func MonadAlt

func MonadAlt[E, A any](fa, that ReaderOption[E, A]) ReaderOption[E, A]

MonadAlt provides an alternative ReaderOption if the first one returns None. If fa returns Some(a), that value is returned; otherwise, the alternative computation is executed. This is useful for providing fallback behavior.

Example:

primary := findUserInCache(123)
fallback := findUserInDB(123)
result := readeroption.MonadAlt(primary, fallback)

func MonadAp

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

MonadAp applies a function wrapped in a ReaderOption to a value wrapped in a ReaderOption. Both computations are executed with the same environment. If either computation returns None, the result is None.

func MonadChain

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

MonadChain sequences two ReaderOption computations, where the second depends on the result of the first. If the first computation returns None, the second is not executed.

Example:

findUser := func(id int) readeroption.ReaderOption[DB, User] { ... }
loadProfile := func(user User) readeroption.ReaderOption[DB, Profile] { ... }
result := readeroption.MonadChain(findUser(123), loadProfile)

func MonadChainOptionK

func MonadChainOptionK[E, A, B any](ma ReaderOption[E, A], f O.Kleisli[A, B]) ReaderOption[E, B]

MonadChainOptionK chains a ReaderOption with a function that returns an Option. This is useful for integrating functions that return Option directly.

Example:

parseAge := func(s string) option.Option[int] { ... }
result := readeroption.MonadChainOptionK(
    readeroption.Of[Config]("25"),
    parseAge,
)

func MonadFlap

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

MonadFlap applies a value to a function wrapped in a ReaderOption. This is the reverse of MonadAp.

func MonadMap

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

MonadMap applies a function to the value inside a ReaderOption. If the ReaderOption contains None, the function is not applied.

Example:

ro := readeroption.Of[Config](42)
doubled := readeroption.MonadMap(ro, N.Mul(2))

func None

func None[E, A any]() ReaderOption[E, A]

None creates a ReaderOption representing a failed computation. The resulting computation ignores the environment and returns None.

Example:

ro := readeroption.None[Config, int]()
result := ro(config) // Returns option.None[int]()

func Of

func Of[E, A any](a A) ReaderOption[E, A]

Of wraps a value in a ReaderOption, representing a successful computation. The resulting computation ignores the environment and returns Some(a).

Example:

ro := readeroption.Of[Config](42)
result := ro(config) // Returns option.Some(42)

func SequenceArray

func SequenceArray[E, A any](ma []ReaderOption[E, A]) ReaderOption[E, []A]

SequenceArray converts an array of ReaderOption values into a ReaderOption of an array. If any element is None, the entire result is None. Otherwise, returns Some containing an array of all the unwrapped values.

This is useful when you have multiple independent ReaderOption computations and want to combine their results into a single array.

Example:

type Config struct { ... }

user1 := readeroption.Of[Config](User{ID: 1, Name: "Alice"})
user2 := readeroption.Of[Config](User{ID: 2, Name: "Bob"})
user3 := readeroption.None[Config, User]()

result := readeroption.SequenceArray([]readeroption.ReaderOption[Config, User]{
    user1, user2, user3,
})
// result(config) will be option.None[[]User]() because user3 is None

result2 := readeroption.SequenceArray([]readeroption.ReaderOption[Config, User]{
    user1, user2,
})
// result2(config) will be option.Some([]User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}})

func SequenceT1

func SequenceT1[E, A any](a ReaderOption[E, A]) ReaderOption[E, T.Tuple1[A]]

SequenceT1 converts a single ReaderOption into a ReaderOption of a 1-tuple. This is mainly useful for consistency with the other SequenceT functions.

Example:

type Config struct { ... }

user := readeroption.Of[Config](User{Name: "Alice"})
result := readeroption.SequenceT1(user)
// result(config) returns option.Some(tuple.MakeTuple1(User{Name: "Alice"}))

func SequenceT2

func SequenceT2[E, A, B any](
	a ReaderOption[E, A],
	b ReaderOption[E, B],
) ReaderOption[E, T.Tuple2[A, B]]

SequenceT2 combines two ReaderOption values into a ReaderOption of a 2-tuple. If either input is None, the result is None.

Example:

type Config struct { ... }

user := readeroption.Of[Config](User{Name: "Alice"})
count := readeroption.Of[Config](42)

result := readeroption.SequenceT2(user, count)
// result(config) returns option.Some(tuple.MakeTuple2(User{Name: "Alice"}, 42))

noneUser := readeroption.None[Config, User]()
result2 := readeroption.SequenceT2(noneUser, count)
// result2(config) returns option.None[tuple.Tuple2[User, int]]()

func SequenceT3

func SequenceT3[E, A, B, C any](
	a ReaderOption[E, A],
	b ReaderOption[E, B],
	c ReaderOption[E, C],
) ReaderOption[E, T.Tuple3[A, B, C]]

SequenceT3 combines three ReaderOption values into a ReaderOption of a 3-tuple. If any input is None, the result is None.

Example:

type Config struct { ... }

user := readeroption.Of[Config](User{Name: "Alice"})
count := readeroption.Of[Config](42)
active := readeroption.Of[Config](true)

result := readeroption.SequenceT3(user, count, active)
// result(config) returns option.Some(tuple.MakeTuple3(User{Name: "Alice"}, 42, true))

func SequenceT4

func SequenceT4[E, A, B, C, D any](
	a ReaderOption[E, A],
	b ReaderOption[E, B],
	c ReaderOption[E, C],
	d ReaderOption[E, D],
) ReaderOption[E, T.Tuple4[A, B, C, D]]

SequenceT4 combines four ReaderOption values into a ReaderOption of a 4-tuple. If any input is None, the result is None.

Example:

type Config struct { ... }

user := readeroption.Of[Config](User{Name: "Alice"})
count := readeroption.Of[Config](42)
active := readeroption.Of[Config](true)
score := readeroption.Of[Config](95.5)

result := readeroption.SequenceT4(user, count, active, score)
// result(config) returns option.Some(tuple.MakeTuple4(User{Name: "Alice"}, 42, true, 95.5))

func Some

func Some[E, A any](r A) ReaderOption[E, A]

Some wraps a value in a ReaderOption, representing a successful computation. This is equivalent to Of but more explicit about the Option semantics.

func SomeReader

func SomeReader[E, A any](r Reader[E, A]) ReaderOption[E, A]

SomeReader lifts a Reader[E, A] into a ReaderOption[E, A]. The resulting computation always succeeds (returns Some).

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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