Documentation
¶
Overview ¶
Package lazy provides a functional programming abstraction for synchronous computations without side effects. It represents deferred computations that are evaluated only when their result is needed.
Fantasy Land Specification ¶
This implementation corresponds to the Fantasy Land IO type (for pure computations): https://github.com/fantasyland/fantasy-land
Implemented Fantasy Land algebras:
- Functor: https://github.com/fantasyland/fantasy-land#functor
- Apply: https://github.com/fantasyland/fantasy-land#apply
- Applicative: https://github.com/fantasyland/fantasy-land#applicative
- Chain: https://github.com/fantasyland/fantasy-land#chain
- Monad: https://github.com/fantasyland/fantasy-land#monad
Overview ¶
A Lazy[A] is simply a function that takes no arguments and returns a value of type A:
type Lazy[A any] = func() A
This allows you to defer the evaluation of a computation until it's actually needed, which is useful for:
- Avoiding unnecessary computations
- Creating infinite data structures
- Implementing memoization
- Composing computations in a pure functional style
Core Concepts ¶
The lazy package implements several functional programming patterns:
**Functor**: Transform values inside a Lazy context using Map
**Applicative**: Combine multiple Lazy computations using Ap and ApS
**Monad**: Chain dependent computations using Chain and Bind
**Memoization**: Cache computation results using Memoize
Basic Usage ¶
Creating and evaluating lazy computations:
import (
"fmt"
"github.com/IBM/fp-go/v2/lazy"
F "github.com/IBM/fp-go/v2/function"
)
// Create a lazy computation
computation := lazy.Of(42)
// Transform it
doubled := F.Pipe1(
computation,
lazy.Map(N.Mul(2)),
)
// Evaluate when needed
result := doubled() // 84
Memoization ¶
Lazy computations can be memoized to ensure they're evaluated only once:
import "math/rand" // Without memoization - generates different values each time random := lazy.FromLazy(rand.Int) value1 := random() // e.g., 12345 value2 := random() // e.g., 67890 (different) // With memoization - caches the first result memoized := lazy.Memoize(rand.Int) value1 := memoized() // e.g., 12345 value2 := memoized() // 12345 (same as value1)
Chaining Computations ¶
Use Chain to compose dependent computations:
getUserId := lazy.Of(123)
getUser := F.Pipe1(
getUserId,
lazy.Chain(func(id int) lazy.Lazy[User] {
return lazy.Of(fetchUser(id))
}),
)
user := getUser()
Do-Notation Style ¶
The package supports do-notation style composition using Bind and ApS:
type Config struct {
Host string
Port int
}
result := F.Pipe2(
lazy.Do(Config{}),
lazy.Bind(
func(host string) func(Config) Config {
return func(c Config) Config { c.Host = host; return c }
},
func(c Config) lazy.Lazy[string] {
return lazy.Of("localhost")
},
),
lazy.Bind(
func(port int) func(Config) Config {
return func(c Config) Config { c.Port = port; return c }
},
func(c Config) lazy.Lazy[int] {
return lazy.Of(8080)
},
),
)
config := result() // Config{Host: "localhost", Port: 8080}
Traverse and Sequence ¶
Transform collections of values into lazy computations:
// Transform array elements
numbers := []int{1, 2, 3}
doubled := F.Pipe1(
numbers,
lazy.TraverseArray(func(x int) lazy.Lazy[int] {
return lazy.Of(x * 2)
}),
)
result := doubled() // []int{2, 4, 6}
// Sequence array of lazy computations
computations := []lazy.Lazy[int]{
lazy.Of(1),
lazy.Of(2),
lazy.Of(3),
}
result := lazy.SequenceArray(computations)() // []int{1, 2, 3}
Retry Logic ¶
The package includes retry functionality for computations that may fail:
import (
R "github.com/IBM/fp-go/v2/retry"
"time"
)
policy := R.CapDelay(
2*time.Second,
R.Monoid.Concat(
R.ExponentialBackoff(10),
R.LimitRetries(5),
),
)
action := func(status R.RetryStatus) lazy.Lazy[string] {
return lazy.Of(fetchData())
}
check := func(value string) bool {
return value == "" // retry if empty
}
result := lazy.Retrying(policy, action, check)()
Algebraic Structures ¶
The package provides algebraic structures for combining lazy computations:
**Semigroup**: Combine two lazy values using a semigroup operation
import M "github.com/IBM/fp-go/v2/monoid" intAddSemigroup := lazy.ApplySemigroup(M.MonoidSum[int]()) result := intAddSemigroup.Concat(lazy.Of(5), lazy.Of(10))() // 15
**Monoid**: Combine lazy values with an identity element
intAddMonoid := lazy.ApplicativeMonoid(M.MonoidSum[int]()) empty := intAddMonoid.Empty()() // 0 result := intAddMonoid.Concat(lazy.Of(5), lazy.Of(10))() // 15
Comparison ¶
Compare lazy computations by evaluating and comparing their results:
import EQ "github.com/IBM/fp-go/v2/eq" eq := lazy.Eq(EQ.FromEquals[int]()) result := eq.Equals(lazy.Of(42), lazy.Of(42)) // true
Key Functions ¶
**Creation**:
- Of: Create a lazy computation from a value
- FromLazy: Create a lazy computation from another lazy computation
- FromImpure: Convert a side effect into a lazy computation
- Defer: Create a lazy computation from a generator function
**Transformation**:
- Map: Transform the value inside a lazy computation
- MapTo: Replace the value with a constant
- Chain: Chain dependent computations
- ChainFirst: Chain computations but keep the first result
- Flatten: Flatten nested lazy computations
**Combination**:
- Ap: Apply a lazy function to a lazy value
- ApFirst: Combine two computations, keeping the first result
- ApSecond: Combine two computations, keeping the second result
**Memoization**:
- Memoize: Cache the result of a computation
**Do-Notation**:
- Do: Start a do-notation context
- Bind: Bind a computation result to a context
- Let: Attach a pure value to a context
- LetTo: Attach a constant to a context
- BindTo: Initialize a context from a value
- ApS: Attach a value using applicative style
**Lens-Based Operations**:
- BindL: Bind using a lens
- LetL: Let using a lens
- LetToL: LetTo using a lens
- ApSL: ApS using a lens
**Collections**:
- TraverseArray: Transform array elements into lazy computations
- SequenceArray: Convert array of lazy computations to lazy array
- TraverseRecord: Transform record values into lazy computations
- SequenceRecord: Convert record of lazy computations to lazy record
**Tuples**:
- SequenceT1, SequenceT2, SequenceT3, SequenceT4: Combine lazy computations into tuples
**Retry**:
- Retrying: Retry a computation according to a policy
**Algebraic**:
- ApplySemigroup: Create a semigroup for lazy values
- ApplicativeMonoid: Create a monoid for lazy values
- Eq: Create an equality predicate for lazy values
Relationship to IO ¶
The lazy package is built on top of the io package and shares the same underlying implementation. The key difference is conceptual:
- lazy.Lazy[A] represents a pure, synchronous computation without side effects
- io.IO[A] represents a computation that may have side effects
In practice, they are the same type, but the lazy package provides a more focused API for pure computations.
Index ¶
- func Ap[B, A any](ma Lazy[A]) func(Lazy[func(A) B]) Lazy[B]
- func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Lazy[A]]
- func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Lazy[A]]
- func Eq[A any](e EQ.Eq[A]) EQ.Eq[Lazy[A]]
- func Map[A, B any](f func(A) B) func(fa Lazy[A]) Lazy[B]
- type Kleisli
- func ApFirst[A, B any](second Lazy[B]) Kleisli[Lazy[A], A]
- func ApS[S1, S2, T any](setter func(T) func(S1) S2, fa Lazy[T]) Kleisli[Lazy[S1], S2]
- func ApSL[S, T any](lens L.Lens[S, T], fa Lazy[T]) Kleisli[Lazy[S], S]
- func ApSecond[A, B any](second Lazy[B]) Kleisli[Lazy[A], B]
- func Bind[S1, S2, T any](setter func(T) func(S1) S2, f Kleisli[S1, T]) Kleisli[Lazy[S1], S2]
- func BindL[S, T any](lens L.Lens[S, T], f Kleisli[T, T]) Kleisli[Lazy[S], S]
- func BindTo[S1, T any](setter func(T) S1) Kleisli[Lazy[T], S1]
- func Chain[A, B any](f Kleisli[A, B]) Kleisli[Lazy[A], B]
- func ChainFirst[A, B any](f Kleisli[A, B]) Kleisli[Lazy[A], A]
- func ChainTo[A, B any](fb Lazy[B]) Kleisli[Lazy[A], B]
- func Let[S1, S2, T any](setter func(T) func(S1) S2, f func(S1) T) Kleisli[Lazy[S1], S2]
- func LetL[S, T any](lens L.Lens[S, T], f func(T) T) Kleisli[Lazy[S], S]
- func LetTo[S1, S2, T any](setter func(T) func(S1) S2, b T) Kleisli[Lazy[S1], S2]
- func LetToL[S, T any](lens L.Lens[S, T], b T) Kleisli[Lazy[S], S]
- func MapTo[A, B any](b B) Kleisli[Lazy[A], B]
- func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B]
- func TraverseArrayWithIndex[A, B any](f func(int, A) Lazy[B]) Kleisli[[]A, []B]
- func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B]
- func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Lazy[B]) Kleisli[map[K]A, map[K]B]
- type Lazy
- func Defer[A any](gen func() Lazy[A]) Lazy[A]
- func Do[S any](empty S) Lazy[S]
- func Flatten[A any](mma Lazy[Lazy[A]]) Lazy[A]
- func FromImpure(f func()) Lazy[any]
- func FromLazy[A any](a Lazy[A]) Lazy[A]
- func Memoize[A any](ma Lazy[A]) Lazy[A]
- func MonadAp[B, A any](mab Lazy[func(A) B], ma Lazy[A]) Lazy[B]
- func MonadApFirst[A, B any](first Lazy[A], second Lazy[B]) Lazy[A]
- func MonadApSecond[A, B any](first Lazy[A], second Lazy[B]) Lazy[B]
- func MonadChain[A, B any](fa Lazy[A], f Kleisli[A, B]) Lazy[B]
- func MonadChainFirst[A, B any](fa Lazy[A], f Kleisli[A, B]) Lazy[A]
- func MonadChainTo[A, B any](fa Lazy[A], fb Lazy[B]) Lazy[B]
- func MonadMap[A, B any](fa Lazy[A], f func(A) B) Lazy[B]
- func MonadMapTo[A, B any](fa Lazy[A], b B) Lazy[B]
- func MonadOf[A any](a A) Lazy[A]
- func MonadTraverseArray[A, B any](tas []A, f Kleisli[A, B]) Lazy[[]B]
- func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f Kleisli[A, B]) Lazy[map[K]B]
- func Of[A any](a A) Lazy[A]
- func Retrying[A any](policy R.RetryPolicy, action Kleisli[R.RetryStatus, A], check func(A) bool) Lazy[A]
- func SequenceArray[A any](tas []Lazy[A]) Lazy[[]A]
- func SequenceRecord[K comparable, A any](tas map[K]Lazy[A]) Lazy[map[K]A]
- func SequenceT1[A any](a Lazy[A]) Lazy[tuple.Tuple1[A]]
- func SequenceT2[A, B any](a Lazy[A], b Lazy[B]) Lazy[tuple.Tuple2[A, B]]
- func SequenceT3[A, B, C any](a Lazy[A], b Lazy[B], c Lazy[C]) Lazy[tuple.Tuple3[A, B, C]]
- func SequenceT4[A, B, C, D any](a Lazy[A], b Lazy[B], c Lazy[C], d Lazy[D]) Lazy[tuple.Tuple4[A, B, C, D]]
- type Operator
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Ap ¶
Ap applies a lazy function to a lazy value. Returns a function that takes a lazy function and returns a lazy result.
This is the curried version of MonadAp, useful for function composition.
Example:
lazyValue := lazy.Of(5) applyTo5 := lazy.Ap[int](lazyValue) lazyFunc := lazy.Of(N.Mul(2)) result := applyTo5(lazyFunc)() // 10
func ApplicativeMonoid ¶
ApplicativeMonoid lifts a Monoid[A] to a Monoid[Lazy[A]]. This allows you to combine lazy computations using the monoid operation on their underlying values, with an identity element.
The resulting monoid's Concat operation will evaluate both lazy computations and combine their results using the original monoid's operation. The Empty operation returns a lazy computation that produces the monoid's identity element.
Parameters:
- m: A monoid for values of type A
Returns:
- A monoid for lazy computations of type A
Example:
import (
M "github.com/IBM/fp-go/v2/monoid"
"github.com/IBM/fp-go/v2/lazy"
)
// Create a monoid for lazy integers using addition
intAddMonoid := lazy.ApplicativeMonoid(M.MonoidSum[int]())
// Get the identity element (0 wrapped in lazy)
empty := intAddMonoid.Empty()() // 0
lazy1 := lazy.Of(5)
lazy2 := lazy.Of(10)
// Combine the lazy computations
result := intAddMonoid.Concat(lazy1, lazy2)() // 15
// Identity laws hold:
// Concat(Empty(), x) == x
// Concat(x, Empty()) == x
func ApplySemigroup ¶
ApplySemigroup lifts a Semigroup[A] to a Semigroup[Lazy[A]]. This allows you to combine lazy computations using the semigroup operation on their underlying values.
The resulting semigroup's Concat operation will evaluate both lazy computations and combine their results using the original semigroup's operation.
Parameters:
- s: A semigroup for values of type A
Returns:
- A semigroup for lazy computations of type A
Example:
import (
M "github.com/IBM/fp-go/v2/monoid"
"github.com/IBM/fp-go/v2/lazy"
)
// Create a semigroup for lazy integers using addition
intAddSemigroup := lazy.ApplySemigroup(M.MonoidSum[int]())
lazy1 := lazy.Of(5)
lazy2 := lazy.Of(10)
// Combine the lazy computations
result := intAddSemigroup.Concat(lazy1, lazy2)() // 15
func Map ¶
Map transforms the value inside a lazy computation using the provided function. Returns a function that can be applied to a lazy computation.
This is the curried version of MonadMap, useful for function composition.
Example:
double := lazy.Map(N.Mul(2)) computation := lazy.Of(5) result := double(computation)() // 10 // Or with pipe: result := F.Pipe1(lazy.Of(5), double)() // 10
Types ¶
type Kleisli ¶
Kleisli represents a function that takes a value of type A and returns a lazy computation producing a value of type B.
Kleisli arrows are used for composing monadic computations. They allow you to chain operations where each step depends on the result of the previous step.
Example:
// A Kleisli arrow that doubles a number lazily
double := func(x int) lazy.Lazy[int] {
return lazy.Of(x * 2)
}
// Chain it with another operation
result := lazy.Chain(double)(lazy.Of(5))() // 10
func ApS ¶
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 {
Config Config
Data Data
}
// These operations are independent and can be combined with ApS
getConfig := lazy.MakeLazy(func() Config { return loadConfig() })
getData := lazy.MakeLazy(func() Data { return loadData() })
result := F.Pipe2(
lazy.Do(State{}),
lazy.ApS(
func(cfg Config) func(State) State {
return func(s State) State { s.Config = cfg; return s }
},
getConfig,
),
lazy.ApS(
func(data Data) func(State) State {
return func(s State) State { s.Data = data; return s }
},
getData,
),
)
func ApSL ¶
ApSL is a variant of ApS 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. This allows you to work with nested fields without manually managing the update logic.
Example:
type Config struct {
Host string
Port int
}
type State struct {
Config Config
Data string
}
configLens := L.Prop[State, Config]("Config")
getConfig := lazy.MakeLazy(func() Config { return Config{Host: "localhost", Port: 8080} })
result := F.Pipe2(
lazy.Do(State{}),
lazy.ApSL(configLens, getConfig),
)
func Bind ¶
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.
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 {
Config Config
Data Data
}
result := F.Pipe2(
lazy.Do(State{}),
lazy.Bind(
func(cfg Config) func(State) State {
return func(s State) State { s.Config = cfg; return s }
},
func(s State) lazy.Lazy[Config] {
return lazy.MakeLazy(func() Config { return loadConfig() })
},
),
lazy.Bind(
func(data Data) func(State) State {
return func(s State) State { s.Data = data; return s }
},
func(s State) lazy.Lazy[Data] {
// This can access s.Config from the previous step
return lazy.MakeLazy(func() Data { return loadData(s.Config) })
},
),
)
func BindL ¶
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 new computation that produces an updated value.
Example:
type Config struct {
Host string
Port int
}
type State struct {
Config Config
Data string
}
configLens := L.Prop[State, Config]("Config")
result := F.Pipe2(
lazy.Do(State{Config: Config{Host: "localhost"}}),
lazy.BindL(configLens, func(cfg Config) lazy.Lazy[Config] {
return lazy.MakeLazy(func() Config {
cfg.Port = 8080
return cfg
})
}),
)
func Chain ¶
Chain composes computations in sequence, using the return value of one computation to determine the next computation.
func ChainFirst ¶
ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and keeping only the result of the first.
func ChainTo ¶
ChainTo composes computations in sequence, ignoring the return value of the first computation
func LetL ¶
LetL is a variant of Let that uses a lens to focus on a specific part of the context. This provides a more ergonomic API when working with nested structures, eliminating the need to manually write setter functions.
The lens parameter provides both a getter and setter for a field of type T within the context S. The function f receives the current value of the focused field and returns a new value (without wrapping in a monad).
Example:
type Config struct {
Host string
Port int
}
type State struct {
Config Config
Data string
}
configLens := L.Prop[State, Config]("Config")
result := F.Pipe2(
lazy.Do(State{Config: Config{Host: "localhost"}}),
lazy.LetL(configLens, func(cfg Config) Config {
cfg.Port = 8080
return cfg
}),
)
func LetToL ¶
LetToL is a variant of LetTo that uses a lens to focus on a specific part of the context. This provides a more ergonomic API when working with nested structures, eliminating the need to manually write setter functions.
The lens parameter provides both a getter and setter for a field of type T within the context S. The value b is set directly to the focused field.
Example:
type Config struct {
Host string
Port int
}
type State struct {
Config Config
Data string
}
configLens := L.Prop[State, Config]("Config")
newConfig := Config{Host: "localhost", Port: 8080}
result := F.Pipe2(
lazy.Do(State{}),
lazy.LetToL(configLens, newConfig),
)
func MapTo ¶
MapTo replaces the value inside a lazy computation with a constant value. Returns a function that can be applied to a lazy computation.
This is the curried version of MonadMapTo.
Example:
replaceWith42 := lazy.MapTo[string](42)
computation := lazy.Of("ignored")
result := replaceWith42(computation)() // 42
func TraverseArray ¶
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B]
TraverseArray applies a function returning an [IO] to all elements in an array and the transforms this into an [IO] of that array
func TraverseArrayWithIndex ¶
TraverseArrayWithIndex applies a function returning an [IO] to all elements in an array and the transforms this into an [IO] of that array
func TraverseRecord ¶
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B]
TraverseRecord applies a function returning an [IO] to all elements in a record and the transforms this into an [IO] of that record
func TraverseRecordWithIndex ¶
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Lazy[B]) Kleisli[map[K]A, map[K]B]
TraverseRecord applies a function returning an [IO] to all elements in a record and the transforms this into an [IO] of that record
type Lazy ¶
type Lazy[A any] = func() A
Lazy represents a synchronous computation without side effects. It is a function that takes no arguments and returns a value of type A.
Lazy computations are evaluated only when their result is needed (lazy evaluation). This allows for:
- Deferring expensive computations until they're actually required
- Creating infinite data structures
- Implementing memoization patterns
- Composing pure computations in a functional style
Example:
// Create a lazy computation computation := lazy.Of(42) // Transform it (not evaluated yet) doubled := lazy.Map(N.Mul(2))(computation) // Evaluate when needed result := doubled() // 84
Note: Lazy is an alias for io.IO[A] but represents pure computations without side effects, whereas IO represents computations that may have side effects.
Example (Creation) ¶
// lazy function of a constant value val := Of(42) // create another function to transform this valS := F.Pipe1( val, Map(strconv.Itoa), ) fmt.Println(valS())
Output: 42
Now is a lazy computation that returns the current timestamp when evaluated. Each evaluation will return the current time at the moment of evaluation.
Example:
time1 := lazy.Now() // ... some time passes ... time2 := lazy.Now() // time1 and time2 will be different
func Do ¶
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 {
Config Config
Data Data
}
result := lazy.Do(State{})
func FromImpure ¶
FromImpure converts a side effect without a return value into a side effect that returns any
func FromLazy ¶
FromLazy creates a lazy computation from another lazy computation. This is an identity function that can be useful for type conversions or making the intent explicit in code.
Example:
original := func() int { return 42 }
wrapped := lazy.FromLazy(original)
result := wrapped() // 42
func MonadAp ¶
MonadAp applies a lazy function to a lazy value. Both the function and the value are evaluated when the result is evaluated.
This is the applicative functor operation, allowing you to apply functions that are themselves wrapped in a lazy context.
Example:
lazyFunc := lazy.Of(N.Mul(2)) lazyValue := lazy.Of(5) result := lazy.MonadAp(lazyFunc, lazyValue)() // 10
func MonadApFirst ¶
MonadApFirst combines two effectful actions, keeping only the result of the first.
func MonadApSecond ¶
MonadApSecond combines two effectful actions, keeping only the result of the second.
func MonadChain ¶
MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
func MonadChainFirst ¶
MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and keeping only the result of the first.
func MonadChainTo ¶
MonadChainTo composes computations in sequence, ignoring the return value of the first computation
func MonadMap ¶
MonadMap transforms the value inside a lazy computation using the provided function. The transformation is not applied until the lazy computation is evaluated.
This is the monadic version of Map, taking the lazy computation as the first parameter.
Example:
computation := lazy.Of(5) doubled := lazy.MonadMap(computation, N.Mul(2)) result := doubled() // 10
func MonadMapTo ¶
MonadMapTo replaces the value inside a lazy computation with a constant value. The original computation is still evaluated, but its result is discarded.
This is useful when you want to sequence computations but only care about the side effects (though Lazy should represent pure computations).
Example:
computation := lazy.Of("ignored")
replaced := lazy.MonadMapTo(computation, 42)
result := replaced() // 42
func MonadOf ¶
MonadOf creates a lazy computation that returns the given value. This is an alias for Of, provided for consistency with monadic naming conventions.
Example:
computation := lazy.MonadOf(42) result := computation() // 42
func MonadTraverseArray ¶
MonadTraverseArray applies a function returning a lazy computation to all elements in an array and transforms this into a lazy computation of that array.
This is the monadic version of TraverseArray, taking the array as the first parameter.
Example:
numbers := []int{1, 2, 3}
result := lazy.MonadTraverseArray(numbers, func(x int) lazy.Lazy[int] {
return lazy.Of(x * 2)
})()
// result is []int{2, 4, 6}
func MonadTraverseRecord ¶
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f Kleisli[A, B]) Lazy[map[K]B]
MonadTraverseRecord applies a function returning a lazy computation to all values in a record (map) and transforms this into a lazy computation of that record.
This is the monadic version of TraverseRecord, taking the record as the first parameter.
Example:
record := map[string]int{"a": 1, "b": 2}
result := lazy.MonadTraverseRecord(record, func(x int) lazy.Lazy[int] {
return lazy.Of(x * 2)
})()
// result is map[string]int{"a": 2, "b": 4}
func Of ¶
Of creates a lazy computation that returns the given value. This is the most basic way to lift a value into the Lazy context.
The computation is pure and will always return the same value when evaluated.
Example:
computation := lazy.Of(42) result := computation() // 42
func Retrying ¶
func Retrying[A any]( policy R.RetryPolicy, action Kleisli[R.RetryStatus, A], check func(A) bool, ) Lazy[A]
Retrying will retry the actions according to the check policy
policy - refers to the retry policy action - converts a status into an operation to be executed check - checks if the result of the action needs to be retried
func SequenceArray ¶
SequenceArray converts an array of [IO] to an [IO] of an array
func SequenceRecord ¶
func SequenceRecord[K comparable, A any](tas map[K]Lazy[A]) Lazy[map[K]A]
SequenceRecord converts a record of [IO] to an [IO] of a record
func SequenceT1 ¶
SequenceT1 combines a single lazy computation into a lazy tuple. This is mainly useful for consistency with the other SequenceT functions.
Example:
lazy1 := lazy.Of(42)
result := lazy.SequenceT1(lazy1)()
// result is tuple.Tuple1[int]{F1: 42}
func SequenceT2 ¶
SequenceT2 combines two lazy computations into a lazy tuple of two elements. Both computations are evaluated when the result is evaluated.
Example:
lazy1 := lazy.Of(42)
lazy2 := lazy.Of("hello")
result := lazy.SequenceT2(lazy1, lazy2)()
// result is tuple.Tuple2[int, string]{F1: 42, F2: "hello"}
func SequenceT3 ¶
SequenceT3 combines three lazy computations into a lazy tuple of three elements. All computations are evaluated when the result is evaluated.
Example:
lazy1 := lazy.Of(42)
lazy2 := lazy.Of("hello")
lazy3 := lazy.Of(true)
result := lazy.SequenceT3(lazy1, lazy2, lazy3)()
// result is tuple.Tuple3[int, string, bool]{F1: 42, F2: "hello", F3: true}
func SequenceT4 ¶
func SequenceT4[A, B, C, D any](a Lazy[A], b Lazy[B], c Lazy[C], d Lazy[D]) Lazy[tuple.Tuple4[A, B, C, D]]
SequenceT4 combines four lazy computations into a lazy tuple of four elements. All computations are evaluated when the result is evaluated.
Example:
lazy1 := lazy.Of(42)
lazy2 := lazy.Of("hello")
lazy3 := lazy.Of(true)
lazy4 := lazy.Of(3.14)
result := lazy.SequenceT4(lazy1, lazy2, lazy3, lazy4)()
// result is tuple.Tuple4[int, string, bool, float64]{F1: 42, F2: "hello", F3: true, F4: 3.14}
type Operator ¶
Operator represents a function that takes a lazy computation of type A and returns a lazy computation of type B.
Operators are used to transform lazy computations. They are essentially Kleisli arrows where the input is already wrapped in a Lazy context.
Example:
// An operator that doubles the value in a lazy computation doubleOp := lazy.Map(N.Mul(2)) // Apply it to a lazy computation result := doubleOp(lazy.Of(5))() // 10