mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-27 22:28:29 +02:00
fix: introduce Kleisli type
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -18,12 +18,12 @@ package readereither
|
|||||||
import "github.com/IBM/fp-go/v2/readereither"
|
import "github.com/IBM/fp-go/v2/readereither"
|
||||||
|
|
||||||
// TraverseArray transforms an array
|
// TraverseArray transforms an array
|
||||||
func TraverseArray[A, B any](f func(A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
|
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return readereither.TraverseArray(f)
|
return readereither.TraverseArray(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndex transforms an array
|
// TraverseArrayWithIndex transforms an array
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) Kleisli[[]A, []B] {
|
||||||
return readereither.TraverseArrayWithIndex(f)
|
return readereither.TraverseArrayWithIndex(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package readereither
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
G "github.com/IBM/fp-go/v2/readereither/generic"
|
G "github.com/IBM/fp-go/v2/readereither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,8 +81,8 @@ func Do[S any](
|
|||||||
// )
|
// )
|
||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) ReaderEither[T],
|
f Kleisli[S1, T],
|
||||||
) func(ReaderEither[S1]) ReaderEither[S2] {
|
) Kleisli[ReaderEither[S1], S2] {
|
||||||
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
|
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ func Bind[S1, S2, T any](
|
|||||||
func Let[S1, S2, T any](
|
func Let[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) func(ReaderEither[S1]) ReaderEither[S2] {
|
) Kleisli[ReaderEither[S1], S2] {
|
||||||
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
|
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,14 +98,14 @@ func Let[S1, S2, T any](
|
|||||||
func LetTo[S1, S2, T any](
|
func LetTo[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) func(ReaderEither[S1]) ReaderEither[S2] {
|
) Kleisli[ReaderEither[S1], S2] {
|
||||||
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
|
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindTo initializes a new state [S1] from a value [T]
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func(ReaderEither[T]) ReaderEither[S1] {
|
) Kleisli[ReaderEither[T], S1] {
|
||||||
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter)
|
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +149,161 @@ func BindTo[S1, T any](
|
|||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa ReaderEither[T],
|
fa ReaderEither[T],
|
||||||
) func(ReaderEither[S1]) ReaderEither[S2] {
|
) Kleisli[ReaderEither[S1], S2] {
|
||||||
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
|
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 ReaderEither computation that produces a value of type T
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that transforms ReaderEither[S] to ReaderEither[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 ApSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderEither[T],
|
||||||
|
) Kleisli[ReaderEither[S], S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, allowing you to:
|
||||||
|
// 1. Extract a field value using the lens
|
||||||
|
// 2. Use that value in a 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 ReaderEither computation
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that transforms ReaderEither[S] to ReaderEither[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.ReaderEither[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 BindL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[T, T],
|
||||||
|
) Kleisli[ReaderEither[S], S] {
|
||||||
|
return Bind[S, S, T](lens.Set, func(s S) ReaderEither[T] {
|
||||||
|
return f(lens.Get(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.
|
||||||
|
//
|
||||||
|
// 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 ReaderEither[S] to ReaderEither[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 LetL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Kleisli[ReaderEither[S], S] {
|
||||||
|
return Let[S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(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.
|
||||||
|
//
|
||||||
|
// 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 ReaderEither[S] to ReaderEither[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 LetToL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Kleisli[ReaderEither[S], S] {
|
||||||
|
return LetTo[S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,26 +28,26 @@ func Curry0[A any](f func(context.Context) (A, error)) ReaderEither[A] {
|
|||||||
return readereither.Curry0(f)
|
return readereither.Curry0(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Curry1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
|
func Curry1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A] {
|
||||||
return readereither.Curry1(f)
|
return readereither.Curry1(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) func(T2) ReaderEither[A] {
|
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) Kleisli[T2, A] {
|
||||||
return readereither.Curry2(f)
|
return readereither.Curry2(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderEither[A] {
|
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) Kleisli[T3, A] {
|
||||||
return readereither.Curry3(f)
|
return readereither.Curry3(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Uncurry1[T1, A any](f func(T1) ReaderEither[A]) func(context.Context, T1) (A, error) {
|
func Uncurry1[T1, A any](f Kleisli[T1, A]) func(context.Context, T1) (A, error) {
|
||||||
return readereither.Uncurry1(f)
|
return readereither.Uncurry1(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Uncurry2[T1, T2, A any](f func(T1) func(T2) ReaderEither[A]) func(context.Context, T1, T2) (A, error) {
|
func Uncurry2[T1, T2, A any](f func(T1) Kleisli[T2, A]) func(context.Context, T1, T2) (A, error) {
|
||||||
return readereither.Uncurry2(f)
|
return readereither.Uncurry2(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderEither[A]) func(context.Context, T1, T2, T3) (A, error) {
|
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) Kleisli[T3, A]) func(context.Context, T1, T2, T3) (A, error) {
|
||||||
return readereither.Uncurry3(f)
|
return readereither.Uncurry3(f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func From0[A any](f func(context.Context) (A, error)) func() ReaderEither[A] {
|
|||||||
return readereither.From0(f)
|
return readereither.From0(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func From1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
|
func From1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A] {
|
||||||
return readereither.From1(f)
|
return readereither.From1(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ func Map[A, B any](f func(A) B) Operator[A, B] {
|
|||||||
return readereither.Map[context.Context, error](f)
|
return readereither.Map[context.Context, error](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChain[A, B any](ma ReaderEither[A], f func(A) ReaderEither[B]) ReaderEither[B] {
|
func MonadChain[A, B any](ma ReaderEither[A], f Kleisli[A, B]) ReaderEither[B] {
|
||||||
return readereither.MonadChain(ma, f)
|
return readereither.MonadChain(ma, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Chain[A, B any](f func(A) ReaderEither[B]) Operator[A, B] {
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||||
return readereither.Chain(f)
|
return readereither.Chain(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,11 +61,11 @@ func Ap[A, B any](fa ReaderEither[A]) func(ReaderEither[func(A) B]) ReaderEither
|
|||||||
return readereither.Ap[B](fa)
|
return readereither.Ap[B](fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderEither[A] {
|
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A] {
|
||||||
return readereither.FromPredicate[context.Context](pred, onFalse)
|
return readereither.FromPredicate[context.Context](pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
|
func OrElse[A any](onLeft Kleisli[error, A]) Kleisli[ReaderEither[A], A] {
|
||||||
return readereither.OrElse(onLeft)
|
return readereither.OrElse(onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,5 +31,6 @@ type (
|
|||||||
// ReaderEither is a specialization of the Reader monad for the typical golang scenario
|
// ReaderEither is a specialization of the Reader monad for the typical golang scenario
|
||||||
ReaderEither[A any] = readereither.ReaderEither[context.Context, error, A]
|
ReaderEither[A any] = readereither.ReaderEither[context.Context, error, A]
|
||||||
|
|
||||||
Operator[A, B any] = reader.Reader[ReaderEither[A], ReaderEither[B]]
|
Kleisli[A, B any] = reader.Reader[A, ReaderEither[B]]
|
||||||
|
Operator[A, B any] = Kleisli[ReaderEither[A], B]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/internal/apply"
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
"github.com/IBM/fp-go/v2/internal/chain"
|
"github.com/IBM/fp-go/v2/internal/chain"
|
||||||
"github.com/IBM/fp-go/v2/internal/functor"
|
"github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||||
@@ -31,6 +32,8 @@ import (
|
|||||||
// Config Config
|
// Config Config
|
||||||
// }
|
// }
|
||||||
// result := readerioeither.Do(State{})
|
// result := readerioeither.Do(State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Do[S any](
|
func Do[S any](
|
||||||
empty S,
|
empty S,
|
||||||
) ReaderIOEither[S] {
|
) ReaderIOEither[S] {
|
||||||
@@ -79,10 +82,12 @@ func Do[S any](
|
|||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) ReaderIOEither[T],
|
f Kleisli[S1, T],
|
||||||
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
) Operator[S1, S2] {
|
||||||
return chain.Bind(
|
return chain.Bind(
|
||||||
Chain[S1, S2],
|
Chain[S1, S2],
|
||||||
Map[T, S2],
|
Map[T, S2],
|
||||||
@@ -92,10 +97,12 @@ func Bind[S1, S2, T any](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Let[S1, S2, T any](
|
func Let[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
) Operator[S1, S2] {
|
||||||
return functor.Let(
|
return functor.Let(
|
||||||
Map[S1, S2],
|
Map[S1, S2],
|
||||||
setter,
|
setter,
|
||||||
@@ -104,10 +111,12 @@ func Let[S1, S2, T any](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func LetTo[S1, S2, T any](
|
func LetTo[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
) Operator[S1, S2] {
|
||||||
return functor.LetTo(
|
return functor.LetTo(
|
||||||
Map[S1, S2],
|
Map[S1, S2],
|
||||||
setter,
|
setter,
|
||||||
@@ -116,6 +125,8 @@ func LetTo[S1, S2, T any](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BindTo initializes a new state [S1] from a value [T]
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) Operator[T, S1] {
|
) Operator[T, S1] {
|
||||||
@@ -166,10 +177,12 @@ func BindTo[S1, T any](
|
|||||||
// getConfig,
|
// getConfig,
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa ReaderIOEither[T],
|
fa ReaderIOEither[T],
|
||||||
) func(ReaderIOEither[S1]) ReaderIOEither[S2] {
|
) Operator[S1, S2] {
|
||||||
return apply.ApS(
|
return apply.ApS(
|
||||||
Ap[S2, T],
|
Ap[S2, T],
|
||||||
Map[S1, func(T) S2],
|
Map[S1, func(T) S2],
|
||||||
@@ -177,3 +190,152 @@ func ApS[S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// getUser := func(ctx context.Context) ioeither.IOEither[error, User] {
|
||||||
|
// return ioeither.TryCatch(func() (User, error) {
|
||||||
|
// return fetchUser(ctx)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Of(State{}),
|
||||||
|
// readerioeither.ApSL(userLens, getUser),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderIOEither[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ReaderIOEither computation that produces an updated value.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// User User
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do(State{}),
|
||||||
|
// readerioeither.BindL(userLens, func(user User) readerioeither.ReaderIOEither[User] {
|
||||||
|
// return func(ctx context.Context) ioeither.IOEither[error, User] {
|
||||||
|
// return ioeither.TryCatch(func() (User, error) {
|
||||||
|
// return fetchUser(ctx)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return Bind[S, S, T](lens.Set, func(s S) ReaderIOEither[T] {
|
||||||
|
return f(lens.Get(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 ReaderIOEither).
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// User User
|
||||||
|
// Config Config
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do(State{User: User{Name: "Alice"}}),
|
||||||
|
// readerioeither.LetL(userLens, func(user User) User {
|
||||||
|
// user.Name = "Bob"
|
||||||
|
// return user
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Operator[S, S] {
|
||||||
|
return Let[S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(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
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// newUser := User{Name: "Bob", ID: 123}
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do(State{}),
|
||||||
|
// readerioeither.LetToL(userLens, newUser),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetToL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Operator[S, S] {
|
||||||
|
return LetTo[S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
E "github.com/IBM/fp-go/v2/either"
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/IBM/fp-go/v2/internal/utils"
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ func TestBind(t *testing.T) {
|
|||||||
Map(utils.GetFullName),
|
Map(utils.GetFullName),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
|
assert.Equal(t, res(t.Context())(), E.Of[error]("John Doe"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApS(t *testing.T) {
|
func TestApS(t *testing.T) {
|
||||||
@@ -54,5 +55,221 @@ func TestApS(t *testing.T) {
|
|||||||
Map(utils.GetFullName),
|
Map(utils.GetFullName),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.Equal(t, res(context.Background())(), E.Of[error]("John Doe"))
|
assert.Equal(t, res(t.Context())(), E.Of[error]("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS_WithError(t *testing.T) {
|
||||||
|
// Test that ApS propagates errors correctly
|
||||||
|
testErr := assert.AnError
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Left[string](testErr)),
|
||||||
|
ApS(utils.SetGivenName, Of("John")),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := res(t.Context())()
|
||||||
|
assert.True(t, E.IsLeft(result))
|
||||||
|
assert.Equal(t, testErr, E.ToError(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS_WithSecondError(t *testing.T) {
|
||||||
|
// Test that ApS propagates errors from the second operation
|
||||||
|
testErr := assert.AnError
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Left[string](testErr)),
|
||||||
|
Map(utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := res(t.Context())()
|
||||||
|
assert.True(t, E.IsLeft(result))
|
||||||
|
assert.Equal(t, testErr, E.ToError(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS_MultipleFields(t *testing.T) {
|
||||||
|
// Test ApS with more than two fields
|
||||||
|
type Person struct {
|
||||||
|
FirstName string
|
||||||
|
MiddleName string
|
||||||
|
LastName string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
setFirstName := func(s string) func(Person) Person {
|
||||||
|
return func(p Person) Person {
|
||||||
|
p.FirstName = s
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMiddleName := func(s string) func(Person) Person {
|
||||||
|
return func(p Person) Person {
|
||||||
|
p.MiddleName = s
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastName := func(s string) func(Person) Person {
|
||||||
|
return func(p Person) Person {
|
||||||
|
p.LastName = s
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAge := func(a int) func(Person) Person {
|
||||||
|
return func(p Person) Person {
|
||||||
|
p.Age = a
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := F.Pipe5(
|
||||||
|
Do(Person{}),
|
||||||
|
ApS(setFirstName, Of("John")),
|
||||||
|
ApS(setMiddleName, Of("Q")),
|
||||||
|
ApS(setLastName, Of("Doe")),
|
||||||
|
ApS(setAge, Of(42)),
|
||||||
|
Map(func(p Person) Person { return p }),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := res(t.Context())()
|
||||||
|
assert.True(t, E.IsRight(result))
|
||||||
|
person := E.ToOption(result)
|
||||||
|
assert.True(t, O.IsSome(person))
|
||||||
|
p, _ := O.Unwrap(person)
|
||||||
|
assert.Equal(t, "John", p.FirstName)
|
||||||
|
assert.Equal(t, "Q", p.MiddleName)
|
||||||
|
assert.Equal(t, "Doe", p.LastName)
|
||||||
|
assert.Equal(t, 42, p.Age)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS_WithDifferentTypes(t *testing.T) {
|
||||||
|
// Test ApS with different value types
|
||||||
|
type State struct {
|
||||||
|
Name string
|
||||||
|
Count int
|
||||||
|
Flag bool
|
||||||
|
}
|
||||||
|
|
||||||
|
setName := func(s string) func(State) State {
|
||||||
|
return func(st State) State {
|
||||||
|
st.Name = s
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setCount := func(c int) func(State) State {
|
||||||
|
return func(st State) State {
|
||||||
|
st.Count = c
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFlag := func(f bool) func(State) State {
|
||||||
|
return func(st State) State {
|
||||||
|
st.Flag = f
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := F.Pipe4(
|
||||||
|
Do(State{}),
|
||||||
|
ApS(setName, Of("test")),
|
||||||
|
ApS(setCount, Of(100)),
|
||||||
|
ApS(setFlag, Of(true)),
|
||||||
|
Map(func(s State) State { return s }),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := res(t.Context())()
|
||||||
|
assert.True(t, E.IsRight(result))
|
||||||
|
stateOpt := E.ToOption(result)
|
||||||
|
assert.True(t, O.IsSome(stateOpt))
|
||||||
|
state, _ := O.Unwrap(stateOpt)
|
||||||
|
assert.Equal(t, "test", state.Name)
|
||||||
|
assert.Equal(t, 100, state.Count)
|
||||||
|
assert.True(t, state.Flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS_EmptyState(t *testing.T) {
|
||||||
|
// Test ApS starting with an empty state
|
||||||
|
type Empty struct{}
|
||||||
|
|
||||||
|
res := Do(Empty{})
|
||||||
|
|
||||||
|
result := res(t.Context())()
|
||||||
|
assert.True(t, E.IsRight(result))
|
||||||
|
emptyOpt := E.ToOption(result)
|
||||||
|
assert.True(t, O.IsSome(emptyOpt))
|
||||||
|
empty, _ := O.Unwrap(emptyOpt)
|
||||||
|
assert.Equal(t, Empty{}, empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS_ChainedWithBind(t *testing.T) {
|
||||||
|
// Test mixing ApS with Bind operations
|
||||||
|
type State struct {
|
||||||
|
Independent string
|
||||||
|
Dependent string
|
||||||
|
}
|
||||||
|
|
||||||
|
setIndependent := func(s string) func(State) State {
|
||||||
|
return func(st State) State {
|
||||||
|
st.Independent = s
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDependent := func(s string) func(State) State {
|
||||||
|
return func(st State) State {
|
||||||
|
st.Dependent = s
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependentValue := func(s State) ReaderIOEither[string] {
|
||||||
|
// This depends on the Independent field
|
||||||
|
return Of(s.Independent + "-dependent")
|
||||||
|
}
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do(State{}),
|
||||||
|
ApS(setIndependent, Of("value")),
|
||||||
|
Bind(setDependent, getDependentValue),
|
||||||
|
Map(func(s State) State { return s }),
|
||||||
|
)
|
||||||
|
|
||||||
|
result := res(t.Context())()
|
||||||
|
assert.True(t, E.IsRight(result))
|
||||||
|
stateOpt := E.ToOption(result)
|
||||||
|
assert.True(t, O.IsSome(stateOpt))
|
||||||
|
state, _ := O.Unwrap(stateOpt)
|
||||||
|
assert.Equal(t, "value", state.Independent)
|
||||||
|
assert.Equal(t, "value-dependent", state.Dependent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS_WithContextCancellation(t *testing.T) {
|
||||||
|
// Test that ApS respects context cancellation
|
||||||
|
type State struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue := func(s string) func(State) State {
|
||||||
|
return func(st State) State {
|
||||||
|
st.Value = s
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a computation that would succeed
|
||||||
|
computation := ApS(setValue, Of("test"))(Do(State{}))
|
||||||
|
|
||||||
|
// Create a cancelled context
|
||||||
|
ctx, cancel := context.WithCancel(t.Context())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
result := computation(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(result))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func Bracket[
|
|||||||
A, B, ANY any](
|
A, B, ANY any](
|
||||||
|
|
||||||
acquire ReaderIOEither[A],
|
acquire ReaderIOEither[A],
|
||||||
use func(A) ReaderIOEither[B],
|
use Kleisli[A, B],
|
||||||
release func(A, Either[B]) ReaderIOEither[ANY],
|
release func(A, Either[B]) ReaderIOEither[ANY],
|
||||||
) ReaderIOEither[B] {
|
) ReaderIOEither[B] {
|
||||||
return bracket.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY], Either[B], A, B](
|
return bracket.Bracket[ReaderIOEither[A], ReaderIOEither[B], ReaderIOEither[ANY], Either[B], A, B](
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import (
|
|||||||
// eqRIE := Eq(eqInt)
|
// eqRIE := Eq(eqInt)
|
||||||
// ctx := context.Background()
|
// ctx := context.Background()
|
||||||
// equal := eqRIE(ctx).Equals(Right[int](42), Right[int](42)) // true
|
// equal := eqRIE(ctx).Equals(Right[int](42), Right[int](42)) // true
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eq[A any](eq eq.Eq[Either[A]]) func(context.Context) eq.Eq[ReaderIOEither[A]] {
|
func Eq[A any](eq eq.Eq[Either[A]]) func(context.Context) eq.Eq[ReaderIOEither[A]] {
|
||||||
return RIOE.Eq[context.Context](eq)
|
return RIOE.Eq[context.Context](eq)
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,8 @@ const (
|
|||||||
// - e: The Either value to lift into ReaderIOEither
|
// - e: The Either value to lift into ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that produces the given Either value.
|
// Returns a ReaderIOEither that produces the given Either value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromEither[A any](e Either[A]) ReaderIOEither[A] {
|
func FromEither[A any](e Either[A]) ReaderIOEither[A] {
|
||||||
return readerioeither.FromEither[context.Context](e)
|
return readerioeither.FromEither[context.Context](e)
|
||||||
}
|
}
|
||||||
@@ -59,6 +61,8 @@ func Left[A any](l error) ReaderIOEither[A] {
|
|||||||
// - r: The success value
|
// - r: The success value
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that always succeeds with the given value.
|
// Returns a ReaderIOEither that always succeeds with the given value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Right[A any](r A) ReaderIOEither[A] {
|
func Right[A any](r A) ReaderIOEither[A] {
|
||||||
return readerioeither.Right[context.Context, error](r)
|
return readerioeither.Right[context.Context, error](r)
|
||||||
}
|
}
|
||||||
@@ -71,6 +75,8 @@ func Right[A any](r A) ReaderIOEither[A] {
|
|||||||
// - f: The transformation function
|
// - f: The transformation function
|
||||||
//
|
//
|
||||||
// Returns a new ReaderIOEither with the transformed value.
|
// Returns a new ReaderIOEither with the transformed value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
||||||
return readerioeither.MonadMap(fa, f)
|
return readerioeither.MonadMap(fa, f)
|
||||||
}
|
}
|
||||||
@@ -82,6 +88,8 @@ func MonadMap[A, B any](fa ReaderIOEither[A], f func(A) B) ReaderIOEither[B] {
|
|||||||
// - f: The transformation function
|
// - f: The transformation function
|
||||||
//
|
//
|
||||||
// Returns a function that transforms a ReaderIOEither.
|
// Returns a function that transforms a ReaderIOEither.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Map[A, B any](f func(A) B) Operator[A, B] {
|
func Map[A, B any](f func(A) B) Operator[A, B] {
|
||||||
return readerioeither.Map[context.Context, error](f)
|
return readerioeither.Map[context.Context, error](f)
|
||||||
}
|
}
|
||||||
@@ -94,6 +102,8 @@ func Map[A, B any](f func(A) B) Operator[A, B] {
|
|||||||
// - b: The constant value to use
|
// - b: The constant value to use
|
||||||
//
|
//
|
||||||
// Returns a new ReaderIOEither with the constant value.
|
// Returns a new ReaderIOEither with the constant value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] {
|
func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] {
|
||||||
return readerioeither.MonadMapTo(fa, b)
|
return readerioeither.MonadMapTo(fa, b)
|
||||||
}
|
}
|
||||||
@@ -105,6 +115,8 @@ func MonadMapTo[A, B any](fa ReaderIOEither[A], b B) ReaderIOEither[B] {
|
|||||||
// - b: The constant value to use
|
// - b: The constant value to use
|
||||||
//
|
//
|
||||||
// Returns a function that transforms a ReaderIOEither.
|
// Returns a function that transforms a ReaderIOEither.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MapTo[A, B any](b B) Operator[A, B] {
|
func MapTo[A, B any](b B) Operator[A, B] {
|
||||||
return readerioeither.MapTo[context.Context, error, A](b)
|
return readerioeither.MapTo[context.Context, error, A](b)
|
||||||
}
|
}
|
||||||
@@ -117,7 +129,9 @@ func MapTo[A, B any](b B) Operator[A, B] {
|
|||||||
// - f: Function that produces the second ReaderIOEither based on the first's result
|
// - f: Function that produces the second ReaderIOEither based on the first's result
|
||||||
//
|
//
|
||||||
// Returns a new ReaderIOEither representing the sequenced computation.
|
// Returns a new ReaderIOEither representing the sequenced computation.
|
||||||
func MonadChain[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[B] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChain[A, B any](ma ReaderIOEither[A], f Kleisli[A, B]) ReaderIOEither[B] {
|
||||||
return readerioeither.MonadChain(ma, f)
|
return readerioeither.MonadChain(ma, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +142,9 @@ func MonadChain[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) Rea
|
|||||||
// - f: Function that produces the second ReaderIOEither based on the first's result
|
// - f: Function that produces the second ReaderIOEither based on the first's result
|
||||||
//
|
//
|
||||||
// Returns a function that sequences ReaderIOEither computations.
|
// Returns a function that sequences ReaderIOEither computations.
|
||||||
func Chain[A, B any](f func(A) ReaderIOEither[B]) Operator[A, B] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||||
return readerioeither.Chain(f)
|
return readerioeither.Chain(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +156,9 @@ func Chain[A, B any](f func(A) ReaderIOEither[B]) Operator[A, B] {
|
|||||||
// - f: Function that produces the second ReaderIOEither
|
// - f: Function that produces the second ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither with the result of the first computation.
|
// Returns a ReaderIOEither with the result of the first computation.
|
||||||
func MonadChainFirst[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]) ReaderIOEither[A] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirst[A, B any](ma ReaderIOEither[A], f Kleisli[A, B]) ReaderIOEither[A] {
|
||||||
return readerioeither.MonadChainFirst(ma, f)
|
return readerioeither.MonadChainFirst(ma, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +169,9 @@ func MonadChainFirst[A, B any](ma ReaderIOEither[A], f func(A) ReaderIOEither[B]
|
|||||||
// - f: Function that produces the second ReaderIOEither
|
// - f: Function that produces the second ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a function that sequences ReaderIOEither computations.
|
// Returns a function that sequences ReaderIOEither computations.
|
||||||
func ChainFirst[A, B any](f func(A) ReaderIOEither[B]) Operator[A, A] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
||||||
return readerioeither.ChainFirst(f)
|
return readerioeither.ChainFirst(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +182,8 @@ func ChainFirst[A, B any](f func(A) ReaderIOEither[B]) Operator[A, A] {
|
|||||||
// - a: The value to wrap
|
// - a: The value to wrap
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that always succeeds with the given value.
|
// Returns a ReaderIOEither that always succeeds with the given value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Of[A any](a A) ReaderIOEither[A] {
|
func Of[A any](a A) ReaderIOEither[A] {
|
||||||
return readerioeither.Of[context.Context, error](a)
|
return readerioeither.Of[context.Context, error](a)
|
||||||
}
|
}
|
||||||
@@ -240,6 +262,8 @@ func MonadAp[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) Read
|
|||||||
// - fa: ReaderIOEither containing a value
|
// - fa: ReaderIOEither containing a value
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither with the function applied to the value.
|
// Returns a ReaderIOEither with the function applied to the value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadApSeq[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) ReaderIOEither[B] {
|
func MonadApSeq[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) ReaderIOEither[B] {
|
||||||
return readerioeither.MonadApSeq(fab, fa)
|
return readerioeither.MonadApSeq(fab, fa)
|
||||||
}
|
}
|
||||||
@@ -251,6 +275,8 @@ func MonadApSeq[B, A any](fab ReaderIOEither[func(A) B], fa ReaderIOEither[A]) R
|
|||||||
// - fa: ReaderIOEither containing a value
|
// - fa: ReaderIOEither containing a value
|
||||||
//
|
//
|
||||||
// Returns a function that applies a ReaderIOEither function to the value.
|
// Returns a function that applies a ReaderIOEither function to the value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Ap[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
func Ap[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
||||||
return function.Bind2nd(MonadAp[B, A], fa)
|
return function.Bind2nd(MonadAp[B, A], fa)
|
||||||
}
|
}
|
||||||
@@ -262,6 +288,8 @@ func Ap[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
|||||||
// - fa: ReaderIOEither containing a value
|
// - fa: ReaderIOEither containing a value
|
||||||
//
|
//
|
||||||
// Returns a function that applies a ReaderIOEither function to the value sequentially.
|
// Returns a function that applies a ReaderIOEither function to the value sequentially.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApSeq[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
func ApSeq[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
||||||
return function.Bind2nd(MonadApSeq[B, A], fa)
|
return function.Bind2nd(MonadApSeq[B, A], fa)
|
||||||
}
|
}
|
||||||
@@ -273,6 +301,8 @@ func ApSeq[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
|||||||
// - fa: ReaderIOEither containing a value
|
// - fa: ReaderIOEither containing a value
|
||||||
//
|
//
|
||||||
// Returns a function that applies a ReaderIOEither function to the value in parallel.
|
// Returns a function that applies a ReaderIOEither function to the value in parallel.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApPar[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
func ApPar[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
||||||
return function.Bind2nd(MonadApPar[B, A], fa)
|
return function.Bind2nd(MonadApPar[B, A], fa)
|
||||||
}
|
}
|
||||||
@@ -285,7 +315,9 @@ func ApPar[B, A any](fa ReaderIOEither[A]) Operator[func(A) B, B] {
|
|||||||
// - onFalse: Function to generate an error when predicate fails
|
// - onFalse: Function to generate an error when predicate fails
|
||||||
//
|
//
|
||||||
// Returns a function that converts a value to ReaderIOEither based on the predicate.
|
// Returns a function that converts a value to ReaderIOEither based on the predicate.
|
||||||
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderIOEither[A] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A] {
|
||||||
return readerioeither.FromPredicate[context.Context](pred, onFalse)
|
return readerioeither.FromPredicate[context.Context](pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +328,9 @@ func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) Read
|
|||||||
// - onLeft: Function that produces an alternative ReaderIOEither from the error
|
// - onLeft: Function that produces an alternative ReaderIOEither from the error
|
||||||
//
|
//
|
||||||
// Returns a function that provides fallback behavior for failed computations.
|
// Returns a function that provides fallback behavior for failed computations.
|
||||||
func OrElse[A any](onLeft func(error) ReaderIOEither[A]) Operator[A, A] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func OrElse[A any](onLeft Kleisli[error, A]) Operator[A, A] {
|
||||||
return readerioeither.OrElse[context.Context](onLeft)
|
return readerioeither.OrElse[context.Context](onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,6 +338,8 @@ func OrElse[A any](onLeft func(error) ReaderIOEither[A]) Operator[A, A] {
|
|||||||
// This is useful for accessing the [context.Context] within a computation.
|
// This is useful for accessing the [context.Context] within a computation.
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that produces the context.
|
// Returns a ReaderIOEither that produces the context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Ask() ReaderIOEither[context.Context] {
|
func Ask() ReaderIOEither[context.Context] {
|
||||||
return readerioeither.Ask[context.Context, error]()
|
return readerioeither.Ask[context.Context, error]()
|
||||||
}
|
}
|
||||||
@@ -316,6 +352,8 @@ func Ask() ReaderIOEither[context.Context] {
|
|||||||
// - f: Function that produces an Either
|
// - f: Function that produces an Either
|
||||||
//
|
//
|
||||||
// Returns a new ReaderIOEither with the chained computation.
|
// Returns a new ReaderIOEither with the chained computation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) ReaderIOEither[B] {
|
func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) ReaderIOEither[B] {
|
||||||
return readerioeither.MonadChainEitherK[context.Context](ma, f)
|
return readerioeither.MonadChainEitherK[context.Context](ma, f)
|
||||||
}
|
}
|
||||||
@@ -327,7 +365,9 @@ func MonadChainEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) Read
|
|||||||
// - f: Function that produces an Either
|
// - f: Function that produces an Either
|
||||||
//
|
//
|
||||||
// Returns a function that chains the Either-returning function.
|
// Returns a function that chains the Either-returning function.
|
||||||
func ChainEitherK[A, B any](f func(A) Either[B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainEitherK[A, B any](f func(A) Either[B]) Operator[A, B] {
|
||||||
return readerioeither.ChainEitherK[context.Context](f)
|
return readerioeither.ChainEitherK[context.Context](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,6 +379,8 @@ func ChainEitherK[A, B any](f func(A) Either[B]) func(ma ReaderIOEither[A]) Read
|
|||||||
// - f: Function that produces an Either
|
// - f: Function that produces an Either
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither with the original value if both computations succeed.
|
// Returns a ReaderIOEither with the original value if both computations succeed.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) ReaderIOEither[A] {
|
func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B]) ReaderIOEither[A] {
|
||||||
return readerioeither.MonadChainFirstEitherK[context.Context](ma, f)
|
return readerioeither.MonadChainFirstEitherK[context.Context](ma, f)
|
||||||
}
|
}
|
||||||
@@ -350,7 +392,9 @@ func MonadChainFirstEitherK[A, B any](ma ReaderIOEither[A], f func(A) Either[B])
|
|||||||
// - f: Function that produces an Either
|
// - f: Function that produces an Either
|
||||||
//
|
//
|
||||||
// Returns a function that chains the Either-returning function.
|
// Returns a function that chains the Either-returning function.
|
||||||
func ChainFirstEitherK[A, B any](f func(A) Either[B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstEitherK[A, B any](f func(A) Either[B]) Operator[A, A] {
|
||||||
return readerioeither.ChainFirstEitherK[context.Context](f)
|
return readerioeither.ChainFirstEitherK[context.Context](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,6 +405,8 @@ func ChainFirstEitherK[A, B any](f func(A) Either[B]) func(ma ReaderIOEither[A])
|
|||||||
// - onNone: Function to generate an error when Option is None
|
// - onNone: Function to generate an error when Option is None
|
||||||
//
|
//
|
||||||
// Returns a function that chains Option-returning functions into ReaderIOEither.
|
// Returns a function that chains Option-returning functions into ReaderIOEither.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operator[A, B] {
|
func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operator[A, B] {
|
||||||
return readerioeither.ChainOptionK[context.Context, A, B](onNone)
|
return readerioeither.ChainOptionK[context.Context, A, B](onNone)
|
||||||
}
|
}
|
||||||
@@ -372,6 +418,8 @@ func ChainOptionK[A, B any](onNone func() error) func(func(A) Option[B]) Operato
|
|||||||
// - t: The IOEither to convert
|
// - t: The IOEither to convert
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that executes the IOEither.
|
// Returns a ReaderIOEither that executes the IOEither.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromIOEither[A any](t ioeither.IOEither[error, A]) ReaderIOEither[A] {
|
func FromIOEither[A any](t ioeither.IOEither[error, A]) ReaderIOEither[A] {
|
||||||
return readerioeither.FromIOEither[context.Context](t)
|
return readerioeither.FromIOEither[context.Context](t)
|
||||||
}
|
}
|
||||||
@@ -383,6 +431,8 @@ func FromIOEither[A any](t ioeither.IOEither[error, A]) ReaderIOEither[A] {
|
|||||||
// - t: The IO to convert
|
// - t: The IO to convert
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that executes the IO and wraps the result in Right.
|
// Returns a ReaderIOEither that executes the IO and wraps the result in Right.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromIO[A any](t IO[A]) ReaderIOEither[A] {
|
func FromIO[A any](t IO[A]) ReaderIOEither[A] {
|
||||||
return readerioeither.FromIO[context.Context, error](t)
|
return readerioeither.FromIO[context.Context, error](t)
|
||||||
}
|
}
|
||||||
@@ -395,6 +445,8 @@ func FromIO[A any](t IO[A]) ReaderIOEither[A] {
|
|||||||
// - t: The Lazy computation to convert
|
// - t: The Lazy computation to convert
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that executes the Lazy computation and wraps the result in Right.
|
// Returns a ReaderIOEither that executes the Lazy computation and wraps the result in Right.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromLazy[A any](t Lazy[A]) ReaderIOEither[A] {
|
func FromLazy[A any](t Lazy[A]) ReaderIOEither[A] {
|
||||||
return readerioeither.FromIO[context.Context, error](t)
|
return readerioeither.FromIO[context.Context, error](t)
|
||||||
}
|
}
|
||||||
@@ -420,6 +472,8 @@ func Never[A any]() ReaderIOEither[A] {
|
|||||||
// - f: Function that produces an IO
|
// - f: Function that produces an IO
|
||||||
//
|
//
|
||||||
// Returns a new ReaderIOEither with the chained IO computation.
|
// Returns a new ReaderIOEither with the chained IO computation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEither[B] {
|
func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEither[B] {
|
||||||
return readerioeither.MonadChainIOK(ma, f)
|
return readerioeither.MonadChainIOK(ma, f)
|
||||||
}
|
}
|
||||||
@@ -431,7 +485,9 @@ func MonadChainIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEith
|
|||||||
// - f: Function that produces an IO
|
// - f: Function that produces an IO
|
||||||
//
|
//
|
||||||
// Returns a function that chains the IO-returning function.
|
// Returns a function that chains the IO-returning function.
|
||||||
func ChainIOK[A, B any](f func(A) IO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainIOK[A, B any](f func(A) IO[B]) Operator[A, B] {
|
||||||
return readerioeither.ChainIOK[context.Context, error](f)
|
return readerioeither.ChainIOK[context.Context, error](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,6 +499,8 @@ func ChainIOK[A, B any](f func(A) IO[B]) func(ma ReaderIOEither[A]) ReaderIOEith
|
|||||||
// - f: Function that produces an IO
|
// - f: Function that produces an IO
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither with the original value after executing the IO.
|
// Returns a ReaderIOEither with the original value after executing the IO.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEither[A] {
|
func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderIOEither[A] {
|
||||||
return readerioeither.MonadChainFirstIOK(ma, f)
|
return readerioeither.MonadChainFirstIOK(ma, f)
|
||||||
}
|
}
|
||||||
@@ -454,7 +512,9 @@ func MonadChainFirstIOK[A, B any](ma ReaderIOEither[A], f func(A) IO[B]) ReaderI
|
|||||||
// - f: Function that produces an IO
|
// - f: Function that produces an IO
|
||||||
//
|
//
|
||||||
// Returns a function that chains the IO-returning function.
|
// Returns a function that chains the IO-returning function.
|
||||||
func ChainFirstIOK[A, B any](f func(A) IO[B]) func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstIOK[A, B any](f func(A) IO[B]) Operator[A, A] {
|
||||||
return readerioeither.ChainFirstIOK[context.Context, error](f)
|
return readerioeither.ChainFirstIOK[context.Context, error](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +525,9 @@ func ChainFirstIOK[A, B any](f func(A) IO[B]) func(ma ReaderIOEither[A]) ReaderI
|
|||||||
// - f: Function that produces an IOEither
|
// - f: Function that produces an IOEither
|
||||||
//
|
//
|
||||||
// Returns a function that chains the IOEither-returning function.
|
// Returns a function that chains the IOEither-returning function.
|
||||||
func ChainIOEitherK[A, B any](f func(A) ioeither.IOEither[error, B]) func(ma ReaderIOEither[A]) ReaderIOEither[B] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainIOEitherK[A, B any](f func(A) ioeither.IOEither[error, B]) Operator[A, B] {
|
||||||
return readerioeither.ChainIOEitherK[context.Context](f)
|
return readerioeither.ChainIOEitherK[context.Context](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,7 +538,7 @@ func ChainIOEitherK[A, B any](f func(A) ioeither.IOEither[error, B]) func(ma Rea
|
|||||||
// - delay: The duration to wait before executing the computation
|
// - delay: The duration to wait before executing the computation
|
||||||
//
|
//
|
||||||
// Returns a function that delays a ReaderIOEither computation.
|
// Returns a function that delays a ReaderIOEither computation.
|
||||||
func Delay[A any](delay time.Duration) func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
func Delay[A any](delay time.Duration) Operator[A, A] {
|
||||||
return func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
return func(ma ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
return func(ctx context.Context) IOEither[A] {
|
return func(ctx context.Context) IOEither[A] {
|
||||||
return func() Either[A] {
|
return func() Either[A] {
|
||||||
@@ -517,6 +579,8 @@ func Timer(delay time.Duration) ReaderIOEither[time.Time] {
|
|||||||
// - gen: Lazy generator function that produces a ReaderIOEither
|
// - gen: Lazy generator function that produces a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that generates a fresh computation on each execution.
|
// Returns a ReaderIOEither that generates a fresh computation on each execution.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Defer[A any](gen Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
func Defer[A any](gen Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||||
return readerioeither.Defer(gen)
|
return readerioeither.Defer(gen)
|
||||||
}
|
}
|
||||||
@@ -528,6 +592,8 @@ func Defer[A any](gen Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
|||||||
// - f: Function that takes a context and returns a function producing (value, error)
|
// - f: Function that takes a context and returns a function producing (value, error)
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that wraps the error-returning function.
|
// Returns a ReaderIOEither that wraps the error-returning function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] {
|
func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A] {
|
||||||
return readerioeither.TryCatch(f, errors.IdentityError)
|
return readerioeither.TryCatch(f, errors.IdentityError)
|
||||||
}
|
}
|
||||||
@@ -540,6 +606,8 @@ func TryCatch[A any](f func(context.Context) func() (A, error)) ReaderIOEither[A
|
|||||||
// - second: Lazy alternative ReaderIOEither to use if first fails
|
// - second: Lazy alternative ReaderIOEither to use if first fails
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that tries the first, then the second if first fails.
|
// Returns a ReaderIOEither that tries the first, then the second if first fails.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadAlt[A any](first ReaderIOEither[A], second Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
func MonadAlt[A any](first ReaderIOEither[A], second Lazy[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||||
return readerioeither.MonadAlt(first, second)
|
return readerioeither.MonadAlt(first, second)
|
||||||
}
|
}
|
||||||
@@ -551,6 +619,8 @@ func MonadAlt[A any](first ReaderIOEither[A], second Lazy[ReaderIOEither[A]]) Re
|
|||||||
// - second: Lazy alternative ReaderIOEither to use if first fails
|
// - second: Lazy alternative ReaderIOEither to use if first fails
|
||||||
//
|
//
|
||||||
// Returns a function that provides fallback behavior.
|
// Returns a function that provides fallback behavior.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Alt[A any](second Lazy[ReaderIOEither[A]]) Operator[A, A] {
|
func Alt[A any](second Lazy[ReaderIOEither[A]]) Operator[A, A] {
|
||||||
return readerioeither.Alt(second)
|
return readerioeither.Alt(second)
|
||||||
}
|
}
|
||||||
@@ -563,6 +633,8 @@ func Alt[A any](second Lazy[ReaderIOEither[A]]) Operator[A, A] {
|
|||||||
// - rdr: The ReaderIOEither to memoize
|
// - rdr: The ReaderIOEither to memoize
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither that caches its result after the first execution.
|
// Returns a ReaderIOEither that caches its result after the first execution.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
|
func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
|
||||||
return readerioeither.Memoize(rdr)
|
return readerioeither.Memoize(rdr)
|
||||||
}
|
}
|
||||||
@@ -574,6 +646,8 @@ func Memoize[A any](rdr ReaderIOEither[A]) ReaderIOEither[A] {
|
|||||||
// - rdr: The nested ReaderIOEither to flatten
|
// - rdr: The nested ReaderIOEither to flatten
|
||||||
//
|
//
|
||||||
// Returns a flattened ReaderIOEither.
|
// Returns a flattened ReaderIOEither.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Flatten[A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
|
func Flatten[A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
|
||||||
return readerioeither.Flatten(rdr)
|
return readerioeither.Flatten(rdr)
|
||||||
}
|
}
|
||||||
@@ -586,6 +660,8 @@ func Flatten[A any](rdr ReaderIOEither[ReaderIOEither[A]]) ReaderIOEither[A] {
|
|||||||
// - a: The value to apply to the function
|
// - a: The value to apply to the function
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither with the function applied to the value.
|
// Returns a ReaderIOEither with the function applied to the value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] {
|
func MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] {
|
||||||
return readerioeither.MonadFlap(fab, a)
|
return readerioeither.MonadFlap(fab, a)
|
||||||
}
|
}
|
||||||
@@ -597,6 +673,8 @@ func MonadFlap[B, A any](fab ReaderIOEither[func(A) B], a A) ReaderIOEither[B] {
|
|||||||
// - a: The value to apply to the function
|
// - a: The value to apply to the function
|
||||||
//
|
//
|
||||||
// Returns a function that applies the value to a ReaderIOEither function.
|
// Returns a function that applies the value to a ReaderIOEither function.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
||||||
return readerioeither.Flap[context.Context, error, B](a)
|
return readerioeither.Flap[context.Context, error, B](a)
|
||||||
}
|
}
|
||||||
@@ -609,7 +687,9 @@ func Flap[B, A any](a A) Operator[func(A) B, B] {
|
|||||||
// - onRight: Handler for success case
|
// - onRight: Handler for success case
|
||||||
//
|
//
|
||||||
// Returns a function that folds a ReaderIOEither into a new ReaderIOEither.
|
// Returns a function that folds a ReaderIOEither into a new ReaderIOEither.
|
||||||
func Fold[A, B any](onLeft func(error) ReaderIOEither[B], onRight func(A) ReaderIOEither[B]) Operator[A, B] {
|
//
|
||||||
|
//go:inline
|
||||||
|
func Fold[A, B any](onLeft Kleisli[error, B], onRight Kleisli[A, B]) Operator[A, B] {
|
||||||
return readerioeither.Fold(onLeft, onRight)
|
return readerioeither.Fold(onLeft, onRight)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,6 +700,8 @@ func Fold[A, B any](onLeft func(error) ReaderIOEither[B], onRight func(A) Reader
|
|||||||
// - onLeft: Function to provide a default value from the error
|
// - onLeft: Function to provide a default value from the error
|
||||||
//
|
//
|
||||||
// Returns a function that converts a ReaderIOEither to a ReaderIO.
|
// Returns a function that converts a ReaderIOEither to a ReaderIO.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func GetOrElse[A any](onLeft func(error) ReaderIO[A]) func(ReaderIOEither[A]) ReaderIO[A] {
|
func GetOrElse[A any](onLeft func(error) ReaderIO[A]) func(ReaderIOEither[A]) ReaderIO[A] {
|
||||||
return readerioeither.GetOrElse(onLeft)
|
return readerioeither.GetOrElse(onLeft)
|
||||||
}
|
}
|
||||||
@@ -631,6 +713,8 @@ func GetOrElse[A any](onLeft func(error) ReaderIO[A]) func(ReaderIOEither[A]) Re
|
|||||||
// - onLeft: Function to transform the error
|
// - onLeft: Function to transform the error
|
||||||
//
|
//
|
||||||
// Returns a function that transforms the error of a ReaderIOEither.
|
// Returns a function that transforms the error of a ReaderIOEither.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func OrLeft[A any](onLeft func(error) ReaderIO[error]) Operator[A, A] {
|
func OrLeft[A any](onLeft func(error) ReaderIO[error]) Operator[A, A] {
|
||||||
return readerioeither.OrLeft[A](onLeft)
|
return readerioeither.OrLeft[A](onLeft)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFromEither(t *testing.T) {
|
func TestFromEither(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
rightVal := E.Right[error](42)
|
rightVal := E.Right[error](42)
|
||||||
@@ -43,7 +43,7 @@ func TestFromEither(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLeftRight(t *testing.T) {
|
func TestLeftRight(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test Left
|
// Test Left
|
||||||
err := errors.New("test error")
|
err := errors.New("test error")
|
||||||
@@ -58,13 +58,13 @@ func TestLeftRight(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOf(t *testing.T) {
|
func TestOf(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
result := Of(42)(ctx)()
|
result := Of(42)(ctx)()
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
assert.Equal(t, E.Right[error](42), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadMap(t *testing.T) {
|
func TestMonadMap(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadMap(Right(42), func(x int) int { return x * 2 })(ctx)()
|
result := MonadMap(Right(42), func(x int) int { return x * 2 })(ctx)()
|
||||||
@@ -77,7 +77,7 @@ func TestMonadMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadMapTo(t *testing.T) {
|
func TestMonadMapTo(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadMapTo(Right(42), "hello")(ctx)()
|
result := MonadMapTo(Right(42), "hello")(ctx)()
|
||||||
@@ -90,7 +90,7 @@ func TestMonadMapTo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadChain(t *testing.T) {
|
func TestMonadChain(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadChain(Right(42), func(x int) ReaderIOEither[int] {
|
result := MonadChain(Right(42), func(x int) ReaderIOEither[int] {
|
||||||
@@ -113,7 +113,7 @@ func TestMonadChain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadChainFirst(t *testing.T) {
|
func TestMonadChainFirst(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadChainFirst(Right(42), func(x int) ReaderIOEither[string] {
|
result := MonadChainFirst(Right(42), func(x int) ReaderIOEither[string] {
|
||||||
@@ -136,7 +136,7 @@ func TestMonadChainFirst(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadApSeq(t *testing.T) {
|
func TestMonadApSeq(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with both Right
|
// Test with both Right
|
||||||
fct := Right(func(x int) int { return x * 2 })
|
fct := Right(func(x int) int { return x * 2 })
|
||||||
@@ -159,7 +159,7 @@ func TestMonadApSeq(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadApPar(t *testing.T) {
|
func TestMonadApPar(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with both Right
|
// Test with both Right
|
||||||
fct := Right(func(x int) int { return x * 2 })
|
fct := Right(func(x int) int { return x * 2 })
|
||||||
@@ -169,7 +169,7 @@ func TestMonadApPar(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFromPredicate(t *testing.T) {
|
func TestFromPredicate(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
pred := func(x int) bool { return x > 0 }
|
pred := func(x int) bool { return x > 0 }
|
||||||
onFalse := func(x int) error { return fmt.Errorf("value %d is not positive", x) }
|
onFalse := func(x int) error { return fmt.Errorf("value %d is not positive", x) }
|
||||||
@@ -184,7 +184,7 @@ func TestFromPredicate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAsk(t *testing.T) {
|
func TestAsk(t *testing.T) {
|
||||||
ctx := context.WithValue(context.Background(), "key", "value")
|
ctx := context.WithValue(t.Context(), "key", "value")
|
||||||
result := Ask()(ctx)()
|
result := Ask()(ctx)()
|
||||||
assert.True(t, E.IsRight(result))
|
assert.True(t, E.IsRight(result))
|
||||||
retrievedCtx, _ := E.Unwrap(result)
|
retrievedCtx, _ := E.Unwrap(result)
|
||||||
@@ -192,7 +192,7 @@ func TestAsk(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadChainEitherK(t *testing.T) {
|
func TestMonadChainEitherK(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadChainEitherK(Right(42), func(x int) E.Either[error, int] {
|
result := MonadChainEitherK(Right(42), func(x int) E.Either[error, int] {
|
||||||
@@ -208,7 +208,7 @@ func TestMonadChainEitherK(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadChainFirstEitherK(t *testing.T) {
|
func TestMonadChainFirstEitherK(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadChainFirstEitherK(Right(42), func(x int) E.Either[error, string] {
|
result := MonadChainFirstEitherK(Right(42), func(x int) E.Either[error, string] {
|
||||||
@@ -224,7 +224,7 @@ func TestMonadChainFirstEitherK(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestChainOptionKFunc(t *testing.T) {
|
func TestChainOptionKFunc(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
onNone := func() error { return errors.New("none error") }
|
onNone := func() error { return errors.New("none error") }
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ func TestChainOptionKFunc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFromIOEither(t *testing.T) {
|
func TestFromIOEither(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
ioe := func() E.Either[error, int] {
|
ioe := func() E.Either[error, int] {
|
||||||
@@ -262,7 +262,7 @@ func TestFromIOEither(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFromIO(t *testing.T) {
|
func TestFromIO(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
io := func() int { return 42 }
|
io := func() int { return 42 }
|
||||||
result := FromIO(io)(ctx)()
|
result := FromIO(io)(ctx)()
|
||||||
@@ -270,7 +270,7 @@ func TestFromIO(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFromLazy(t *testing.T) {
|
func TestFromLazy(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
lazy := func() int { return 42 }
|
lazy := func() int { return 42 }
|
||||||
result := FromLazy(lazy)(ctx)()
|
result := FromLazy(lazy)(ctx)()
|
||||||
@@ -278,7 +278,7 @@ func TestFromLazy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNeverWithCancel(t *testing.T) {
|
func TestNeverWithCancel(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(t.Context())
|
||||||
|
|
||||||
// Start Never in a goroutine
|
// Start Never in a goroutine
|
||||||
done := make(chan E.Either[error, int])
|
done := make(chan E.Either[error, int])
|
||||||
@@ -295,7 +295,7 @@ func TestNeverWithCancel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadChainIOK(t *testing.T) {
|
func TestMonadChainIOK(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadChainIOK(Right(42), func(x int) func() int {
|
result := MonadChainIOK(Right(42), func(x int) func() int {
|
||||||
@@ -305,7 +305,7 @@ func TestMonadChainIOK(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadChainFirstIOK(t *testing.T) {
|
func TestMonadChainFirstIOK(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right
|
// Test with Right
|
||||||
result := MonadChainFirstIOK(Right(42), func(x int) func() string {
|
result := MonadChainFirstIOK(Right(42), func(x int) func() string {
|
||||||
@@ -315,7 +315,7 @@ func TestMonadChainFirstIOK(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelayFunc(t *testing.T) {
|
func TestDelayFunc(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
delay := 100 * time.Millisecond
|
delay := 100 * time.Millisecond
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
@@ -328,7 +328,7 @@ func TestDelayFunc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefer(t *testing.T) {
|
func TestDefer(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
gen := func() ReaderIOEither[int] {
|
gen := func() ReaderIOEither[int] {
|
||||||
@@ -348,7 +348,7 @@ func TestDefer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTryCatch(t *testing.T) {
|
func TestTryCatch(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test success
|
// Test success
|
||||||
result := TryCatch(func(ctx context.Context) func() (int, error) {
|
result := TryCatch(func(ctx context.Context) func() (int, error) {
|
||||||
@@ -369,7 +369,7 @@ func TestTryCatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadAlt(t *testing.T) {
|
func TestMonadAlt(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with Right (alternative not called)
|
// Test with Right (alternative not called)
|
||||||
result := MonadAlt(Right(42), func() ReaderIOEither[int] {
|
result := MonadAlt(Right(42), func() ReaderIOEither[int] {
|
||||||
@@ -386,7 +386,7 @@ func TestMonadAlt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMemoize(t *testing.T) {
|
func TestMemoize(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
rdr := Memoize(FromLazy(func() int {
|
rdr := Memoize(FromLazy(func() int {
|
||||||
@@ -404,7 +404,7 @@ func TestMemoize(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFlatten(t *testing.T) {
|
func TestFlatten(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
nested := Right(Right(42))
|
nested := Right(Right(42))
|
||||||
result := Flatten(nested)(ctx)()
|
result := Flatten(nested)(ctx)()
|
||||||
@@ -412,7 +412,7 @@ func TestFlatten(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadFlap(t *testing.T) {
|
func TestMonadFlap(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
fab := Right(func(x int) int { return x * 2 })
|
fab := Right(func(x int) int { return x * 2 })
|
||||||
result := MonadFlap(fab, 42)(ctx)()
|
result := MonadFlap(fab, 42)(ctx)()
|
||||||
assert.Equal(t, E.Right[error](84), result)
|
assert.Equal(t, E.Right[error](84), result)
|
||||||
@@ -420,19 +420,19 @@ func TestMonadFlap(t *testing.T) {
|
|||||||
|
|
||||||
func TestWithContext(t *testing.T) {
|
func TestWithContext(t *testing.T) {
|
||||||
// Test with non-canceled context
|
// Test with non-canceled context
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
result := WithContext(Right(42))(ctx)()
|
result := WithContext(Right(42))(ctx)()
|
||||||
assert.Equal(t, E.Right[error](42), result)
|
assert.Equal(t, E.Right[error](42), result)
|
||||||
|
|
||||||
// Test with canceled context
|
// Test with canceled context
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(t.Context())
|
||||||
cancel()
|
cancel()
|
||||||
result = WithContext(Right(42))(ctx)()
|
result = WithContext(Right(42))(ctx)()
|
||||||
assert.True(t, E.IsLeft(result))
|
assert.True(t, E.IsLeft(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMonadAp(t *testing.T) {
|
func TestMonadAp(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with both Right
|
// Test with both Right
|
||||||
fct := Right(func(x int) int { return x * 2 })
|
fct := Right(func(x int) int { return x * 2 })
|
||||||
@@ -443,7 +443,7 @@ func TestMonadAp(t *testing.T) {
|
|||||||
|
|
||||||
// Test traverse functions
|
// Test traverse functions
|
||||||
func TestSequenceArray(t *testing.T) {
|
func TestSequenceArray(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with all Right
|
// Test with all Right
|
||||||
arr := []ReaderIOEither[int]{Right(1), Right(2), Right(3)}
|
arr := []ReaderIOEither[int]{Right(1), Right(2), Right(3)}
|
||||||
@@ -460,7 +460,7 @@ func TestSequenceArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTraverseArray(t *testing.T) {
|
func TestTraverseArray(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test transformation
|
// Test transformation
|
||||||
arr := []int{1, 2, 3}
|
arr := []int{1, 2, 3}
|
||||||
@@ -473,7 +473,7 @@ func TestTraverseArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSequenceRecord(t *testing.T) {
|
func TestSequenceRecord(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test with all Right
|
// Test with all Right
|
||||||
rec := map[string]ReaderIOEither[int]{
|
rec := map[string]ReaderIOEither[int]{
|
||||||
@@ -488,7 +488,7 @@ func TestSequenceRecord(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTraverseRecord(t *testing.T) {
|
func TestTraverseRecord(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
// Test transformation
|
// Test transformation
|
||||||
rec := map[string]int{"a": 1, "b": 2}
|
rec := map[string]int{"a": 1, "b": 2}
|
||||||
@@ -503,7 +503,7 @@ func TestTraverseRecord(t *testing.T) {
|
|||||||
|
|
||||||
// Test monoid functions
|
// Test monoid functions
|
||||||
func TestAltSemigroup(t *testing.T) {
|
func TestAltSemigroup(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
sg := AltSemigroup[int]()
|
sg := AltSemigroup[int]()
|
||||||
|
|
||||||
@@ -519,7 +519,7 @@ func TestAltSemigroup(t *testing.T) {
|
|||||||
|
|
||||||
// Test Do notation
|
// Test Do notation
|
||||||
func TestDo(t *testing.T) {
|
func TestDo(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Value int
|
Value int
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ import (
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
// })
|
// })
|
||||||
func WithResource[A, R, ANY any](onCreate ReaderIOEither[R], onRelease func(R) ReaderIOEither[ANY]) func(func(R) ReaderIOEither[A]) ReaderIOEither[A] {
|
func WithResource[A, R, ANY any](onCreate ReaderIOEither[R], onRelease func(R) ReaderIOEither[ANY]) Kleisli[Kleisli[R, A], A] {
|
||||||
return function.Flow2(
|
return function.Flow2(
|
||||||
function.Bind2nd(function.Flow2[func(R) ReaderIOEither[A], Operator[A, A], R, ReaderIOEither[A], ReaderIOEither[A]], WithContext[A]),
|
function.Bind2nd(function.Flow2[func(R) ReaderIOEither[A], Operator[A, A], R, ReaderIOEither[A], ReaderIOEither[A]], WithContext[A]),
|
||||||
RIE.WithResource[A, context.Context, error, R](WithContext(onCreate), onRelease),
|
RIE.WithResource[A, context.Context, error, R](WithContext(onCreate), onRelease),
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
||||||
func TraverseArray[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return array.Traverse[[]A](
|
return array.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -45,7 +45,7 @@ func TraverseArray[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEith
|
|||||||
// - f: Function that transforms each element with its index into a ReaderIOEither
|
// - f: Function that transforms each element with its index into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []B] {
|
||||||
return array.TraverseWithIndex[[]A](
|
return array.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -72,7 +72,7 @@ func SequenceArray[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
|||||||
// - f: Function that transforms each value into a ReaderIOEither
|
// - f: Function that transforms each value into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a function that transforms a map into a ReaderIOEither of a map.
|
// Returns a function that transforms a map into a ReaderIOEither of a map.
|
||||||
func TraverseRecord[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.Traverse[map[K]A](
|
return record.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -89,7 +89,7 @@ func TraverseRecord[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(ma
|
|||||||
// - f: Function that transforms each key-value pair into a ReaderIOEither
|
// - f: Function that transforms each key-value pair into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a function that transforms a map into a ReaderIOEither of a map.
|
// Returns a function that transforms a map into a ReaderIOEither of a map.
|
||||||
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.TraverseWithIndex[map[K]A](
|
return record.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -117,7 +117,7 @@ func SequenceRecord[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIOEit
|
|||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing an array of transformed values.
|
// Returns a ReaderIOEither containing an array of transformed values.
|
||||||
func MonadTraverseArraySeq[A, B any](as []A, f func(A) ReaderIOEither[B]) ReaderIOEither[[]B] {
|
func MonadTraverseArraySeq[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B] {
|
||||||
return array.MonadTraverse[[]A](
|
return array.MonadTraverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -134,7 +134,7 @@ func MonadTraverseArraySeq[A, B any](as []A, f func(A) ReaderIOEither[B]) Reader
|
|||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
||||||
func TraverseArraySeq[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return array.Traverse[[]A](
|
return array.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -145,7 +145,7 @@ func TraverseArraySeq[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOE
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||||
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []B] {
|
||||||
return array.TraverseWithIndex[[]A](
|
return array.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -167,7 +167,7 @@ func SequenceArraySeq[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MonadTraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// MonadTraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
func MonadTraverseRecordSeq[K comparable, A, B any](as map[K]A, f func(A) ReaderIOEither[B]) ReaderIOEither[map[K]B] {
|
func MonadTraverseRecordSeq[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) ReaderIOEither[map[K]B] {
|
||||||
return record.MonadTraverse[map[K]A](
|
return record.MonadTraverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -178,7 +178,7 @@ func MonadTraverseRecordSeq[K comparable, A, B any](as map[K]A, f func(A) Reader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
func TraverseRecordSeq[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
func TraverseRecordSeq[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.Traverse[map[K]A](
|
return record.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -189,7 +189,7 @@ func TraverseRecordSeq[K comparable, A, B any](f func(A) ReaderIOEither[B]) func
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordWithIndexSeq uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
func TraverseRecordWithIndexSeq[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.TraverseWithIndex[map[K]A](
|
return record.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -212,7 +212,7 @@ func SequenceRecordSeq[K comparable, A any](ma map[K]ReaderIOEither[A]) ReaderIO
|
|||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a ReaderIOEither containing an array of transformed values.
|
// Returns a ReaderIOEither containing an array of transformed values.
|
||||||
func MonadTraverseArrayPar[A, B any](as []A, f func(A) ReaderIOEither[B]) ReaderIOEither[[]B] {
|
func MonadTraverseArrayPar[A, B any](as []A, f Kleisli[A, B]) ReaderIOEither[[]B] {
|
||||||
return array.MonadTraverse[[]A](
|
return array.MonadTraverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -229,7 +229,7 @@ func MonadTraverseArrayPar[A, B any](as []A, f func(A) ReaderIOEither[B]) Reader
|
|||||||
// - f: Function that transforms each element into a ReaderIOEither
|
// - f: Function that transforms each element into a ReaderIOEither
|
||||||
//
|
//
|
||||||
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
// Returns a function that transforms an array into a ReaderIOEither of an array.
|
||||||
func TraverseArrayPar[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return array.Traverse[[]A](
|
return array.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -240,7 +240,7 @@ func TraverseArrayPar[A, B any](f func(A) ReaderIOEither[B]) func([]A) ReaderIOE
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[[]B]]
|
||||||
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) func([]A) ReaderIOEither[[]B] {
|
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOEither[B]) Kleisli[[]A, []B] {
|
||||||
return array.TraverseWithIndex[[]A](
|
return array.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -262,7 +262,7 @@ func SequenceArrayPar[A any](ma []ReaderIOEither[A]) ReaderIOEither[[]A] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
func TraverseRecordPar[K comparable, A, B any](f func(A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
func TraverseRecordPar[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.Traverse[map[K]A](
|
return record.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -273,7 +273,7 @@ func TraverseRecordPar[K comparable, A, B any](f func(A) ReaderIOEither[B]) func
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// TraverseRecordWithIndexPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) func(map[K]A) ReaderIOEither[map[K]B] {
|
func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEither[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return record.TraverseWithIndex[map[K]A](
|
return record.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -284,7 +284,7 @@ func TraverseRecordWithIndexPar[K comparable, A, B any](f func(K, A) ReaderIOEit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MonadTraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
// MonadTraverseRecordPar uses transforms a record [map[K]A] into [map[K]ReaderIOEither[B]] and then resolves that into a [ReaderIOEither[map[K]B]]
|
||||||
func MonadTraverseRecordPar[K comparable, A, B any](as map[K]A, f func(A) ReaderIOEither[B]) ReaderIOEither[map[K]B] {
|
func MonadTraverseRecordPar[K comparable, A, B any](as map[K]A, f Kleisli[A, B]) ReaderIOEither[map[K]B] {
|
||||||
return record.MonadTraverse[map[K]A](
|
return record.MonadTraverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
|
|||||||
@@ -99,10 +99,12 @@ type (
|
|||||||
// result := fetchUser("123")(ctx)()
|
// result := fetchUser("123")(ctx)()
|
||||||
ReaderIOEither[A any] = readerioeither.ReaderIOEither[context.Context, error, A]
|
ReaderIOEither[A any] = readerioeither.ReaderIOEither[context.Context, error, A]
|
||||||
|
|
||||||
|
Kleisli[A, B any] = reader.Reader[A, ReaderIOEither[B]]
|
||||||
|
|
||||||
// Operator represents a transformation from one ReaderIOEither to another.
|
// Operator represents a transformation from one ReaderIOEither to another.
|
||||||
// This is useful for point-free style composition and building reusable transformations.
|
// This is useful for point-free style composition and building reusable transformations.
|
||||||
//
|
//
|
||||||
// Operator[A, B] is equivalent to func(ReaderIOEither[A]) ReaderIOEither[B]
|
// Operator[A, B] is equivalent to Kleisli[ReaderIOEither[A], B]
|
||||||
//
|
//
|
||||||
// Example usage:
|
// Example usage:
|
||||||
// // Define a reusable transformation
|
// // Define a reusable transformation
|
||||||
@@ -110,5 +112,5 @@ type (
|
|||||||
//
|
//
|
||||||
// // Apply the transformation
|
// // Apply the transformation
|
||||||
// result := toUpper(computation)
|
// result := toUpper(computation)
|
||||||
Operator[A, B any] = Reader[ReaderIOEither[A], ReaderIOEither[B]]
|
Operator[A, B any] = Kleisli[ReaderIOEither[A], B]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package either
|
package either
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/function"
|
||||||
A "github.com/IBM/fp-go/v2/internal/apply"
|
A "github.com/IBM/fp-go/v2/internal/apply"
|
||||||
C "github.com/IBM/fp-go/v2/internal/chain"
|
C "github.com/IBM/fp-go/v2/internal/chain"
|
||||||
F "github.com/IBM/fp-go/v2/internal/functor"
|
F "github.com/IBM/fp-go/v2/internal/functor"
|
||||||
@@ -171,3 +172,204 @@ func ApS[E, S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 and enables working with
|
||||||
|
// nested fields in a type-safe manner.
|
||||||
|
//
|
||||||
|
// Unlike BindL, ApSL uses applicative semantics, meaning the computation fa is independent
|
||||||
|
// of the current state and can be evaluated concurrently.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - E: Error type for the Either
|
||||||
|
// - S: Structure type containing the field to update
|
||||||
|
// - T: Type of the field being updated
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||||
|
// - fa: An Either[E, T] computation that produces the value to set
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - An endomorphism that updates the focused field in the Either context
|
||||||
|
//
|
||||||
|
// 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 },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// either.Right[error](Person{Name: "Alice", Age: 25}),
|
||||||
|
// either.ApSL(ageLens, either.Right[error](30)),
|
||||||
|
// ) // Right(Person{Name: "Alice", Age: 30})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApSL[E, S, T any](
|
||||||
|
lens Lens[S, T],
|
||||||
|
fa Either[E, T],
|
||||||
|
) Endomorphism[Either[E, S]] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindL attaches the result of a computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Bind with a lens, allowing you to use
|
||||||
|
// optics to update nested structures based on their current values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The computation function f receives the current value of the focused field and returns
|
||||||
|
// an Either that produces the new value.
|
||||||
|
//
|
||||||
|
// Unlike ApSL, BindL uses monadic sequencing, meaning the computation f can depend on
|
||||||
|
// the current value of the focused field.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - E: Error type for the Either
|
||||||
|
// - S: Structure type containing the field to update
|
||||||
|
// - T: Type of the field being updated
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||||
|
// - f: A function that takes the current field value and returns an Either[E, T]
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - An endomorphism that updates the focused field based on its current value
|
||||||
|
//
|
||||||
|
// 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 the counter, but fail if it would exceed 100
|
||||||
|
// increment := func(v int) either.Either[error, int] {
|
||||||
|
// if v >= 100 {
|
||||||
|
// return either.Left[int](errors.New("counter overflow"))
|
||||||
|
// }
|
||||||
|
// return either.Right[error](v + 1)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// either.Right[error](Counter{Value: 42}),
|
||||||
|
// either.BindL(valueLens, increment),
|
||||||
|
// ) // Right(Counter{Value: 43})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindL[E, S, T any](
|
||||||
|
lens Lens[S, T],
|
||||||
|
f func(T) Either[E, T],
|
||||||
|
) Endomorphism[Either[E, S]] {
|
||||||
|
return Bind[E, S, S, T](lens.Set, function.Flow2(lens.Get, f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Let with a lens, allowing you to use
|
||||||
|
// optics to update nested structures with pure transformations.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The transformation function f receives the current value of the focused field and returns
|
||||||
|
// the new value directly (not wrapped in Either).
|
||||||
|
//
|
||||||
|
// This is useful for pure transformations that cannot fail, such as mathematical operations,
|
||||||
|
// string manipulations, or other deterministic updates.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - E: Error type for the Either
|
||||||
|
// - S: Structure type containing the field to update
|
||||||
|
// - T: Type of the field being updated
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||||
|
// - f: An endomorphism (T → T) that transforms the current field value
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - An endomorphism that updates the focused field with the transformed value
|
||||||
|
//
|
||||||
|
// 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 the counter value
|
||||||
|
// double := func(v int) int { return v * 2 }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// either.Right[error](Counter{Value: 21}),
|
||||||
|
// either.LetL(valueLens, double),
|
||||||
|
// ) // Right(Counter{Value: 42})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetL[E, S, T any](
|
||||||
|
lens Lens[S, T],
|
||||||
|
f Endomorphism[T],
|
||||||
|
) Endomorphism[Either[E, S]] {
|
||||||
|
return Let[E, S, S, T](lens.Set, function.Flow2(lens.Get, f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines LetTo with a lens, allowing you to use
|
||||||
|
// optics to set nested fields to specific values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides the setter for a field within the structure S.
|
||||||
|
// Unlike LetL which transforms the current value, LetToL simply replaces it with
|
||||||
|
// the provided constant value b.
|
||||||
|
//
|
||||||
|
// This is useful for resetting fields, initializing values, or setting fields to
|
||||||
|
// predetermined constants.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - E: Error type for the Either
|
||||||
|
// - S: Structure type containing the field to update
|
||||||
|
// - T: Type of the field being updated
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lens: A Lens[S, T] that focuses on a field of type T within structure S
|
||||||
|
// - b: The constant value to set the field to
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - An endomorphism that sets the focused field to the constant value
|
||||||
|
//
|
||||||
|
// 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(
|
||||||
|
// either.Right[error](Config{Debug: true, Timeout: 30}),
|
||||||
|
// either.LetToL(debugLens, false),
|
||||||
|
// ) // Right(Config{Debug: false, Timeout: 30})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetToL[E, S, T any](
|
||||||
|
lens Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Endomorphism[Either[E, S]] {
|
||||||
|
return LetTo[E, S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/IBM/fp-go/v2/internal/utils"
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,3 +55,307 @@ func TestApS(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, res, Of[error]("John Doe"))
|
assert.Equal(t, res, Of[error]("John Doe"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test types for lens-based operations
|
||||||
|
type Counter struct {
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Debug bool
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApSL(t *testing.T) {
|
||||||
|
// Create a lens for the Age field
|
||||||
|
ageLens := L.MakeLens(
|
||||||
|
func(p Person) int { return p.Age },
|
||||||
|
func(p Person, a int) Person { p.Age = a; return p },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("ApSL with Right value", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Person{Name: "Alice", Age: 25}),
|
||||||
|
ApSL(ageLens, Right[error](30)),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Person{Name: "Alice", Age: 30})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ApSL with Left in context", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Left[Person](assert.AnError),
|
||||||
|
ApSL(ageLens, Right[error](30)),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Left[Person](assert.AnError)
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ApSL with Left in value", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Person{Name: "Alice", Age: 25}),
|
||||||
|
ApSL(ageLens, Left[int](assert.AnError)),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Left[Person](assert.AnError)
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ApSL with both Left", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Left[Person](assert.AnError),
|
||||||
|
ApSL(ageLens, Left[int](assert.AnError)),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Left[Person](assert.AnError)
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindL(t *testing.T) {
|
||||||
|
// Create a lens for the Value field
|
||||||
|
valueLens := L.MakeLens(
|
||||||
|
func(c Counter) int { return c.Value },
|
||||||
|
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("BindL with successful transformation", func(t *testing.T) {
|
||||||
|
// Increment the counter, but fail if it would exceed 100
|
||||||
|
increment := func(v int) Either[error, int] {
|
||||||
|
if v >= 100 {
|
||||||
|
return Left[int](assert.AnError)
|
||||||
|
}
|
||||||
|
return Right[error](v + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Counter{Value: 42}),
|
||||||
|
BindL(valueLens, increment),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 43})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("BindL with failing transformation", func(t *testing.T) {
|
||||||
|
increment := func(v int) Either[error, int] {
|
||||||
|
if v >= 100 {
|
||||||
|
return Left[int](assert.AnError)
|
||||||
|
}
|
||||||
|
return Right[error](v + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Counter{Value: 100}),
|
||||||
|
BindL(valueLens, increment),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Left[Counter](assert.AnError)
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("BindL with Left input", func(t *testing.T) {
|
||||||
|
increment := func(v int) Either[error, int] {
|
||||||
|
return Right[error](v + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Left[Counter](assert.AnError),
|
||||||
|
BindL(valueLens, increment),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Left[Counter](assert.AnError)
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("BindL with multiple operations", func(t *testing.T) {
|
||||||
|
double := func(v int) Either[error, int] {
|
||||||
|
return Right[error](v * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTen := func(v int) Either[error, int] {
|
||||||
|
return Right[error](v + 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Right[error](Counter{Value: 5}),
|
||||||
|
BindL(valueLens, double),
|
||||||
|
BindL(valueLens, addTen),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 20})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLetL(t *testing.T) {
|
||||||
|
// Create a lens for the Value field
|
||||||
|
valueLens := L.MakeLens(
|
||||||
|
func(c Counter) int { return c.Value },
|
||||||
|
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("LetL with pure transformation", func(t *testing.T) {
|
||||||
|
double := func(v int) int { return v * 2 }
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Counter{Value: 21}),
|
||||||
|
LetL[error](valueLens, double),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 42})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetL with Left input", func(t *testing.T) {
|
||||||
|
double := func(v int) int { return v * 2 }
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Left[Counter](assert.AnError),
|
||||||
|
LetL[error](valueLens, double),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Left[Counter](assert.AnError)
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetL with multiple transformations", func(t *testing.T) {
|
||||||
|
double := func(v int) int { return v * 2 }
|
||||||
|
addTen := func(v int) int { return v + 10 }
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Right[error](Counter{Value: 5}),
|
||||||
|
LetL[error](valueLens, double),
|
||||||
|
LetL[error](valueLens, addTen),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 20})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetL with identity transformation", func(t *testing.T) {
|
||||||
|
identity := func(v int) int { return v }
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Counter{Value: 42}),
|
||||||
|
LetL[error](valueLens, identity),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 42})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLetToL(t *testing.T) {
|
||||||
|
// Create a lens for the Debug field
|
||||||
|
debugLens := L.MakeLens(
|
||||||
|
func(c Config) bool { return c.Debug },
|
||||||
|
func(c Config, d bool) Config { c.Debug = d; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("LetToL with constant value", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Config{Debug: true, Timeout: 30}),
|
||||||
|
LetToL[error](debugLens, false),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Config{Debug: false, Timeout: 30})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetToL with Left input", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Left[Config](assert.AnError),
|
||||||
|
LetToL[error](debugLens, false),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Left[Config](assert.AnError)
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetToL with multiple fields", func(t *testing.T) {
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) int { return c.Timeout },
|
||||||
|
func(c Config, t int) Config { c.Timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Right[error](Config{Debug: true, Timeout: 30}),
|
||||||
|
LetToL[error](debugLens, false),
|
||||||
|
LetToL[error](timeoutLens, 60),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Config{Debug: false, Timeout: 60})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetToL setting same value", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Right[error](Config{Debug: false, Timeout: 30}),
|
||||||
|
LetToL[error](debugLens, false),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Config{Debug: false, Timeout: 30})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLensOperationsCombined(t *testing.T) {
|
||||||
|
// Test combining different lens operations
|
||||||
|
valueLens := L.MakeLens(
|
||||||
|
func(c Counter) int { return c.Value },
|
||||||
|
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("Combine LetToL and LetL", func(t *testing.T) {
|
||||||
|
double := func(v int) int { return v * 2 }
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Right[error](Counter{Value: 100}),
|
||||||
|
LetToL[error](valueLens, 10),
|
||||||
|
LetL[error](valueLens, double),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 20})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Combine LetL and BindL", func(t *testing.T) {
|
||||||
|
double := func(v int) int { return v * 2 }
|
||||||
|
validate := func(v int) Either[error, int] {
|
||||||
|
if v > 100 {
|
||||||
|
return Left[int](assert.AnError)
|
||||||
|
}
|
||||||
|
return Right[error](v)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Right[error](Counter{Value: 25}),
|
||||||
|
LetL[error](valueLens, double),
|
||||||
|
BindL(valueLens, validate),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 50})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Combine ApSL and LetL", func(t *testing.T) {
|
||||||
|
addFive := func(v int) int { return v + 5 }
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Right[error](Counter{Value: 10}),
|
||||||
|
ApSL(valueLens, Right[error](20)),
|
||||||
|
LetL[error](valueLens, addFive),
|
||||||
|
)
|
||||||
|
|
||||||
|
expected := Right[error](Counter{Value: 25})
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func MonadAp[B, E, A any](fab Either[E, func(a A) B], fa Either[E, A]) Either[E,
|
|||||||
|
|
||||||
// Ap is the curried version of [MonadAp].
|
// Ap is the curried version of [MonadAp].
|
||||||
// Returns a function that applies a wrapped function to the given wrapped value.
|
// Returns a function that applies a wrapped function to the given wrapped value.
|
||||||
func Ap[B, E, A any](fa Either[E, A]) func(fab Either[E, func(a A) B]) Either[E, B] {
|
func Ap[B, E, A any](fa Either[E, A]) Operator[E, func(A) B, B] {
|
||||||
return F.Bind2nd(MonadAp[B, E, A], fa)
|
return F.Bind2nd(MonadAp[B, E, A], fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ func MonadMapTo[E, A, B any](fa Either[E, A], b B) Either[E, B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MapTo is the curried version of [MonadMapTo].
|
// MapTo is the curried version of [MonadMapTo].
|
||||||
func MapTo[E, A, B any](b B) func(Either[E, A]) Either[E, B] {
|
func MapTo[E, A, B any](b B) Operator[E, A, B] {
|
||||||
return Map[E](F.Constant1[A](b))
|
return Map[E](F.Constant1[A](b))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,26 +211,26 @@ func MonadChainOptionK[A, B, E any](onNone func() E, ma Either[E, A], f func(A)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ChainOptionK is the curried version of [MonadChainOptionK].
|
// ChainOptionK is the curried version of [MonadChainOptionK].
|
||||||
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) Option[B]) func(Either[E, A]) Either[E, B] {
|
func ChainOptionK[A, B, E any](onNone func() E) func(func(A) Option[B]) Operator[E, A, B] {
|
||||||
from := FromOption[B](onNone)
|
from := FromOption[B](onNone)
|
||||||
return func(f func(A) Option[B]) func(Either[E, A]) Either[E, B] {
|
return func(f func(A) Option[B]) Operator[E, A, B] {
|
||||||
return Chain(F.Flow2(f, from))
|
return Chain(F.Flow2(f, from))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainTo is the curried version of [MonadChainTo].
|
// ChainTo is the curried version of [MonadChainTo].
|
||||||
func ChainTo[A, E, B any](mb Either[E, B]) func(Either[E, A]) Either[E, B] {
|
func ChainTo[A, E, B any](mb Either[E, B]) Operator[E, A, B] {
|
||||||
return F.Constant1[Either[E, A]](mb)
|
return F.Constant1[Either[E, A]](mb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chain is the curried version of [MonadChain].
|
// Chain is the curried version of [MonadChain].
|
||||||
// Sequences two computations where the second depends on the first.
|
// Sequences two computations where the second depends on the first.
|
||||||
func Chain[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, B] {
|
func Chain[E, A, B any](f func(a A) Either[E, B]) Operator[E, A, B] {
|
||||||
return Fold(Left[B, E], f)
|
return Fold(Left[B, E], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainFirst is the curried version of [MonadChainFirst].
|
// ChainFirst is the curried version of [MonadChainFirst].
|
||||||
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) func(Either[E, A]) Either[E, A] {
|
func ChainFirst[E, A, B any](f func(a A) Either[E, B]) Operator[E, A, A] {
|
||||||
return C.ChainFirst(
|
return C.ChainFirst(
|
||||||
Chain[E, A, A],
|
Chain[E, A, A],
|
||||||
Map[E, B, A],
|
Map[E, B, A],
|
||||||
@@ -437,7 +437,7 @@ func AltW[E, E1, A any](that L.Lazy[Either[E1, A]]) func(Either[E, A]) Either[E1
|
|||||||
// return either.Right[error](99)
|
// return either.Right[error](99)
|
||||||
// })
|
// })
|
||||||
// result := alternative(either.Left[int](errors.New("fail"))) // Right(99)
|
// result := alternative(either.Left[int](errors.New("fail"))) // Right(99)
|
||||||
func Alt[E, A any](that L.Lazy[Either[E, A]]) func(Either[E, A]) Either[E, A] {
|
func Alt[E, A any](that L.Lazy[Either[E, A]]) Operator[E, A, A] {
|
||||||
return AltW[E](that)
|
return AltW[E](that)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,7 +449,7 @@ func Alt[E, A any](that L.Lazy[Either[E, A]]) func(Either[E, A]) Either[E, A] {
|
|||||||
// return either.Right[error](0) // default value
|
// return either.Right[error](0) // default value
|
||||||
// })
|
// })
|
||||||
// result := recover(either.Left[int](errors.New("fail"))) // Right(0)
|
// result := recover(either.Left[int](errors.New("fail"))) // Right(0)
|
||||||
func OrElse[E, A any](onLeft func(e E) Either[E, A]) func(Either[E, A]) Either[E, A] {
|
func OrElse[E, A any](onLeft func(e E) Either[E, A]) Operator[E, A, A] {
|
||||||
return Fold(onLeft, Of[E, A])
|
return Fold(onLeft, Of[E, A])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,7 +518,7 @@ func MonadFlap[E, B, A any](fab Either[E, func(A) B], a A) Either[E, B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Flap is the curried version of [MonadFlap].
|
// Flap is the curried version of [MonadFlap].
|
||||||
func Flap[E, B, A any](a A) func(Either[E, func(A) B]) Either[E, B] {
|
func Flap[E, B, A any](a A) Operator[E, func(A) B, B] {
|
||||||
return FC.Flap(Map[E, func(A) B, B], a)
|
return FC.Flap(Map[E, func(A) B, B], a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
type eitherFunctor[E, A, B any] struct{}
|
type eitherFunctor[E, A, B any] struct{}
|
||||||
|
|
||||||
func (o *eitherFunctor[E, A, B]) Map(f func(A) B) func(Either[E, A]) Either[E, B] {
|
func (o *eitherFunctor[E, A, B]) Map(f func(A) B) Operator[E, A, B] {
|
||||||
return Map[E, A, B](f)
|
return Map[E, A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
L "github.com/IBM/fp-go/v2/logging"
|
L "github.com/IBM/fp-go/v2/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func _log[E, A any](left func(string, ...any), right func(string, ...any), prefix string) func(Either[E, A]) Either[E, A] {
|
func _log[E, A any](left func(string, ...any), right func(string, ...any), prefix string) Operator[E, A, A] {
|
||||||
return Fold(
|
return Fold(
|
||||||
func(e E) Either[E, A] {
|
func(e E) Either[E, A] {
|
||||||
left("%s: %v", prefix, e)
|
left("%s: %v", prefix, e)
|
||||||
@@ -50,9 +50,9 @@ func _log[E, A any](left func(string, ...any), right func(string, ...any), prefi
|
|||||||
// )
|
// )
|
||||||
// // Logs: "Processing: 42"
|
// // Logs: "Processing: 42"
|
||||||
// // result is Right(84)
|
// // result is Right(84)
|
||||||
func Logger[E, A any](loggers ...*log.Logger) func(string) func(Either[E, A]) Either[E, A] {
|
func Logger[E, A any](loggers ...*log.Logger) func(string) Operator[E, A, A] {
|
||||||
left, right := L.LoggingCallbacks(loggers...)
|
left, right := L.LoggingCallbacks(loggers...)
|
||||||
return func(prefix string) func(Either[E, A]) Either[E, A] {
|
return func(prefix string) Operator[E, A, A] {
|
||||||
delegate := _log[E, A](left, right, prefix)
|
delegate := _log[E, A](left, right, prefix)
|
||||||
return func(ma Either[E, A]) Either[E, A] {
|
return func(ma Either[E, A]) Either[E, A] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ func (o *eitherMonad[E, A, B]) Of(a A) Either[E, A] {
|
|||||||
return Of[E, A](a)
|
return Of[E, A](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *eitherMonad[E, A, B]) Map(f func(A) B) func(Either[E, A]) Either[E, B] {
|
func (o *eitherMonad[E, A, B]) Map(f func(A) B) Operator[E, A, B] {
|
||||||
return Map[E, A, B](f)
|
return Map[E, A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *eitherMonad[E, A, B]) Chain(f func(A) Either[E, B]) func(Either[E, A]) Either[E, B] {
|
func (o *eitherMonad[E, A, B]) Chain(f func(A) Either[E, B]) Operator[E, A, B] {
|
||||||
return Chain[E, A, B](f)
|
return Chain[E, A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *eitherMonad[E, A, B]) Ap(fa Either[E, A]) func(Either[E, func(A) B]) Either[E, B] {
|
func (o *eitherMonad[E, A, B]) Ap(fa Either[E, A]) Operator[E, func(A) B, B] {
|
||||||
return Ap[B, E, A](fa)
|
return Ap[B, E, A](fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
// m := either.AlternativeMonoid[error](intAdd)
|
// m := either.AlternativeMonoid[error](intAdd)
|
||||||
// result := m.Concat(either.Right[error](1), either.Right[error](2))
|
// result := m.Concat(either.Right[error](1), either.Right[error](2))
|
||||||
// // result is Right(3)
|
// // result is Right(3)
|
||||||
func AlternativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
func AlternativeMonoid[E, A any](m M.Monoid[A]) Monoid[E, A] {
|
||||||
return M.AlternativeMonoid(
|
return M.AlternativeMonoid(
|
||||||
Of[E, A],
|
Of[E, A],
|
||||||
MonadMap[E, A, func(A) A],
|
MonadMap[E, A, func(A) A],
|
||||||
@@ -51,7 +51,7 @@ func AlternativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
|||||||
// m := either.AltMonoid[error, int](zero)
|
// m := either.AltMonoid[error, int](zero)
|
||||||
// result := m.Concat(either.Left[int](errors.New("err1")), either.Right[error](42))
|
// result := m.Concat(either.Left[int](errors.New("err1")), either.Right[error](42))
|
||||||
// // result is Right(42)
|
// // result is Right(42)
|
||||||
func AltMonoid[E, A any](zero L.Lazy[Either[E, A]]) M.Monoid[Either[E, A]] {
|
func AltMonoid[E, A any](zero L.Lazy[Either[E, A]]) Monoid[E, A] {
|
||||||
return M.AltMonoid(
|
return M.AltMonoid(
|
||||||
zero,
|
zero,
|
||||||
MonadAlt[E, A],
|
MonadAlt[E, A],
|
||||||
|
|||||||
@@ -15,10 +15,22 @@
|
|||||||
|
|
||||||
package either
|
package either
|
||||||
|
|
||||||
import "github.com/IBM/fp-go/v2/option"
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/endomorphism"
|
||||||
|
"github.com/IBM/fp-go/v2/monoid"
|
||||||
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
|
)
|
||||||
|
|
||||||
// Option is a type alias for option.Option, provided for convenience
|
// Option is a type alias for option.Option, provided for convenience
|
||||||
// when working with Either and Option together.
|
// when working with Either and Option together.
|
||||||
type (
|
type (
|
||||||
Option[A any] = option.Option[A]
|
Option[A any] = option.Option[A]
|
||||||
|
Lens[S, T any] = lens.Lens[S, T]
|
||||||
|
Endomorphism[T any] = endomorphism.Endomorphism[T]
|
||||||
|
|
||||||
|
Kleisli[E, A, B any] = reader.Reader[A, Either[E, B]]
|
||||||
|
Operator[E, A, B any] = Kleisli[E, Either[E, A], B]
|
||||||
|
Monoid[E, A any] = monoid.Monoid[Either[E, A]]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import (
|
|||||||
A "github.com/IBM/fp-go/v2/array"
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
ENDO "github.com/IBM/fp-go/v2/endomorphism"
|
ENDO "github.com/IBM/fp-go/v2/endomorphism"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
|
||||||
LA "github.com/IBM/fp-go/v2/optics/lens/array"
|
LA "github.com/IBM/fp-go/v2/optics/lens/array"
|
||||||
|
LO "github.com/IBM/fp-go/v2/optics/lens/option"
|
||||||
LRG "github.com/IBM/fp-go/v2/optics/lens/record/generic"
|
LRG "github.com/IBM/fp-go/v2/optics/lens/record/generic"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
RG "github.com/IBM/fp-go/v2/record/generic"
|
RG "github.com/IBM/fp-go/v2/record/generic"
|
||||||
@@ -50,7 +50,7 @@ var (
|
|||||||
|
|
||||||
composeHead = F.Pipe1(
|
composeHead = F.Pipe1(
|
||||||
LA.AtHead[string](),
|
LA.AtHead[string](),
|
||||||
L.ComposeOptions[url.Values, string](A.Empty[string]()),
|
LO.Compose[url.Values, string](A.Empty[string]()),
|
||||||
)
|
)
|
||||||
|
|
||||||
// AtValue is a [L.Lens] that focusses on first value in form fields
|
// AtValue is a [L.Lens] that focusses on first value in form fields
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ import (
|
|||||||
|
|
||||||
A "github.com/IBM/fp-go/v2/array"
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
|
||||||
LA "github.com/IBM/fp-go/v2/optics/lens/array"
|
LA "github.com/IBM/fp-go/v2/optics/lens/array"
|
||||||
|
LO "github.com/IBM/fp-go/v2/optics/lens/option"
|
||||||
LRG "github.com/IBM/fp-go/v2/optics/lens/record/generic"
|
LRG "github.com/IBM/fp-go/v2/optics/lens/record/generic"
|
||||||
RG "github.com/IBM/fp-go/v2/record/generic"
|
RG "github.com/IBM/fp-go/v2/record/generic"
|
||||||
)
|
)
|
||||||
@@ -136,7 +136,7 @@ var (
|
|||||||
// element of a string array, returning an Option[string].
|
// element of a string array, returning an Option[string].
|
||||||
composeHead = F.Pipe1(
|
composeHead = F.Pipe1(
|
||||||
LA.AtHead[string](),
|
LA.AtHead[string](),
|
||||||
L.ComposeOptions[http.Header, string](A.Empty[string]()),
|
LO.Compose[http.Header, string](A.Empty[string]()),
|
||||||
)
|
)
|
||||||
|
|
||||||
// AtValue is a Lens that focuses on the first value of a specific header.
|
// AtValue is a Lens that focuses on the first value of a specific header.
|
||||||
|
|||||||
@@ -49,19 +49,19 @@ func Of[A any](a A) A {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChain[A, B any](ma A, f func(A) B) B {
|
func MonadChain[A, B any](ma A, f Kleisli[A, B]) B {
|
||||||
return f(ma)
|
return f(ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Chain[A, B any](f func(A) B) Operator[A, B] {
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChainFirst[A, B any](fa A, f func(A) B) A {
|
func MonadChainFirst[A, B any](fa A, f Kleisli[A, B]) A {
|
||||||
return chain.MonadChainFirst(MonadChain[A, A], MonadMap[B, A], fa, f)
|
return chain.MonadChainFirst(MonadChain[A, A], MonadMap[B, A], fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChainFirst[A, B any](f func(A) B) Operator[A, A] {
|
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
||||||
return chain.ChainFirst(Chain[A, A], Map[B, A], f)
|
return chain.ChainFirst(Chain[A, A], Map[B, A], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,6 @@
|
|||||||
package identity
|
package identity
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Operator[A, B any] = func(A) B
|
Kleisli[A, B any] = func(A) B
|
||||||
|
Operator[A, B any] = Kleisli[A, B]
|
||||||
)
|
)
|
||||||
|
|||||||
136
v2/io/bind.go
136
v2/io/bind.go
@@ -19,6 +19,7 @@ import (
|
|||||||
INTA "github.com/IBM/fp-go/v2/internal/apply"
|
INTA "github.com/IBM/fp-go/v2/internal/apply"
|
||||||
INTC "github.com/IBM/fp-go/v2/internal/chain"
|
INTC "github.com/IBM/fp-go/v2/internal/chain"
|
||||||
INTF "github.com/IBM/fp-go/v2/internal/functor"
|
INTF "github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do creates an empty context of type S to be used with the Bind operation.
|
// Do creates an empty context of type S to be used with the Bind operation.
|
||||||
@@ -58,7 +59,7 @@ func Do[S any](
|
|||||||
// }, fetchUser)
|
// }, fetchUser)
|
||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) IO[T],
|
f Kleisli[S1, T],
|
||||||
) Operator[S1, S2] {
|
) Operator[S1, S2] {
|
||||||
return INTC.Bind(
|
return INTC.Bind(
|
||||||
Chain[S1, S2],
|
Chain[S1, S2],
|
||||||
@@ -152,3 +153,136 @@ func ApS[S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 Config struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLens(
|
||||||
|
// func(c Config) int { return c.Port },
|
||||||
|
// func(c Config, p int) Config { c.Port = p; return c },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// io.Of(Config{Host: "localhost"}),
|
||||||
|
// io.ApSL(portLens, io.Of(8080)),
|
||||||
|
// )
|
||||||
|
func ApSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa IO[T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindL attaches the result of a computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Bind with a lens, allowing you to use
|
||||||
|
// optics to update nested structures based on their current values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The computation function f receives the current value of the focused field and returns
|
||||||
|
// an IO that produces the new value.
|
||||||
|
//
|
||||||
|
// 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 the counter asynchronously
|
||||||
|
// increment := func(v int) io.IO[int] {
|
||||||
|
// return io.Of(v + 1)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// io.Of(Counter{Value: 42}),
|
||||||
|
// io.BindL(valueLens, increment),
|
||||||
|
// ) // IO[Counter{Value: 43}]
|
||||||
|
func BindL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[T, T],
|
||||||
|
) Operator[S, S] {
|
||||||
|
return Bind[S, S, T](lens.Set, func(s S) IO[T] {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Let with a lens, allowing you to use
|
||||||
|
// optics to update nested structures with pure transformations.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The transformation function f receives the current value of the focused field and returns
|
||||||
|
// the new value directly (not wrapped in IO).
|
||||||
|
//
|
||||||
|
// 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 the counter value
|
||||||
|
// double := func(v int) int { return v * 2 }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// io.Of(Counter{Value: 21}),
|
||||||
|
// io.LetL(valueLens, double),
|
||||||
|
// ) // IO[Counter{Value: 42}]
|
||||||
|
func LetL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Operator[S, S] {
|
||||||
|
return Let[S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines LetTo with a lens, allowing you to use
|
||||||
|
// optics to set nested fields to specific values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides the setter for a field within the structure S.
|
||||||
|
// Unlike LetL which transforms the current value, LetToL simply replaces it with
|
||||||
|
// the provided constant value b.
|
||||||
|
//
|
||||||
|
// 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(
|
||||||
|
// io.Of(Config{Debug: true, Timeout: 30}),
|
||||||
|
// io.LetToL(debugLens, false),
|
||||||
|
// ) // IO[Config{Debug: false, Timeout: 30}]
|
||||||
|
func LetToL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Operator[S, S] {
|
||||||
|
return LetTo[S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
"github.com/IBM/fp-go/v2/internal/utils"
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,3 +55,144 @@ func TestApS(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, res(), "John Doe")
|
assert.Equal(t, res(), "John Doe")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test types for lens-based operations
|
||||||
|
type Counter struct {
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindL(t *testing.T) {
|
||||||
|
valueLens := L.MakeLens(
|
||||||
|
func(c Counter) int { return c.Value },
|
||||||
|
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("BindL with successful transformation", func(t *testing.T) {
|
||||||
|
increment := func(v int) IO[int] {
|
||||||
|
return Of(v + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Of(Counter{Value: 42}),
|
||||||
|
BindL(valueLens, increment),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Counter{Value: 43}, result())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("BindL with multiple operations", func(t *testing.T) {
|
||||||
|
double := func(v int) IO[int] {
|
||||||
|
return Of(v * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTen := func(v int) IO[int] {
|
||||||
|
return Of(v + 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Of(Counter{Value: 5}),
|
||||||
|
BindL(valueLens, double),
|
||||||
|
BindL(valueLens, addTen),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Counter{Value: 20}, result())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLetL(t *testing.T) {
|
||||||
|
valueLens := L.MakeLens(
|
||||||
|
func(c Counter) int { return c.Value },
|
||||||
|
func(c Counter, v int) Counter { c.Value = v; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("LetL with pure transformation", func(t *testing.T) {
|
||||||
|
double := func(v int) int { return v * 2 }
|
||||||
|
|
||||||
|
result := F.Pipe1(
|
||||||
|
Of(Counter{Value: 21}),
|
||||||
|
LetL(valueLens, double),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Counter{Value: 42}, result())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetL with multiple transformations", func(t *testing.T) {
|
||||||
|
double := func(v int) int { return v * 2 }
|
||||||
|
addTen := func(v int) int { return v + 10 }
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Of(Counter{Value: 5}),
|
||||||
|
LetL(valueLens, double),
|
||||||
|
LetL(valueLens, addTen),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Counter{Value: 20}, result())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLetToL(t *testing.T) {
|
||||||
|
ageLens := L.MakeLens(
|
||||||
|
func(p Person) int { return p.Age },
|
||||||
|
func(p Person, a int) Person { p.Age = a; return p },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("LetToL with constant value", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Of(Person{Name: "Alice", Age: 25}),
|
||||||
|
LetToL(ageLens, 30),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Person{Name: "Alice", Age: 30}, result())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("LetToL with multiple fields", func(t *testing.T) {
|
||||||
|
nameLens := L.MakeLens(
|
||||||
|
func(p Person) string { return p.Name },
|
||||||
|
func(p Person, n string) Person { p.Name = n; return p },
|
||||||
|
)
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Of(Person{Name: "Alice", Age: 25}),
|
||||||
|
LetToL(ageLens, 30),
|
||||||
|
LetToL(nameLens, "Bob"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Person{Name: "Bob", Age: 30}, result())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApSL(t *testing.T) {
|
||||||
|
ageLens := L.MakeLens(
|
||||||
|
func(p Person) int { return p.Age },
|
||||||
|
func(p Person, a int) Person { p.Age = a; return p },
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("ApSL with value", func(t *testing.T) {
|
||||||
|
result := F.Pipe1(
|
||||||
|
Of(Person{Name: "Alice", Age: 25}),
|
||||||
|
ApSL(ageLens, Of(30)),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Person{Name: "Alice", Age: 30}, result())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ApSL with chaining", func(t *testing.T) {
|
||||||
|
nameLens := L.MakeLens(
|
||||||
|
func(p Person) string { return p.Name },
|
||||||
|
func(p Person, n string) Person { p.Name = n; return p },
|
||||||
|
)
|
||||||
|
|
||||||
|
result := F.Pipe2(
|
||||||
|
Of(Person{Name: "Alice", Age: 25}),
|
||||||
|
ApSL(ageLens, Of(30)),
|
||||||
|
ApSL(nameLens, Of("Bob")),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, Person{Name: "Bob", Age: 30}, result())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
// whether the body action returns and error or not.
|
// whether the body action returns and error or not.
|
||||||
func Bracket[A, B, ANY any](
|
func Bracket[A, B, ANY any](
|
||||||
acquire IO[A],
|
acquire IO[A],
|
||||||
use func(A) IO[B],
|
use Kleisli[A, B],
|
||||||
release func(A, B) IO[ANY],
|
release func(A, B) IO[ANY],
|
||||||
) IO[B] {
|
) IO[B] {
|
||||||
return INTB.Bracket[IO[A], IO[B], IO[ANY], B, A, B](
|
return INTB.Bracket[IO[A], IO[B], IO[ANY], B, A, B](
|
||||||
|
|||||||
2643
v2/io/gen.go
2643
v2/io/gen.go
File diff suppressed because it is too large
Load Diff
11
v2/io/io.go
11
v2/io/io.go
@@ -44,7 +44,8 @@ type (
|
|||||||
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details
|
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioltagt] for more details
|
||||||
IO[A any] = func() A
|
IO[A any] = func() A
|
||||||
|
|
||||||
Operator[A, B any] = R.Reader[IO[A], IO[B]]
|
Kleisli[A, B any] = R.Reader[A, IO[B]]
|
||||||
|
Operator[A, B any] = Kleisli[IO[A], B]
|
||||||
Monoid[A any] = M.Monoid[IO[A]]
|
Monoid[A any] = M.Monoid[IO[A]]
|
||||||
Semigroup[A any] = S.Semigroup[IO[A]]
|
Semigroup[A any] = S.Semigroup[IO[A]]
|
||||||
)
|
)
|
||||||
@@ -121,14 +122,14 @@ func MapTo[A, B any](b B) Operator[A, B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
|
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||||
func MonadChain[A, B any](fa IO[A], f func(A) IO[B]) IO[B] {
|
func MonadChain[A, B any](fa IO[A], f Kleisli[A, B]) IO[B] {
|
||||||
return func() B {
|
return func() B {
|
||||||
return f(fa())()
|
return f(fa())()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chain composes computations in sequence, using the return value of one computation to determine the next computation.
|
// Chain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||||
func Chain[A, B any](f func(A) IO[B]) Operator[A, B] {
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||||
return F.Bind2nd(MonadChain[A, B], f)
|
return F.Bind2nd(MonadChain[A, B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,13 +202,13 @@ func Memoize[A any](ma IO[A]) IO[A] {
|
|||||||
|
|
||||||
// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
// 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.
|
// keeping only the result of the first.
|
||||||
func MonadChainFirst[A, B any](fa IO[A], f func(A) IO[B]) IO[A] {
|
func MonadChainFirst[A, B any](fa IO[A], f Kleisli[A, B]) IO[A] {
|
||||||
return chain.MonadChainFirst(MonadChain[A, A], MonadMap[B, A], fa, f)
|
return chain.MonadChainFirst(MonadChain[A, A], MonadMap[B, A], fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
// 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.
|
// keeping only the result of the first.
|
||||||
func ChainFirst[A, B any](f func(A) IO[B]) Operator[A, A] {
|
func ChainFirst[A, B any](f Kleisli[A, B]) Operator[A, A] {
|
||||||
return chain.ChainFirst(
|
return chain.ChainFirst(
|
||||||
Chain[A, A],
|
Chain[A, A],
|
||||||
Map[B, A],
|
Map[B, A],
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ import (
|
|||||||
// io.ChainFirst(io.Logger[User]()("Fetched user")),
|
// io.ChainFirst(io.Logger[User]()("Fetched user")),
|
||||||
// processUser,
|
// processUser,
|
||||||
// )
|
// )
|
||||||
func Logger[A any](loggers ...*log.Logger) func(string) func(A) IO[any] {
|
func Logger[A any](loggers ...*log.Logger) func(string) Kleisli[A, any] {
|
||||||
_, right := L.LoggingCallbacks(loggers...)
|
_, right := L.LoggingCallbacks(loggers...)
|
||||||
return func(prefix string) func(A) IO[any] {
|
return func(prefix string) Kleisli[A, any] {
|
||||||
return func(a A) IO[any] {
|
return func(a A) IO[any] {
|
||||||
return FromImpure(func() {
|
return FromImpure(func() {
|
||||||
right("%s: %v", prefix, a)
|
right("%s: %v", prefix, a)
|
||||||
@@ -53,7 +53,7 @@ func Logger[A any](loggers ...*log.Logger) func(string) func(A) IO[any] {
|
|||||||
// io.ChainFirst(io.Logf[User]("User: %+v")),
|
// io.ChainFirst(io.Logf[User]("User: %+v")),
|
||||||
// processUser,
|
// processUser,
|
||||||
// )
|
// )
|
||||||
func Logf[A any](prefix string) func(A) IO[any] {
|
func Logf[A any](prefix string) Kleisli[A, any] {
|
||||||
return func(a A) IO[any] {
|
return func(a A) IO[any] {
|
||||||
return FromImpure(func() {
|
return FromImpure(func() {
|
||||||
log.Printf(prefix, a)
|
log.Printf(prefix, a)
|
||||||
@@ -72,7 +72,7 @@ func Logf[A any](prefix string) func(A) IO[any] {
|
|||||||
// io.ChainFirst(io.Printf[User]("User: %+v\n")),
|
// io.ChainFirst(io.Printf[User]("User: %+v\n")),
|
||||||
// processUser,
|
// processUser,
|
||||||
// )
|
// )
|
||||||
func Printf[A any](prefix string) func(A) IO[any] {
|
func Printf[A any](prefix string) Kleisli[A, any] {
|
||||||
return func(a A) IO[any] {
|
return func(a A) IO[any] {
|
||||||
return FromImpure(func() {
|
return FromImpure(func() {
|
||||||
fmt.Printf(prefix, a)
|
fmt.Printf(prefix, a)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (o *ioMonad[A, B]) Map(f func(A) B) Operator[A, B] {
|
|||||||
return Map(f)
|
return Map(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ioMonad[A, B]) Chain(f func(A) IO[B]) Operator[A, B] {
|
func (o *ioMonad[A, B]) Chain(f Kleisli[A, B]) Operator[A, B] {
|
||||||
return Chain(f)
|
return Chain(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import (
|
|||||||
// return readData(f)
|
// return readData(f)
|
||||||
// })
|
// })
|
||||||
func WithResource[
|
func WithResource[
|
||||||
R, A, ANY any](onCreate IO[R], onRelease func(R) IO[ANY]) func(func(R) IO[A]) IO[A] {
|
R, A, ANY any](onCreate IO[R], onRelease func(R) IO[ANY]) Kleisli[Kleisli[R, A], A] {
|
||||||
// simply map to implementation of bracket
|
// simply map to implementation of bracket
|
||||||
return function.Bind13of3(Bracket[R, A, ANY])(onCreate, function.Ignore2of2[A](onRelease))
|
return function.Bind13of3(Bracket[R, A, ANY])(onCreate, function.Ignore2of2[A](onRelease))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ type (
|
|||||||
// )
|
// )
|
||||||
func Retrying[A any](
|
func Retrying[A any](
|
||||||
policy R.RetryPolicy,
|
policy R.RetryPolicy,
|
||||||
action func(R.RetryStatus) IO[A],
|
action Kleisli[R.RetryStatus, A],
|
||||||
check func(A) bool,
|
check func(A) bool,
|
||||||
) IO[A] {
|
) IO[A] {
|
||||||
// get an implementation for the types
|
// get an implementation for the types
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import (
|
|||||||
// fetchUsers := func(id int) io.IO[User] { return fetchUser(id) }
|
// fetchUsers := func(id int) io.IO[User] { return fetchUser(id) }
|
||||||
// users := io.MonadTraverseArray([]int{1, 2, 3}, fetchUsers)
|
// users := io.MonadTraverseArray([]int{1, 2, 3}, fetchUsers)
|
||||||
// result := users() // []User with all fetched users
|
// result := users() // []User with all fetched users
|
||||||
func MonadTraverseArray[A, B any](tas []A, f func(A) IO[B]) IO[[]B] {
|
func MonadTraverseArray[A, B any](tas []A, f Kleisli[A, B]) IO[[]B] {
|
||||||
return INTA.MonadTraverse(
|
return INTA.MonadTraverse(
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -50,7 +50,7 @@ func MonadTraverseArray[A, B any](tas []A, f func(A) IO[B]) IO[[]B] {
|
|||||||
// return fetchUser(id)
|
// return fetchUser(id)
|
||||||
// })
|
// })
|
||||||
// users := fetchUsers([]int{1, 2, 3})
|
// users := fetchUsers([]int{1, 2, 3})
|
||||||
func TraverseArray[A, B any](f func(A) IO[B]) func([]A) IO[[]B] {
|
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return INTA.Traverse[[]A](
|
return INTA.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -68,7 +68,7 @@ func TraverseArray[A, B any](f func(A) IO[B]) func([]A) IO[[]B] {
|
|||||||
// numbered := io.TraverseArrayWithIndex(func(i int, s string) io.IO[string] {
|
// numbered := io.TraverseArrayWithIndex(func(i int, s string) io.IO[string] {
|
||||||
// return io.Of(fmt.Sprintf("%d: %s", i, s))
|
// return io.Of(fmt.Sprintf("%d: %s", i, s))
|
||||||
// })
|
// })
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) IO[B]) func([]A) IO[[]B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) IO[B]) Kleisli[[]A, []B] {
|
||||||
return INTA.TraverseWithIndex[[]A](
|
return INTA.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -98,7 +98,7 @@ func SequenceArray[A any](tas []IO[A]) IO[[]A] {
|
|||||||
// fetchData := func(url string) io.IO[Data] { return fetch(url) }
|
// fetchData := func(url string) io.IO[Data] { return fetch(url) }
|
||||||
// urls := map[string]string{"a": "http://a.com", "b": "http://b.com"}
|
// urls := map[string]string{"a": "http://a.com", "b": "http://b.com"}
|
||||||
// data := io.MonadTraverseRecord(urls, fetchData)
|
// data := io.MonadTraverseRecord(urls, fetchData)
|
||||||
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) IO[B]) IO[map[K]B] {
|
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f Kleisli[A, B]) IO[map[K]B] {
|
||||||
return INTR.MonadTraverse(
|
return INTR.MonadTraverse(
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -112,7 +112,7 @@ func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) IO[B]) I
|
|||||||
// TraverseRecord returns a function that applies an IO-returning function to each value
|
// TraverseRecord returns a function that applies an IO-returning function to each value
|
||||||
// in a map and collects the results. This is the curried version of MonadTraverseRecord.
|
// in a map and collects the results. This is the curried version of MonadTraverseRecord.
|
||||||
// Executes in parallel by default.
|
// Executes in parallel by default.
|
||||||
func TraverseRecord[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO[map[K]B] {
|
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return INTR.Traverse[map[K]A](
|
return INTR.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -124,7 +124,7 @@ func TraverseRecord[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO[ma
|
|||||||
|
|
||||||
// TraverseRecordWithIndex is like TraverseRecord but the function also receives the key.
|
// TraverseRecordWithIndex is like TraverseRecord but the function also receives the key.
|
||||||
// Executes in parallel by default.
|
// Executes in parallel by default.
|
||||||
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) IO[B]) func(map[K]A) IO[map[K]B] {
|
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) IO[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return INTR.TraverseWithIndex[map[K]A](
|
return INTR.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -153,7 +153,7 @@ func SequenceRecord[K comparable, A any](tas map[K]IO[A]) IO[map[K]A] {
|
|||||||
//
|
//
|
||||||
// fetchUsers := func(id int) io.IO[User] { return fetchUser(id) }
|
// fetchUsers := func(id int) io.IO[User] { return fetchUser(id) }
|
||||||
// users := io.MonadTraverseArraySeq([]int{1, 2, 3}, fetchUsers)
|
// users := io.MonadTraverseArraySeq([]int{1, 2, 3}, fetchUsers)
|
||||||
func MonadTraverseArraySeq[A, B any](tas []A, f func(A) IO[B]) IO[[]B] {
|
func MonadTraverseArraySeq[A, B any](tas []A, f Kleisli[A, B]) IO[[]B] {
|
||||||
return INTA.MonadTraverse(
|
return INTA.MonadTraverse(
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -167,7 +167,7 @@ func MonadTraverseArraySeq[A, B any](tas []A, f func(A) IO[B]) IO[[]B] {
|
|||||||
// TraverseArraySeq returns a function that applies an IO-returning function to each element
|
// TraverseArraySeq returns a function that applies an IO-returning function to each element
|
||||||
// of an array and collects the results. Executes sequentially (one after another).
|
// of an array and collects the results. Executes sequentially (one after another).
|
||||||
// Use this when operations must be performed in order or when parallel execution is not desired.
|
// Use this when operations must be performed in order or when parallel execution is not desired.
|
||||||
func TraverseArraySeq[A, B any](f func(A) IO[B]) func([]A) IO[[]B] {
|
func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return INTA.Traverse[[]A](
|
return INTA.Traverse[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -179,7 +179,7 @@ func TraverseArraySeq[A, B any](f func(A) IO[B]) func([]A) IO[[]B] {
|
|||||||
|
|
||||||
// TraverseArrayWithIndexSeq is like TraverseArraySeq but the function also receives the index.
|
// TraverseArrayWithIndexSeq is like TraverseArraySeq but the function also receives the index.
|
||||||
// Executes sequentially (one after another).
|
// Executes sequentially (one after another).
|
||||||
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) IO[B]) func([]A) IO[[]B] {
|
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) IO[B]) Kleisli[[]A, []B] {
|
||||||
return INTA.TraverseWithIndex[[]A](
|
return INTA.TraverseWithIndex[[]A](
|
||||||
Of[[]B],
|
Of[[]B],
|
||||||
Map[[]B, func(B) []B],
|
Map[[]B, func(B) []B],
|
||||||
@@ -197,7 +197,7 @@ func SequenceArraySeq[A any](tas []IO[A]) IO[[]A] {
|
|||||||
|
|
||||||
// MonadTraverseRecordSeq applies an IO-returning function to each value in a map
|
// MonadTraverseRecordSeq applies an IO-returning function to each value in a map
|
||||||
// and collects the results into an IO of a map. Executes sequentially.
|
// and collects the results into an IO of a map. Executes sequentially.
|
||||||
func MonadTraverseRecordSeq[K comparable, A, B any](tas map[K]A, f func(A) IO[B]) IO[map[K]B] {
|
func MonadTraverseRecordSeq[K comparable, A, B any](tas map[K]A, f Kleisli[A, B]) IO[map[K]B] {
|
||||||
return INTR.MonadTraverse(
|
return INTR.MonadTraverse(
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -210,7 +210,7 @@ func MonadTraverseRecordSeq[K comparable, A, B any](tas map[K]A, f func(A) IO[B]
|
|||||||
|
|
||||||
// TraverseRecordSeq returns a function that applies an IO-returning function to each value
|
// TraverseRecordSeq returns a function that applies an IO-returning function to each value
|
||||||
// in a map and collects the results. Executes sequentially (one after another).
|
// in a map and collects the results. Executes sequentially (one after another).
|
||||||
func TraverseRecordSeq[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO[map[K]B] {
|
func TraverseRecordSeq[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return INTR.Traverse[map[K]A](
|
return INTR.Traverse[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
@@ -223,7 +223,7 @@ func TraverseRecordSeq[K comparable, A, B any](f func(A) IO[B]) func(map[K]A) IO
|
|||||||
// TraverseRecordWithIndeSeq is like TraverseRecordSeq but the function also receives the key.
|
// TraverseRecordWithIndeSeq is like TraverseRecordSeq but the function also receives the key.
|
||||||
// Executes sequentially (one after another).
|
// Executes sequentially (one after another).
|
||||||
// Note: There's a typo in the function name (Inde instead of Index) for backward compatibility.
|
// Note: There's a typo in the function name (Inde instead of Index) for backward compatibility.
|
||||||
func TraverseRecordWithIndeSeq[K comparable, A, B any](f func(K, A) IO[B]) func(map[K]A) IO[map[K]B] {
|
func TraverseRecordWithIndeSeq[K comparable, A, B any](f func(K, A) IO[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return INTR.TraverseWithIndex[map[K]A](
|
return INTR.TraverseWithIndex[map[K]A](
|
||||||
Of[map[K]B],
|
Of[map[K]B],
|
||||||
Map[map[K]B, func(B) map[K]B],
|
Map[map[K]B, func(B) map[K]B],
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/internal/apply"
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
"github.com/IBM/fp-go/v2/internal/chain"
|
"github.com/IBM/fp-go/v2/internal/chain"
|
||||||
"github.com/IBM/fp-go/v2/internal/functor"
|
"github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||||
@@ -164,3 +165,139 @@ func ApS[E, S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 Config struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLens(
|
||||||
|
// func(c Config) int { return c.Port },
|
||||||
|
// func(c Config, p int) Config { c.Port = p; return c },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// ioeither.Of[error](Config{Host: "localhost"}),
|
||||||
|
// ioeither.ApSL(portLens, ioeither.Of[error](8080)),
|
||||||
|
// )
|
||||||
|
func ApSL[E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa IOEither[E, T],
|
||||||
|
) Operator[E, S, S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindL attaches the result of a computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Bind with a lens, allowing you to use
|
||||||
|
// optics to update nested structures based on their current values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The computation function f receives the current value of the focused field and returns
|
||||||
|
// an IOEither that produces the new value.
|
||||||
|
//
|
||||||
|
// 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) ioeither.IOEither[error, int] {
|
||||||
|
// return ioeither.TryCatch(func() (int, error) {
|
||||||
|
// if v >= 100 {
|
||||||
|
// return 0, errors.New("overflow")
|
||||||
|
// }
|
||||||
|
// return v + 1, nil
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// ioeither.Of[error](Counter{Value: 42}),
|
||||||
|
// ioeither.BindL(valueLens, increment),
|
||||||
|
// )
|
||||||
|
func BindL[E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) IOEither[E, T],
|
||||||
|
) Operator[E, S, S] {
|
||||||
|
return Bind[E, S, S, T](lens.Set, func(s S) IOEither[E, T] {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Let with a lens, allowing you to use
|
||||||
|
// optics to update nested structures with pure transformations.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The transformation function f receives the current value of the focused field and returns
|
||||||
|
// the new value directly (not wrapped in IOEither).
|
||||||
|
//
|
||||||
|
// 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(
|
||||||
|
// ioeither.Of[error](Counter{Value: 21}),
|
||||||
|
// ioeither.LetL(valueLens, double),
|
||||||
|
// )
|
||||||
|
func LetL[E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Operator[E, S, S] {
|
||||||
|
return Let[E, S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines LetTo with a lens, allowing you to use
|
||||||
|
// optics to set nested fields to specific values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides the setter for a field within the structure S.
|
||||||
|
// Unlike LetL which transforms the current value, LetToL simply replaces it with
|
||||||
|
// the provided constant value b.
|
||||||
|
//
|
||||||
|
// 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(
|
||||||
|
// ioeither.Of[error](Config{Debug: true, Timeout: 30}),
|
||||||
|
// ioeither.LetToL(debugLens, false),
|
||||||
|
// )
|
||||||
|
func LetToL[E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Operator[E, S, S] {
|
||||||
|
return LetTo[E, S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TraverseArray transforms an array
|
// TraverseArray transforms an array
|
||||||
func TraverseArray[A, B any](f func(A) IOOption[B]) func([]A) IOOption[[]B] {
|
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return function.Flow2(
|
return function.Flow2(
|
||||||
io.TraverseArray(f),
|
io.TraverseArray(f),
|
||||||
io.Map(option.SequenceArray[B]),
|
io.Map(option.SequenceArray[B]),
|
||||||
@@ -30,7 +30,7 @@ func TraverseArray[A, B any](f func(A) IOOption[B]) func([]A) IOOption[[]B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndex transforms an array
|
// TraverseArrayWithIndex transforms an array
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) IOOption[B]) func([]A) IOOption[[]B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) IOOption[B]) Kleisli[[]A, []B] {
|
||||||
return function.Flow2(
|
return function.Flow2(
|
||||||
io.TraverseArrayWithIndex(f),
|
io.TraverseArrayWithIndex(f),
|
||||||
io.Map(option.SequenceArray[B]),
|
io.Map(option.SequenceArray[B]),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/internal/apply"
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
"github.com/IBM/fp-go/v2/internal/chain"
|
"github.com/IBM/fp-go/v2/internal/chain"
|
||||||
"github.com/IBM/fp-go/v2/internal/functor"
|
"github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||||
@@ -72,8 +73,8 @@ func Do[S any](
|
|||||||
// )
|
// )
|
||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) IOOption[T],
|
f Kleisli[S1, T],
|
||||||
) func(IOOption[S1]) IOOption[S2] {
|
) Kleisli[IOOption[S1], S2] {
|
||||||
return chain.Bind(
|
return chain.Bind(
|
||||||
Chain[S1, S2],
|
Chain[S1, S2],
|
||||||
Map[T, S2],
|
Map[T, S2],
|
||||||
@@ -86,7 +87,7 @@ func Bind[S1, S2, T any](
|
|||||||
func Let[S1, S2, T any](
|
func Let[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) func(IOOption[S1]) IOOption[S2] {
|
) Kleisli[IOOption[S1], S2] {
|
||||||
return functor.Let(
|
return functor.Let(
|
||||||
Map[S1, S2],
|
Map[S1, S2],
|
||||||
setter,
|
setter,
|
||||||
@@ -98,7 +99,7 @@ func Let[S1, S2, T any](
|
|||||||
func LetTo[S1, S2, T any](
|
func LetTo[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) func(IOOption[S1]) IOOption[S2] {
|
) Kleisli[IOOption[S1], S2] {
|
||||||
return functor.LetTo(
|
return functor.LetTo(
|
||||||
Map[S1, S2],
|
Map[S1, S2],
|
||||||
setter,
|
setter,
|
||||||
@@ -109,7 +110,7 @@ func LetTo[S1, S2, T any](
|
|||||||
// BindTo initializes a new state [S1] from a value [T]
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func(IOOption[T]) IOOption[S1] {
|
) Kleisli[IOOption[T], S1] {
|
||||||
return chain.BindTo(
|
return chain.BindTo(
|
||||||
Map[T, S1],
|
Map[T, S1],
|
||||||
setter,
|
setter,
|
||||||
@@ -152,7 +153,7 @@ func BindTo[S1, T any](
|
|||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa IOOption[T],
|
fa IOOption[T],
|
||||||
) func(IOOption[S1]) IOOption[S2] {
|
) Kleisli[IOOption[S1], S2] {
|
||||||
return apply.ApS(
|
return apply.ApS(
|
||||||
Ap[S2, T],
|
Ap[S2, T],
|
||||||
Map[S1, func(T) S2],
|
Map[S1, func(T) S2],
|
||||||
@@ -160,3 +161,136 @@ func ApS[S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
// Name string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ageLens := lens.MakeLens(
|
||||||
|
// func(s State) int { return s.Age },
|
||||||
|
// func(s State, a int) State { s.Age = a; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// iooption.Of(State{Name: "Alice"}),
|
||||||
|
// iooption.ApSL(ageLens, iooption.Some(30)),
|
||||||
|
// )
|
||||||
|
func ApSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa IOOption[T],
|
||||||
|
) Kleisli[IOOption[S], S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindL attaches the result of a computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Bind with a lens, allowing you to use
|
||||||
|
// optics to update nested structures based on their current values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The computation function f receives the current value of the focused field and returns
|
||||||
|
// an IOOption that produces the new value.
|
||||||
|
//
|
||||||
|
// 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 the counter, but return None if it would exceed 100
|
||||||
|
// increment := func(v int) iooption.IOOption[int] {
|
||||||
|
// return iooption.FromIO(io.Of(v + 1))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// iooption.Of(Counter{Value: 42}),
|
||||||
|
// iooption.BindL(valueLens, increment),
|
||||||
|
// ) // IOOption[Counter{Value: 43}]
|
||||||
|
func BindL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[T, T],
|
||||||
|
) Kleisli[IOOption[S], S] {
|
||||||
|
return Bind[S, S, T](lens.Set, func(s S) IOOption[T] {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Let with a lens, allowing you to use
|
||||||
|
// optics to update nested structures with pure transformations.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The transformation function f receives the current value of the focused field and returns
|
||||||
|
// the new value directly (not wrapped in IOOption).
|
||||||
|
//
|
||||||
|
// 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 the counter value
|
||||||
|
// double := func(v int) int { return v * 2 }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// iooption.Of(Counter{Value: 21}),
|
||||||
|
// iooption.LetL(valueLens, double),
|
||||||
|
// ) // IOOption[Counter{Value: 42}]
|
||||||
|
func LetL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Kleisli[IOOption[S], S] {
|
||||||
|
return Let[S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines LetTo with a lens, allowing you to use
|
||||||
|
// optics to set nested fields to specific values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides the setter for a field within the structure S.
|
||||||
|
// Unlike LetL which transforms the current value, LetToL simply replaces it with
|
||||||
|
// the provided constant value b.
|
||||||
|
//
|
||||||
|
// 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(
|
||||||
|
// iooption.Of(Config{Debug: true, Timeout: 30}),
|
||||||
|
// iooption.LetToL(debugLens, false),
|
||||||
|
// ) // IOOption[Config{Debug: false, Timeout: 30}]
|
||||||
|
func LetToL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Kleisli[IOOption[S], S] {
|
||||||
|
return LetTo[S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
// whether the body action returns and error or not.
|
// whether the body action returns and error or not.
|
||||||
func Bracket[A, B, ANY any](
|
func Bracket[A, B, ANY any](
|
||||||
acquire IOOption[A],
|
acquire IOOption[A],
|
||||||
use func(A) IOOption[B],
|
use Kleisli[A, B],
|
||||||
release func(A, Option[B]) IOOption[ANY],
|
release func(A, Option[B]) IOOption[ANY],
|
||||||
) IOOption[B] {
|
) IOOption[B] {
|
||||||
return G.Bracket[IOOption[A], IOOption[B], IOOption[ANY], Option[B], A, B](
|
return G.Bracket[IOOption[A], IOOption[B], IOOption[ANY], Option[B], A, B](
|
||||||
|
|||||||
2643
v2/iooption/gen.go
2643
v2/iooption/gen.go
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ import "github.com/IBM/fp-go/v2/function"
|
|||||||
|
|
||||||
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
// WithResource constructs a function that creates a resource, then operates on it and then releases the resource
|
||||||
func WithResource[
|
func WithResource[
|
||||||
R, A, ANY any](onCreate IOOption[R], onRelease func(R) IOOption[ANY]) func(func(R) IOOption[A]) IOOption[A] {
|
R, A, ANY any](onCreate IOOption[R], onRelease func(R) IOOption[ANY]) Kleisli[Kleisli[R, A], A] {
|
||||||
// simply map to implementation of bracket
|
// simply map to implementation of bracket
|
||||||
return function.Bind13of3(Bracket[R, A, ANY])(onCreate, function.Ignore2of2[Option[A]](onRelease))
|
return function.Bind13of3(Bracket[R, A, ANY])(onCreate, function.Ignore2of2[Option[A]](onRelease))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
// Retrying will retry the actions according to the check policy
|
// Retrying will retry the actions according to the check policy
|
||||||
func Retrying[A any](
|
func Retrying[A any](
|
||||||
policy R.RetryPolicy,
|
policy R.RetryPolicy,
|
||||||
action func(R.RetryStatus) IOOption[A],
|
action Kleisli[R.RetryStatus, A],
|
||||||
check func(A) bool,
|
check func(A) bool,
|
||||||
) IOOption[A] {
|
) IOOption[A] {
|
||||||
// get an implementation for the types
|
// get an implementation for the types
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/io"
|
"github.com/IBM/fp-go/v2/io"
|
||||||
"github.com/IBM/fp-go/v2/lazy"
|
"github.com/IBM/fp-go/v2/lazy"
|
||||||
"github.com/IBM/fp-go/v2/option"
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -31,4 +32,7 @@ type (
|
|||||||
// IOOption represents a synchronous computation that may fail
|
// IOOption represents a synchronous computation that may fail
|
||||||
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
|
// refer to [https://andywhite.xyz/posts/2021-01-27-rte-foundations/#ioeitherlte-agt] for more details
|
||||||
IOOption[A any] = io.IO[Option[A]]
|
IOOption[A any] = io.IO[Option[A]]
|
||||||
|
|
||||||
|
Kleisli[A, B any] = reader.Reader[A, IOOption[B]]
|
||||||
|
Operator[A, B any] = Kleisli[IOOption[A], B]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ func Do[S any](
|
|||||||
// ) // Produces: {1,10}, {1,20}, {2,20}, {2,40}, {3,30}, {3,60}
|
// ) // Produces: {1,10}, {1,20}, {2,20}, {2,40}, {3,30}, {3,60}
|
||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) Iterator[T],
|
f Kleisli[S1, T],
|
||||||
) func(Iterator[S1]) Iterator[S2] {
|
) Kleisli[Iterator[S1], S2] {
|
||||||
return G.Bind[Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, f)
|
return G.Bind[Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ func Bind[S1, S2, T any](
|
|||||||
func Let[S1, S2, T any](
|
func Let[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) func(Iterator[S1]) Iterator[S2] {
|
) Kleisli[Iterator[S1], S2] {
|
||||||
return G.Let[Iterator[S1], Iterator[S2], S1, S2, T](setter, f)
|
return G.Let[Iterator[S1], Iterator[S2], S1, S2, T](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,14 +88,14 @@ func Let[S1, S2, T any](
|
|||||||
func LetTo[S1, S2, T any](
|
func LetTo[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) func(Iterator[S1]) Iterator[S2] {
|
) Kleisli[Iterator[S1], S2] {
|
||||||
return G.LetTo[Iterator[S1], Iterator[S2], S1, S2, T](setter, b)
|
return G.LetTo[Iterator[S1], Iterator[S2], S1, S2, T](setter, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindTo initializes a new state [S1] from a value [T]
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func(Iterator[T]) Iterator[S1] {
|
) Kleisli[Iterator[T], S1] {
|
||||||
return G.BindTo[Iterator[S1], Iterator[T], S1, T](setter)
|
return G.BindTo[Iterator[S1], Iterator[T], S1, T](setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +135,6 @@ func BindTo[S1, T any](
|
|||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa Iterator[T],
|
fa Iterator[T],
|
||||||
) func(Iterator[S1]) Iterator[S2] {
|
) Kleisli[Iterator[S1], S2] {
|
||||||
return G.ApS[Iterator[func(T) S2], Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, fa)
|
return G.ApS[Iterator[func(T) S2], Iterator[S1], Iterator[S2], Iterator[T], S1, S2, T](setter, fa)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ import (
|
|||||||
|
|
||||||
// Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`.
|
// Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`.
|
||||||
// Stops when either the data or selectors iterator has been exhausted.
|
// Stops when either the data or selectors iterator has been exhausted.
|
||||||
func Compress[U any](sel Iterator[bool]) func(Iterator[U]) Iterator[U] {
|
func Compress[U any](sel Iterator[bool]) Kleisli[Iterator[U], U] {
|
||||||
return G.Compress[Iterator[U], Iterator[bool], Iterator[P.Pair[U, bool]]](sel)
|
return G.Compress[Iterator[U], Iterator[bool], Iterator[P.Pair[U, bool]]](sel)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ import (
|
|||||||
|
|
||||||
// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element.
|
// DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element.
|
||||||
// Note, the [Iterator] does not produce any output until the predicate first becomes false
|
// Note, the [Iterator] does not produce any output until the predicate first becomes false
|
||||||
func DropWhile[U any](pred func(U) bool) func(Iterator[U]) Iterator[U] {
|
func DropWhile[U any](pred func(U) bool) Kleisli[Iterator[U], U] {
|
||||||
return G.DropWhile[Iterator[U]](pred)
|
return G.DropWhile[Iterator[U]](pred)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,15 +18,11 @@ package stateless
|
|||||||
import (
|
import (
|
||||||
"github.com/IBM/fp-go/v2/iooption"
|
"github.com/IBM/fp-go/v2/iooption"
|
||||||
G "github.com/IBM/fp-go/v2/iterator/stateless/generic"
|
G "github.com/IBM/fp-go/v2/iterator/stateless/generic"
|
||||||
L "github.com/IBM/fp-go/v2/lazy"
|
|
||||||
M "github.com/IBM/fp-go/v2/monoid"
|
M "github.com/IBM/fp-go/v2/monoid"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
"github.com/IBM/fp-go/v2/pair"
|
"github.com/IBM/fp-go/v2/pair"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Iterator represents a stateless, pure way to iterate over a sequence
|
|
||||||
type Iterator[U any] L.Lazy[O.Option[pair.Pair[Iterator[U], U]]]
|
|
||||||
|
|
||||||
// Next returns the [Iterator] for the next element in an iterator [pair.Pair]
|
// Next returns the [Iterator] for the next element in an iterator [pair.Pair]
|
||||||
func Next[U any](m pair.Pair[Iterator[U], U]) Iterator[U] {
|
func Next[U any](m pair.Pair[Iterator[U], U]) Iterator[U] {
|
||||||
return pair.Head(m)
|
return pair.Head(m)
|
||||||
@@ -68,15 +64,15 @@ func MonadMap[U, V any](ma Iterator[U], f func(U) V) Iterator[V] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map transforms an [Iterator] of type [U] into an [Iterator] of type [V] via a mapping function
|
// Map transforms an [Iterator] of type [U] into an [Iterator] of type [V] via a mapping function
|
||||||
func Map[U, V any](f func(U) V) func(ma Iterator[U]) Iterator[V] {
|
func Map[U, V any](f func(U) V) Operator[U, V] {
|
||||||
return G.Map[Iterator[V], Iterator[U]](f)
|
return G.Map[Iterator[V], Iterator[U]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChain[U, V any](ma Iterator[U], f func(U) Iterator[V]) Iterator[V] {
|
func MonadChain[U, V any](ma Iterator[U], f Kleisli[U, V]) Iterator[V] {
|
||||||
return G.MonadChain[Iterator[V], Iterator[U]](ma, f)
|
return G.MonadChain[Iterator[V], Iterator[U]](ma, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Chain[U, V any](f func(U) Iterator[V]) func(Iterator[U]) Iterator[V] {
|
func Chain[U, V any](f Kleisli[U, V]) Kleisli[Iterator[U], V] {
|
||||||
return G.Chain[Iterator[V], Iterator[U]](f)
|
return G.Chain[Iterator[V], Iterator[U]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,17 +97,17 @@ func Replicate[U any](a U) Iterator[U] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FilterMap filters and transforms the content of an iterator
|
// FilterMap filters and transforms the content of an iterator
|
||||||
func FilterMap[U, V any](f func(U) O.Option[V]) func(ma Iterator[U]) Iterator[V] {
|
func FilterMap[U, V any](f func(U) O.Option[V]) Operator[U, V] {
|
||||||
return G.FilterMap[Iterator[V], Iterator[U]](f)
|
return G.FilterMap[Iterator[V], Iterator[U]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter filters the content of an iterator
|
// Filter filters the content of an iterator
|
||||||
func Filter[U any](f func(U) bool) func(ma Iterator[U]) Iterator[U] {
|
func Filter[U any](f func(U) bool) Operator[U, U] {
|
||||||
return G.Filter[Iterator[U]](f)
|
return G.Filter[Iterator[U]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ap is the applicative functor for iterators
|
// Ap is the applicative functor for iterators
|
||||||
func Ap[V, U any](ma Iterator[U]) func(Iterator[func(U) V]) Iterator[V] {
|
func Ap[V, U any](ma Iterator[U]) Operator[func(U) V, V] {
|
||||||
return G.Ap[Iterator[func(U) V], Iterator[V]](ma)
|
return G.Ap[Iterator[func(U) V], Iterator[V]](ma)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +128,7 @@ func Count(start int) Iterator[int] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FilterChain filters and transforms the content of an iterator
|
// FilterChain filters and transforms the content of an iterator
|
||||||
func FilterChain[U, V any](f func(U) O.Option[Iterator[V]]) func(ma Iterator[U]) Iterator[V] {
|
func FilterChain[U, V any](f func(U) O.Option[Iterator[V]]) Operator[U, V] {
|
||||||
return G.FilterChain[Iterator[Iterator[V]], Iterator[V], Iterator[U]](f)
|
return G.FilterChain[Iterator[Iterator[V]], Iterator[V], Iterator[U]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,10 +142,10 @@ func Fold[U any](m M.Monoid[U]) func(Iterator[U]) U {
|
|||||||
return G.Fold[Iterator[U]](m)
|
return G.Fold[Iterator[U]](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadChainFirst[U, V any](ma Iterator[U], f func(U) Iterator[V]) Iterator[U] {
|
func MonadChainFirst[U, V any](ma Iterator[U], f Kleisli[U, V]) Iterator[U] {
|
||||||
return G.MonadChainFirst[Iterator[V], Iterator[U], U, V](ma, f)
|
return G.MonadChainFirst[Iterator[V], Iterator[U], U, V](ma, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChainFirst[U, V any](f func(U) Iterator[V]) func(Iterator[U]) Iterator[U] {
|
func ChainFirst[U, V any](f Kleisli[U, V]) Operator[U, U] {
|
||||||
return G.ChainFirst[Iterator[V], Iterator[U], U, V](f)
|
return G.ChainFirst[Iterator[V], Iterator[U], U, V](f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,19 @@
|
|||||||
|
|
||||||
package stateless
|
package stateless
|
||||||
|
|
||||||
import "github.com/IBM/fp-go/v2/option"
|
import (
|
||||||
|
L "github.com/IBM/fp-go/v2/lazy"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/pair"
|
||||||
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Option[A any] = option.Option[A]
|
Option[A any] = option.Option[A]
|
||||||
|
|
||||||
|
// Iterator represents a stateless, pure way to iterate over a sequence
|
||||||
|
Iterator[U any] L.Lazy[Option[pair.Pair[Iterator[U], U]]]
|
||||||
|
|
||||||
|
Kleisli[A, B any] = reader.Reader[A, Iterator[B]]
|
||||||
|
Operator[A, B any] = Kleisli[Iterator[A], B]
|
||||||
)
|
)
|
||||||
|
|||||||
151
v2/lazy/bind.go
151
v2/lazy/bind.go
@@ -16,6 +16,8 @@
|
|||||||
package lazy
|
package lazy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
|
||||||
"github.com/IBM/fp-go/v2/io"
|
"github.com/IBM/fp-go/v2/io"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,8 +72,8 @@ func Do[S any](
|
|||||||
// )
|
// )
|
||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) Lazy[T],
|
f Kleisli[S1, T],
|
||||||
) func(Lazy[S1]) Lazy[S2] {
|
) Kleisli[Lazy[S1], S2] {
|
||||||
return io.Bind(setter, f)
|
return io.Bind(setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ func Bind[S1, S2, T any](
|
|||||||
func Let[S1, S2, T any](
|
func Let[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
) func(Lazy[S1]) Lazy[S2] {
|
) Kleisli[Lazy[S1], S2] {
|
||||||
return io.Let(setter, f)
|
return io.Let(setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,14 +89,14 @@ func Let[S1, S2, T any](
|
|||||||
func LetTo[S1, S2, T any](
|
func LetTo[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
) func(Lazy[S1]) Lazy[S2] {
|
) Kleisli[Lazy[S1], S2] {
|
||||||
return io.LetTo(setter, b)
|
return io.LetTo(setter, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindTo initializes a new state [S1] from a value [T]
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func(Lazy[T]) Lazy[S1] {
|
) Kleisli[Lazy[T], S1] {
|
||||||
return io.BindTo(setter)
|
return io.BindTo(setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +136,143 @@ func BindTo[S1, T any](
|
|||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa Lazy[T],
|
fa Lazy[T],
|
||||||
) func(Lazy[S1]) Lazy[S2] {
|
) Kleisli[Lazy[S1], S2] {
|
||||||
return io.ApS(setter, fa)
|
return io.ApS(setter, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 ApSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa Lazy[T],
|
||||||
|
) Kleisli[Lazy[S], S] {
|
||||||
|
return io.ApSL(lens, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 BindL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[T, T],
|
||||||
|
) Kleisli[Lazy[S], S] {
|
||||||
|
return io.BindL(lens, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 LetL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Kleisli[Lazy[S], S] {
|
||||||
|
return io.LetL(lens, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 LetToL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Kleisli[Lazy[S], S] {
|
||||||
|
return io.LetToL(lens, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/io"
|
"github.com/IBM/fp-go/v2/io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lazy represents a synchronous computation without side effects
|
|
||||||
type Lazy[A any] = func() A
|
|
||||||
|
|
||||||
func Of[A any](a A) Lazy[A] {
|
func Of[A any](a A) Lazy[A] {
|
||||||
return io.Of(a)
|
return io.Of(a)
|
||||||
}
|
}
|
||||||
@@ -53,17 +50,17 @@ func MonadMapTo[A, B any](fa Lazy[A], b B) Lazy[B] {
|
|||||||
return io.MonadMapTo(fa, b)
|
return io.MonadMapTo(fa, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapTo[A, B any](b B) func(Lazy[A]) Lazy[B] {
|
func MapTo[A, B any](b B) Kleisli[Lazy[A], B] {
|
||||||
return io.MapTo[A](b)
|
return io.MapTo[A](b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
|
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||||
func MonadChain[A, B any](fa Lazy[A], f func(A) Lazy[B]) Lazy[B] {
|
func MonadChain[A, B any](fa Lazy[A], f Kleisli[A, B]) Lazy[B] {
|
||||||
return io.MonadChain(fa, f)
|
return io.MonadChain(fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chain composes computations in sequence, using the return value of one computation to determine the next computation.
|
// Chain composes computations in sequence, using the return value of one computation to determine the next computation.
|
||||||
func Chain[A, B any](f func(A) Lazy[B]) func(Lazy[A]) Lazy[B] {
|
func Chain[A, B any](f Kleisli[A, B]) Kleisli[Lazy[A], B] {
|
||||||
return io.Chain(f)
|
return io.Chain(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,13 +83,13 @@ func Memoize[A any](ma Lazy[A]) Lazy[A] {
|
|||||||
|
|
||||||
// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
// 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.
|
// keeping only the result of the first.
|
||||||
func MonadChainFirst[A, B any](fa Lazy[A], f func(A) Lazy[B]) Lazy[A] {
|
func MonadChainFirst[A, B any](fa Lazy[A], f Kleisli[A, B]) Lazy[A] {
|
||||||
return io.MonadChainFirst(fa, f)
|
return io.MonadChainFirst(fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
// 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.
|
// keeping only the result of the first.
|
||||||
func ChainFirst[A, B any](f func(A) Lazy[B]) func(Lazy[A]) Lazy[A] {
|
func ChainFirst[A, B any](f Kleisli[A, B]) Kleisli[Lazy[A], A] {
|
||||||
return io.ChainFirst(f)
|
return io.ChainFirst(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +99,7 @@ func MonadApFirst[A, B any](first Lazy[A], second Lazy[B]) Lazy[A] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ApFirst combines two effectful actions, keeping only the result of the first.
|
// ApFirst combines two effectful actions, keeping only the result of the first.
|
||||||
func ApFirst[A, B any](second Lazy[B]) func(Lazy[A]) Lazy[A] {
|
func ApFirst[A, B any](second Lazy[B]) Kleisli[Lazy[A], A] {
|
||||||
return io.ApFirst[A](second)
|
return io.ApFirst[A](second)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +109,7 @@ func MonadApSecond[A, B any](first Lazy[A], second Lazy[B]) Lazy[B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ApSecond combines two effectful actions, keeping only the result of the second.
|
// ApSecond combines two effectful actions, keeping only the result of the second.
|
||||||
func ApSecond[A, B any](second Lazy[B]) func(Lazy[A]) Lazy[B] {
|
func ApSecond[A, B any](second Lazy[B]) Kleisli[Lazy[A], B] {
|
||||||
return io.ApSecond[A](second)
|
return io.ApSecond[A](second)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +119,7 @@ func MonadChainTo[A, B any](fa Lazy[A], fb Lazy[B]) Lazy[B] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ChainTo composes computations in sequence, ignoring the return value of the first computation
|
// ChainTo composes computations in sequence, ignoring the return value of the first computation
|
||||||
func ChainTo[A, B any](fb Lazy[B]) func(Lazy[A]) Lazy[B] {
|
func ChainTo[A, B any](fb Lazy[B]) Kleisli[Lazy[A], B] {
|
||||||
return io.ChainTo[A](fb)
|
return io.ChainTo[A](fb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
// check - checks if the result of the action needs to be retried
|
// check - checks if the result of the action needs to be retried
|
||||||
func Retrying[A any](
|
func Retrying[A any](
|
||||||
policy R.RetryPolicy,
|
policy R.RetryPolicy,
|
||||||
action func(R.RetryStatus) Lazy[A],
|
action Kleisli[R.RetryStatus, A],
|
||||||
check func(A) bool,
|
check func(A) bool,
|
||||||
) Lazy[A] {
|
) Lazy[A] {
|
||||||
return io.Retrying(policy, action, check)
|
return io.Retrying(policy, action, check)
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ package lazy
|
|||||||
|
|
||||||
import "github.com/IBM/fp-go/v2/io"
|
import "github.com/IBM/fp-go/v2/io"
|
||||||
|
|
||||||
func MonadTraverseArray[A, B any](tas []A, f func(A) Lazy[B]) Lazy[[]B] {
|
func MonadTraverseArray[A, B any](tas []A, f Kleisli[A, B]) Lazy[[]B] {
|
||||||
return io.MonadTraverseArray(tas, f)
|
return io.MonadTraverseArray(tas, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArray applies a function returning an [IO] to all elements in an array and the
|
// TraverseArray applies a function returning an [IO] to all elements in an array and the
|
||||||
// transforms this into an [IO] of that array
|
// transforms this into an [IO] of that array
|
||||||
func TraverseArray[A, B any](f func(A) Lazy[B]) func([]A) Lazy[[]B] {
|
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return io.TraverseArray(f)
|
return io.TraverseArray(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseArrayWithIndex applies a function returning an [IO] to all elements in an array and the
|
// TraverseArrayWithIndex applies a function returning an [IO] to all elements in an array and the
|
||||||
// transforms this into an [IO] of that array
|
// transforms this into an [IO] of that array
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) Lazy[B]) func([]A) Lazy[[]B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) Lazy[B]) Kleisli[[]A, []B] {
|
||||||
return io.TraverseArrayWithIndex(f)
|
return io.TraverseArrayWithIndex(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,19 +38,19 @@ func SequenceArray[A any](tas []Lazy[A]) Lazy[[]A] {
|
|||||||
return io.SequenceArray(tas)
|
return io.SequenceArray(tas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f func(A) Lazy[B]) Lazy[map[K]B] {
|
func MonadTraverseRecord[K comparable, A, B any](tas map[K]A, f Kleisli[A, B]) Lazy[map[K]B] {
|
||||||
return io.MonadTraverseRecord(tas, f)
|
return io.MonadTraverseRecord(tas, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
||||||
// transforms this into an [IO] of that record
|
// transforms this into an [IO] of that record
|
||||||
func TraverseRecord[K comparable, A, B any](f func(A) Lazy[B]) func(map[K]A) Lazy[map[K]B] {
|
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return io.TraverseRecord[K](f)
|
return io.TraverseRecord[K](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
// TraverseRecord applies a function returning an [IO] to all elements in a record and the
|
||||||
// transforms this into an [IO] of that record
|
// transforms this into an [IO] of that record
|
||||||
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Lazy[B]) func(map[K]A) Lazy[map[K]B] {
|
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Lazy[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return io.TraverseRecordWithIndex[K](f)
|
return io.TraverseRecordWithIndex[K](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
v2/lazy/types.go
Normal file
9
v2/lazy/types.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package lazy
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Lazy represents a synchronous computation without side effects
|
||||||
|
Lazy[A any] = func() A
|
||||||
|
|
||||||
|
Kleisli[A, B any] = func(A) Lazy[B]
|
||||||
|
Operator[A, B any] = Kleisli[Lazy[A], B]
|
||||||
|
)
|
||||||
@@ -17,10 +17,7 @@
|
|||||||
package lens
|
package lens
|
||||||
|
|
||||||
import (
|
import (
|
||||||
EM "github.com/IBM/fp-go/v2/endomorphism"
|
|
||||||
"github.com/IBM/fp-go/v2/function"
|
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// setCopy wraps a setter for a pointer into a setter that first creates a copy before
|
// setCopy wraps a setter for a pointer into a setter that first creates a copy before
|
||||||
@@ -44,32 +41,156 @@ func setCopyCurried[SET ~func(A) Endomorphism[*S], S, A any](setter SET) func(a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeLens creates a [Lens] based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the
|
// MakeLens creates a [Lens] based on a getter and a setter F.
|
||||||
// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeLensRef`
|
//
|
||||||
// and for other kinds of data structures that are copied by reference make sure the setter creates the copy.
|
// The setter must create a (shallow) copy of the data structure. This happens automatically
|
||||||
|
// when the data is passed by value. For pointer-based structures, use [MakeLensRef] instead.
|
||||||
|
// For other reference types (slices, maps), ensure the setter creates a copy.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - GET: Getter function type (S → A)
|
||||||
|
// - SET: Setter function type (S, A → S)
|
||||||
|
// - S: Source structure type
|
||||||
|
// - A: Focus/field type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - get: Function to extract value A from structure S
|
||||||
|
// - set: Function to update value A in structure S, returning a new S
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A Lens[S, A] that can get and set values immutably
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// nameLens := lens.MakeLens(
|
||||||
|
// func(p Person) string { return p.Name },
|
||||||
|
// func(p Person, name string) Person {
|
||||||
|
// p.Name = name
|
||||||
|
// return p
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// person := Person{Name: "Alice", Age: 30}
|
||||||
|
// name := nameLens.Get(person) // "Alice"
|
||||||
|
// updated := nameLens.Set("Bob")(person) // Person{Name: "Bob", Age: 30}
|
||||||
func MakeLens[GET ~func(S) A, SET ~func(S, A) S, S, A any](get GET, set SET) Lens[S, A] {
|
func MakeLens[GET ~func(S) A, SET ~func(S, A) S, S, A any](get GET, set SET) Lens[S, A] {
|
||||||
return MakeLensCurried(get, function.Curry2(F.Swap(set)))
|
return MakeLensCurried(get, F.Curry2(F.Swap(set)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeLensCurried creates a [Lens] based on a getter and a setter function. Make sure that the setter creates a (shallow) copy of the
|
// MakeLensCurried creates a [Lens] with a curried setter F.
|
||||||
// data. This happens automatically if the data is passed by value. For pointers consider to use `MakeLensRef`
|
//
|
||||||
// and for other kinds of data structures that are copied by reference make sure the setter creates the copy.
|
// This is similar to [MakeLens] but accepts a curried setter (A → S → S) instead of
|
||||||
|
// an uncurried one (S, A → S). The curried form is more composable in functional pipelines.
|
||||||
|
//
|
||||||
|
// The setter must create a (shallow) copy of the data structure. This happens automatically
|
||||||
|
// when the data is passed by value. For pointer-based structures, use [MakeLensRefCurried].
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - GET: Getter function type (S → A)
|
||||||
|
// - SET: Curried setter function type (A → S → S)
|
||||||
|
// - S: Source structure type
|
||||||
|
// - A: Focus/field type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - get: Function to extract value A from structure S
|
||||||
|
// - set: Curried function to update value A in structure S
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A Lens[S, A] that can get and set values immutably
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// nameLens := lens.MakeLensCurried(
|
||||||
|
// func(p Person) string { return p.Name },
|
||||||
|
// func(name string) func(Person) Person {
|
||||||
|
// return func(p Person) Person {
|
||||||
|
// p.Name = name
|
||||||
|
// return p
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// )
|
||||||
func MakeLensCurried[GET ~func(S) A, SET ~func(A) Endomorphism[S], S, A any](get GET, set SET) Lens[S, A] {
|
func MakeLensCurried[GET ~func(S) A, SET ~func(A) Endomorphism[S], S, A any](get GET, set SET) Lens[S, A] {
|
||||||
return Lens[S, A]{Get: get, Set: set}
|
return Lens[S, A]{Get: get, Set: set}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeLensRef creates a [Lens] based on a getter and a setter function. The setter passed in does not have to create a shallow
|
// MakeLensRef creates a [Lens] for pointer-based structures.
|
||||||
// copy, the implementation wraps the setter into one that copies the pointer before modifying it
|
|
||||||
//
|
//
|
||||||
// Such a [Lens] assumes that property A of S always exists
|
// Unlike [MakeLens], the setter does not need to create a copy manually. This function
|
||||||
|
// automatically wraps the setter to create a shallow copy of the pointed-to value before
|
||||||
|
// modification, ensuring immutability.
|
||||||
|
//
|
||||||
|
// This lens assumes that property A always exists in structure S (i.e., it's not optional).
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - GET: Getter function type (*S → A)
|
||||||
|
// - SET: Setter function type (*S, A → *S)
|
||||||
|
// - S: Source structure type (will be used as *S)
|
||||||
|
// - A: Focus/field type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - get: Function to extract value A from pointer *S
|
||||||
|
// - set: Function to update value A in pointer *S (copying handled automatically)
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A Lens[*S, A] that can get and set values immutably on pointers
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// nameLens := lens.MakeLensRef(
|
||||||
|
// func(p *Person) string { return p.Name },
|
||||||
|
// func(p *Person, name string) *Person {
|
||||||
|
// p.Name = name // No manual copy needed
|
||||||
|
// return p
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// person := &Person{Name: "Alice", Age: 30}
|
||||||
|
// updated := nameLens.Set("Bob")(person)
|
||||||
|
// // person.Name is still "Alice", updated is a new pointer with Name "Bob"
|
||||||
func MakeLensRef[GET ~func(*S) A, SET func(*S, A) *S, S, A any](get GET, set SET) Lens[*S, A] {
|
func MakeLensRef[GET ~func(*S) A, SET func(*S, A) *S, S, A any](get GET, set SET) Lens[*S, A] {
|
||||||
return MakeLens(get, setCopy(set))
|
return MakeLens(get, setCopy(set))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeLensRefCurried creates a [Lens] based on a getter and a setter function. The setter passed in does not have to create a shallow
|
// MakeLensRefCurried creates a [Lens] for pointer-based structures with a curried setter.
|
||||||
// copy, the implementation wraps the setter into one that copies the pointer before modifying it
|
|
||||||
//
|
//
|
||||||
// Such a [Lens] assumes that property A of S always exists
|
// This combines the benefits of [MakeLensRef] (automatic copying) with [MakeLensCurried]
|
||||||
|
// (curried setter for better composition). The setter does not need to create a copy manually;
|
||||||
|
// this function automatically wraps it to ensure immutability.
|
||||||
|
//
|
||||||
|
// This lens assumes that property A always exists in structure S (i.e., it's not optional).
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: Source structure type (will be used as *S)
|
||||||
|
// - A: Focus/field type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - get: Function to extract value A from pointer *S
|
||||||
|
// - set: Curried function to update value A in pointer *S (copying handled automatically)
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A Lens[*S, A] that can get and set values immutably on pointers
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// nameLens := lens.MakeLensRefCurried(
|
||||||
|
// func(p *Person) string { return p.Name },
|
||||||
|
// func(name string) func(*Person) *Person {
|
||||||
|
// return func(p *Person) *Person {
|
||||||
|
// p.Name = name // No manual copy needed
|
||||||
|
// return p
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// )
|
||||||
func MakeLensRefCurried[S, A any](get func(*S) A, set func(A) Endomorphism[*S]) Lens[*S, A] {
|
func MakeLensRefCurried[S, A any](get func(*S) A, set func(A) Endomorphism[*S]) Lens[*S, A] {
|
||||||
return MakeLensCurried(get, setCopyCurried(set))
|
return MakeLensCurried(get, setCopyCurried(set))
|
||||||
}
|
}
|
||||||
@@ -79,12 +200,54 @@ func id[GET ~func(S) S, SET ~func(S, S) S, S any](creator func(get GET, set SET)
|
|||||||
return creator(F.Identity[S], F.Second[S, S])
|
return creator(F.Identity[S], F.Second[S, S])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id returns a [Lens] implementing the identity operation
|
// Id returns an identity [Lens] that focuses on the entire structure.
|
||||||
|
//
|
||||||
|
// The identity lens is useful as a starting point for lens composition or when you need
|
||||||
|
// a lens that doesn't actually focus on a subpart. Get returns the structure unchanged,
|
||||||
|
// and Set replaces the entire structure.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: The structure type
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A Lens[S, S] where both source and focus are the same type
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// idLens := lens.Id[Person]()
|
||||||
|
// person := Person{Name: "Alice", Age: 30}
|
||||||
|
//
|
||||||
|
// same := idLens.Get(person) // Returns person unchanged
|
||||||
|
// replaced := idLens.Set(Person{Name: "Bob", Age: 25})(person)
|
||||||
|
// // replaced is Person{Name: "Bob", Age: 25}
|
||||||
func Id[S any]() Lens[S, S] {
|
func Id[S any]() Lens[S, S] {
|
||||||
return id(MakeLens[Endomorphism[S], func(S, S) S])
|
return id(MakeLens[Endomorphism[S], func(S, S) S])
|
||||||
}
|
}
|
||||||
|
|
||||||
// IdRef returns a [Lens] implementing the identity operation
|
// IdRef returns an identity [Lens] for pointer-based structures.
|
||||||
|
//
|
||||||
|
// This is the pointer version of [Id]. It focuses on the entire pointer structure,
|
||||||
|
// with automatic copying to ensure immutability.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: The structure type (will be used as *S)
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A Lens[*S, *S] where both source and focus are pointers to the same type
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// idLens := lens.IdRef[Person]()
|
||||||
|
// person := &Person{Name: "Alice", Age: 30}
|
||||||
|
//
|
||||||
|
// same := idLens.Get(person) // Returns person pointer
|
||||||
|
// replaced := idLens.Set(&Person{Name: "Bob", Age: 25})(person)
|
||||||
|
// // person.Name is still "Alice", replaced is a new pointer
|
||||||
func IdRef[S any]() Lens[*S, *S] {
|
func IdRef[S any]() Lens[*S, *S] {
|
||||||
return id(MakeLensRef[Endomorphism[*S], func(*S, *S) *S])
|
return id(MakeLensRef[Endomorphism[*S], func(*S, *S) *S])
|
||||||
}
|
}
|
||||||
@@ -105,111 +268,94 @@ func compose[GET ~func(S) B, SET ~func(S, B) S, S, A, B any](creator func(get GE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose combines two lenses and allows to narrow down the focus to a sub-lens
|
// Compose combines two lenses to focus on a deeply nested field.
|
||||||
|
//
|
||||||
|
// Given a lens from S to A and a lens from A to B, Compose creates a lens from S to B.
|
||||||
|
// This allows you to navigate through nested structures in a composable way.
|
||||||
|
//
|
||||||
|
// The composition follows the mathematical property: (sa ∘ ab).Get = ab.Get ∘ sa.Get
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: Outer structure type
|
||||||
|
// - A: Intermediate structure type
|
||||||
|
// - B: Inner focus type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ab: Lens from A to B (inner lens)
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a Lens[S, A] and returns a Lens[S, B]
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Address struct {
|
||||||
|
// Street string
|
||||||
|
// City string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Address Address
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// addressLens := lens.MakeLens(
|
||||||
|
// func(p Person) Address { return p.Address },
|
||||||
|
// func(p Person, a Address) Person { p.Address = a; return p },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// streetLens := lens.MakeLens(
|
||||||
|
// func(a Address) string { return a.Street },
|
||||||
|
// func(a Address, s string) Address { a.Street = s; return a },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Compose to access street directly from person
|
||||||
|
// personStreetLens := F.Pipe1(addressLens, lens.Compose[Person](streetLens))
|
||||||
|
//
|
||||||
|
// person := Person{Name: "Alice", Address: Address{Street: "Main St"}}
|
||||||
|
// street := personStreetLens.Get(person) // "Main St"
|
||||||
|
// updated := personStreetLens.Set("Oak Ave")(person)
|
||||||
func Compose[S, A, B any](ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
|
func Compose[S, A, B any](ab Lens[A, B]) func(Lens[S, A]) Lens[S, B] {
|
||||||
return compose(MakeLens[func(S) B, func(S, B) S], ab)
|
return compose(MakeLens[func(S) B, func(S, B) S], ab)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComposeOption combines a `Lens` that returns an optional value with a `Lens` that returns a definite value
|
// ComposeRef combines two lenses for pointer-based structures.
|
||||||
// the getter returns an `Option[B]` because the container `A` could already be an option
|
//
|
||||||
// if the setter is invoked with `Some[B]` then the value of `B` will be set, potentially on a default value of `A` if `A` did not exist
|
// This is the pointer version of [Compose], automatically handling copying to ensure immutability.
|
||||||
// if the setter is invoked with `None[B]` then the container `A` is reset to `None[A]` because this is the only way to remove `B`
|
// It allows you to navigate through nested pointer structures in a composable way.
|
||||||
func ComposeOption[S, B, A any](defaultA A) func(ab Lens[A, B]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
//
|
||||||
defa := F.Constant(defaultA)
|
// Type Parameters:
|
||||||
return func(ab Lens[A, B]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
// - S: Outer structure type (will be used as *S)
|
||||||
foldab := O.Fold(O.None[B], F.Flow2(ab.Get, O.Some[B]))
|
// - A: Intermediate structure type
|
||||||
return func(sa Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
// - B: Inner focus type
|
||||||
// set A on S
|
//
|
||||||
seta := F.Flow2(
|
// Parameters:
|
||||||
O.Some[A],
|
// - ab: Lens from A to B (inner lens)
|
||||||
sa.Set,
|
//
|
||||||
)
|
// Returns:
|
||||||
// remove A from S
|
// - A function that takes a Lens[*S, A] and returns a Lens[*S, B]
|
||||||
unseta := F.Nullary2(
|
//
|
||||||
O.None[A],
|
// Example:
|
||||||
sa.Set,
|
//
|
||||||
)
|
// type Address struct {
|
||||||
return MakeLens(
|
// Street string
|
||||||
F.Flow2(sa.Get, foldab),
|
// }
|
||||||
func(s S, ob O.Option[B]) S {
|
//
|
||||||
return F.Pipe2(
|
// type Person struct {
|
||||||
ob,
|
// Name string
|
||||||
O.Fold(unseta, func(b B) Endomorphism[S] {
|
// Address Address
|
||||||
setbona := F.Flow2(
|
// }
|
||||||
ab.Set(b),
|
//
|
||||||
seta,
|
// addressLens := lens.MakeLensRef(
|
||||||
)
|
// func(p *Person) Address { return p.Address },
|
||||||
return F.Pipe2(
|
// func(p *Person, a Address) *Person { p.Address = a; return p },
|
||||||
s,
|
// )
|
||||||
sa.Get,
|
//
|
||||||
O.Fold(
|
// streetLens := lens.MakeLens(
|
||||||
F.Nullary2(
|
// func(a Address) string { return a.Street },
|
||||||
defa,
|
// func(a Address, s string) Address { a.Street = s; return a },
|
||||||
setbona,
|
// )
|
||||||
),
|
//
|
||||||
setbona,
|
// personStreetLens := F.Pipe1(addressLens, lens.ComposeRef[Person](streetLens))
|
||||||
),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
EM.Ap(s),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ComposeOptions combines a `Lens` that returns an optional value with a `Lens` that returns another optional value
|
|
||||||
// the getter returns `None[B]` if either `A` or `B` is `None`
|
|
||||||
// if the setter is called with `Some[B]` and `A` exists, 'A' is updated with `B`
|
|
||||||
// if the setter is called with `Some[B]` and `A` does not exist, the default of 'A' is updated with `B`
|
|
||||||
// if the setter is called with `None[B]` and `A` does not exist this is the identity operation on 'S'
|
|
||||||
// if the setter is called with `None[B]` and `A` does exist, 'B' is removed from 'A'
|
|
||||||
func ComposeOptions[S, B, A any](defaultA A) func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
|
||||||
defa := F.Constant(defaultA)
|
|
||||||
noops := EM.Identity[S]
|
|
||||||
noneb := O.None[B]()
|
|
||||||
return func(ab Lens[A, O.Option[B]]) func(Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
|
||||||
unsetb := ab.Set(noneb)
|
|
||||||
return func(sa Lens[S, O.Option[A]]) Lens[S, O.Option[B]] {
|
|
||||||
// sets an A onto S
|
|
||||||
seta := F.Flow2(
|
|
||||||
O.Some[A],
|
|
||||||
sa.Set,
|
|
||||||
)
|
|
||||||
return MakeLensCurried(
|
|
||||||
F.Flow2(
|
|
||||||
sa.Get,
|
|
||||||
O.Chain(ab.Get),
|
|
||||||
),
|
|
||||||
func(b O.Option[B]) Endomorphism[S] {
|
|
||||||
return func(s S) S {
|
|
||||||
return O.MonadFold(b, func() Endomorphism[S] {
|
|
||||||
return F.Pipe2(
|
|
||||||
s,
|
|
||||||
sa.Get,
|
|
||||||
O.Fold(noops, F.Flow2(unsetb, seta)),
|
|
||||||
)
|
|
||||||
}, func(b B) Endomorphism[S] {
|
|
||||||
// sets a B onto an A
|
|
||||||
setb := F.Flow2(
|
|
||||||
ab.Set(O.Some(b)),
|
|
||||||
seta,
|
|
||||||
)
|
|
||||||
return F.Pipe2(
|
|
||||||
s,
|
|
||||||
sa.Get,
|
|
||||||
O.Fold(F.Nullary2(defa, setb), setb),
|
|
||||||
)
|
|
||||||
})(s)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compose combines two lenses and allows to narrow down the focus to a sub-lens
|
|
||||||
func ComposeRef[S, A, B any](ab Lens[A, B]) func(Lens[*S, A]) Lens[*S, B] {
|
func ComposeRef[S, A, B any](ab Lens[A, B]) func(Lens[*S, A]) Lens[*S, B] {
|
||||||
return compose(MakeLensRef[func(*S) B, func(*S, B) *S], ab)
|
return compose(MakeLensRef[func(*S) B, func(*S, B) *S], ab)
|
||||||
}
|
}
|
||||||
@@ -218,101 +364,108 @@ func modify[FCT ~func(A) A, S, A any](f FCT, sa Lens[S, A], s S) S {
|
|||||||
return sa.Set(f(sa.Get(s)))(s)
|
return sa.Set(f(sa.Get(s)))(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify changes a property of a [Lens] by invoking a transformation function
|
// Modify transforms a value through a lens using a transformation F.
|
||||||
// if the transformed property has not changes, the method returns the original state
|
//
|
||||||
|
// Instead of setting a specific value, Modify applies a function to the current value.
|
||||||
|
// This is useful for updates like incrementing a counter, appending to a string, etc.
|
||||||
|
// If the transformation doesn't change the value, the original structure is returned.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: Structure type
|
||||||
|
// - FCT: Transformation function type (A → A)
|
||||||
|
// - A: Focus type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: Transformation function to apply to the focused value
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a Lens[S, A] and returns an Endomorphism[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 },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// counter := Counter{Value: 5}
|
||||||
|
//
|
||||||
|
// // Increment the counter
|
||||||
|
// incremented := F.Pipe2(
|
||||||
|
// valueLens,
|
||||||
|
// lens.Modify[Counter](func(v int) int { return v + 1 }),
|
||||||
|
// F.Ap(counter),
|
||||||
|
// )
|
||||||
|
// // incremented.Value == 6
|
||||||
|
//
|
||||||
|
// // Double the counter
|
||||||
|
// doubled := F.Pipe2(
|
||||||
|
// valueLens,
|
||||||
|
// lens.Modify[Counter](func(v int) int { return v * 2 }),
|
||||||
|
// F.Ap(counter),
|
||||||
|
// )
|
||||||
|
// // doubled.Value == 10
|
||||||
func Modify[S any, FCT ~func(A) A, A any](f FCT) func(Lens[S, A]) Endomorphism[S] {
|
func Modify[S any, FCT ~func(A) A, A any](f FCT) func(Lens[S, A]) Endomorphism[S] {
|
||||||
return function.Curry3(modify[FCT, S, A])(f)
|
return F.Curry3(modify[FCT, S, A])(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IMap transforms the focus type of a lens using an isomorphism.
|
||||||
|
//
|
||||||
|
// An isomorphism is a pair of functions (A → B, B → A) that are inverses of each other.
|
||||||
|
// IMap allows you to work with a lens in a different but equivalent type. This is useful
|
||||||
|
// for unit conversions, encoding/decoding, or any bidirectional transformation.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - E: Structure type
|
||||||
|
// - AB: Forward transformation function type (A → B)
|
||||||
|
// - BA: Backward transformation function type (B → A)
|
||||||
|
// - A: Original focus type
|
||||||
|
// - B: Transformed focus type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ab: Forward transformation (A → B)
|
||||||
|
// - ba: Backward transformation (B → A)
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a Lens[E, A] and returns a Lens[E, B]
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Celsius float64
|
||||||
|
// type Fahrenheit float64
|
||||||
|
//
|
||||||
|
// celsiusToFahrenheit := func(c Celsius) Fahrenheit {
|
||||||
|
// return Fahrenheit(c*9/5 + 32)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fahrenheitToCelsius := func(f Fahrenheit) Celsius {
|
||||||
|
// return Celsius((f - 32) * 5 / 9)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type Weather struct {
|
||||||
|
// Temperature Celsius
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// tempCelsiusLens := lens.MakeLens(
|
||||||
|
// func(w Weather) Celsius { return w.Temperature },
|
||||||
|
// func(w Weather, t Celsius) Weather { w.Temperature = t; return w },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Create a lens that works with Fahrenheit
|
||||||
|
// tempFahrenheitLens := F.Pipe1(
|
||||||
|
// tempCelsiusLens,
|
||||||
|
// lens.IMap[Weather](celsiusToFahrenheit, fahrenheitToCelsius),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// weather := Weather{Temperature: 20} // 20°C
|
||||||
|
// tempF := tempFahrenheitLens.Get(weather) // 68°F
|
||||||
|
// updated := tempFahrenheitLens.Set(86)(weather) // Set to 86°F (30°C)
|
||||||
func IMap[E any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) func(Lens[E, A]) Lens[E, B] {
|
func IMap[E any, AB ~func(A) B, BA ~func(B) A, A, B any](ab AB, ba BA) func(Lens[E, A]) Lens[E, B] {
|
||||||
return func(ea Lens[E, A]) Lens[E, B] {
|
return func(ea Lens[E, A]) Lens[E, B] {
|
||||||
return Lens[E, B]{Get: F.Flow2(ea.Get, ab), Set: F.Flow2(ba, ea.Set)}
|
return Lens[E, B]{Get: F.Flow2(ea.Get, ab), Set: F.Flow2(ba, ea.Set)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
|
||||||
// if the optional value is set then the nil value will be set instead
|
|
||||||
func fromPredicate[GET ~func(S) O.Option[A], SET ~func(S, O.Option[A]) S, S, A any](creator func(get GET, set SET) Lens[S, O.Option[A]], pred func(A) bool, nilValue A) func(sa Lens[S, A]) Lens[S, O.Option[A]] {
|
|
||||||
fromPred := O.FromPredicate(pred)
|
|
||||||
return func(sa Lens[S, A]) Lens[S, O.Option[A]] {
|
|
||||||
fold := O.Fold(F.Bind1of1(sa.Set)(nilValue), sa.Set)
|
|
||||||
return creator(F.Flow2(sa.Get, fromPred), func(s S, a O.Option[A]) S {
|
|
||||||
return F.Pipe2(
|
|
||||||
a,
|
|
||||||
fold,
|
|
||||||
EM.Ap(s),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
|
||||||
// if the optional value is set then the nil value will be set instead
|
|
||||||
func FromPredicate[S, A any](pred func(A) bool, nilValue A) func(sa Lens[S, A]) Lens[S, O.Option[A]] {
|
|
||||||
return fromPredicate(MakeLens[func(S) O.Option[A], func(S, O.Option[A]) S], pred, nilValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromPredicateRef returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
|
||||||
// if the optional value is set then the nil value will be set instead
|
|
||||||
func FromPredicateRef[S, A any](pred func(A) bool, nilValue A) func(sa Lens[*S, A]) Lens[*S, O.Option[A]] {
|
|
||||||
return fromPredicate(MakeLensRef[func(*S) O.Option[A], func(*S, O.Option[A]) *S], pred, nilValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
|
||||||
// if the optional value is set then the `nil` value will be set instead
|
|
||||||
func FromNillable[S, A any](sa Lens[S, *A]) Lens[S, O.Option[*A]] {
|
|
||||||
return FromPredicate[S](F.IsNonNil[A], nil)(sa)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromNillableRef returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
|
||||||
// if the optional value is set then the `nil` value will be set instead
|
|
||||||
func FromNillableRef[S, A any](sa Lens[*S, *A]) Lens[*S, O.Option[*A]] {
|
|
||||||
return FromPredicateRef[S](F.IsNonNil[A], nil)(sa)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items
|
|
||||||
func fromNullableProp[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] {
|
|
||||||
return func(sa Lens[S, A]) Lens[S, A] {
|
|
||||||
return creator(F.Flow3(
|
|
||||||
sa.Get,
|
|
||||||
isNullable,
|
|
||||||
O.GetOrElse(F.Constant(defaultValue)),
|
|
||||||
), func(s S, a A) S {
|
|
||||||
return sa.Set(a)(s)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items
|
|
||||||
func FromNullableProp[S, A any](isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] {
|
|
||||||
return fromNullableProp(MakeLens[func(S) A, func(S, A) S], isNullable, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromNullablePropRef returns a `Lens` from a property that may be optional. The getter returns a default value for these items
|
|
||||||
func FromNullablePropRef[S, A any](isNullable func(A) O.Option[A], defaultValue A) func(sa Lens[*S, A]) Lens[*S, A] {
|
|
||||||
return fromNullableProp(MakeLensRef[func(*S) A, func(*S, A) *S], isNullable, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fromFromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
|
|
||||||
func fromOption[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], defaultValue A) func(sa Lens[S, O.Option[A]]) Lens[S, A] {
|
|
||||||
return func(sa Lens[S, O.Option[A]]) Lens[S, A] {
|
|
||||||
return creator(F.Flow2(
|
|
||||||
sa.Get,
|
|
||||||
O.GetOrElse(F.Constant(defaultValue)),
|
|
||||||
), func(s S, a A) S {
|
|
||||||
return sa.Set(O.Some(a))(s)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromFromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
|
|
||||||
func FromOption[S, A any](defaultValue A) func(sa Lens[S, O.Option[A]]) Lens[S, A] {
|
|
||||||
return fromOption(MakeLens[func(S) A, func(S, A) S], defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromFromOptionRef returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
|
|
||||||
func FromOptionRef[S, A any](defaultValue A) func(sa Lens[*S, O.Option[A]]) Lens[*S, A] {
|
|
||||||
return fromOption(MakeLensRef[func(*S) A, func(*S, A) *S], defaultValue)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -172,83 +171,6 @@ func TestPassByValue(t *testing.T) {
|
|||||||
assert.Equal(t, "value2", s2.name)
|
assert.Equal(t, "value2", s2.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromNullableProp(t *testing.T) {
|
|
||||||
// default inner object
|
|
||||||
defaultInner := &Inner{
|
|
||||||
Value: 0,
|
|
||||||
Foo: "foo",
|
|
||||||
}
|
|
||||||
// access to the value
|
|
||||||
value := MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
|
||||||
// access to inner
|
|
||||||
inner := FromNullableProp[Outer](O.FromNillable[Inner], defaultInner)(MakeLens(Outer.GetInner, Outer.SetInner))
|
|
||||||
// compose
|
|
||||||
lens := F.Pipe1(
|
|
||||||
inner,
|
|
||||||
Compose[Outer](value),
|
|
||||||
)
|
|
||||||
outer1 := Outer{inner: &Inner{Value: 1, Foo: "a"}}
|
|
||||||
// the checks
|
|
||||||
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(1)(Outer{}))
|
|
||||||
assert.Equal(t, 0, lens.Get(Outer{}))
|
|
||||||
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(1)(Outer{inner: &Inner{Value: 2, Foo: "foo"}}))
|
|
||||||
assert.Equal(t, 1, lens.Get(Outer{inner: &Inner{Value: 1, Foo: "foo"}}))
|
|
||||||
assert.Equal(t, outer1, Modify[Outer](F.Identity[int])(lens)(outer1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestComposeOption(t *testing.T) {
|
|
||||||
// default inner object
|
|
||||||
defaultInner := &Inner{
|
|
||||||
Value: 0,
|
|
||||||
Foo: "foo",
|
|
||||||
}
|
|
||||||
// access to the value
|
|
||||||
value := MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
|
||||||
// access to inner
|
|
||||||
inner := FromNillable(MakeLens(Outer.GetInner, Outer.SetInner))
|
|
||||||
// compose lenses
|
|
||||||
lens := F.Pipe1(
|
|
||||||
inner,
|
|
||||||
ComposeOption[Outer, int](defaultInner)(value),
|
|
||||||
)
|
|
||||||
outer1 := Outer{inner: &Inner{Value: 1, Foo: "a"}}
|
|
||||||
// the checks
|
|
||||||
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(O.Some(1))(Outer{}))
|
|
||||||
assert.Equal(t, O.None[int](), lens.Get(Outer{}))
|
|
||||||
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(O.Some(1))(Outer{inner: &Inner{Value: 2, Foo: "foo"}}))
|
|
||||||
assert.Equal(t, O.Some(1), lens.Get(Outer{inner: &Inner{Value: 1, Foo: "foo"}}))
|
|
||||||
assert.Equal(t, outer1, Modify[Outer](F.Identity[O.Option[int]])(lens)(outer1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestComposeOptions(t *testing.T) {
|
|
||||||
// default inner object
|
|
||||||
defaultValue1 := 1
|
|
||||||
defaultFoo1 := "foo1"
|
|
||||||
defaultInner := &InnerOpt{
|
|
||||||
Value: &defaultValue1,
|
|
||||||
Foo: &defaultFoo1,
|
|
||||||
}
|
|
||||||
// access to the value
|
|
||||||
value := FromNillable(MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue))
|
|
||||||
// access to inner
|
|
||||||
inner := FromNillable(MakeLens(OuterOpt.GetInnerOpt, OuterOpt.SetInnerOpt))
|
|
||||||
// compose lenses
|
|
||||||
lens := F.Pipe1(
|
|
||||||
inner,
|
|
||||||
ComposeOptions[OuterOpt, *int](defaultInner)(value),
|
|
||||||
)
|
|
||||||
// additional settings
|
|
||||||
defaultValue2 := 2
|
|
||||||
defaultFoo2 := "foo2"
|
|
||||||
outer1 := OuterOpt{inner: &InnerOpt{Value: &defaultValue2, Foo: &defaultFoo2}}
|
|
||||||
// the checks
|
|
||||||
assert.Equal(t, OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo1}}, lens.Set(O.Some(&defaultValue1))(OuterOpt{}))
|
|
||||||
assert.Equal(t, O.None[*int](), lens.Get(OuterOpt{}))
|
|
||||||
assert.Equal(t, OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo2}}, lens.Set(O.Some(&defaultValue1))(OuterOpt{inner: &InnerOpt{Value: &defaultValue2, Foo: &defaultFoo2}}))
|
|
||||||
assert.Equal(t, O.Some(&defaultValue1), lens.Get(OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo1}}))
|
|
||||||
assert.Equal(t, outer1, Modify[OuterOpt](F.Identity[O.Option[*int]])(lens)(outer1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIdRef(t *testing.T) {
|
func TestIdRef(t *testing.T) {
|
||||||
idLens := IdRef[Street]()
|
idLens := IdRef[Street]()
|
||||||
street := &Street{num: 1, name: "Main"}
|
street := &Street{num: 1, name: "Main"}
|
||||||
@@ -272,93 +194,6 @@ func TestComposeRef(t *testing.T) {
|
|||||||
assert.Equal(t, sampleStreet.name, sampleAddress.street.name) // Original unchanged
|
assert.Equal(t, sampleStreet.name, sampleAddress.street.name) // Original unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromPredicateRef(t *testing.T) {
|
|
||||||
type Person struct {
|
|
||||||
age int
|
|
||||||
}
|
|
||||||
|
|
||||||
ageLens := MakeLensRef(
|
|
||||||
func(p *Person) int { return p.age },
|
|
||||||
func(p *Person, age int) *Person {
|
|
||||||
p.age = age
|
|
||||||
return p
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
adultLens := FromPredicateRef[Person](func(age int) bool { return age >= 18 }, 0)(ageLens)
|
|
||||||
|
|
||||||
adult := &Person{age: 25}
|
|
||||||
assert.Equal(t, O.Some(25), adultLens.Get(adult))
|
|
||||||
|
|
||||||
minor := &Person{age: 15}
|
|
||||||
assert.Equal(t, O.None[int](), adultLens.Get(minor))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromNillableRef(t *testing.T) {
|
|
||||||
type Config struct {
|
|
||||||
timeout *int
|
|
||||||
}
|
|
||||||
|
|
||||||
timeoutLens := MakeLensRef(
|
|
||||||
func(c *Config) *int { return c.timeout },
|
|
||||||
func(c *Config, t *int) *Config {
|
|
||||||
c.timeout = t
|
|
||||||
return c
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
optLens := FromNillableRef(timeoutLens)
|
|
||||||
|
|
||||||
config := &Config{timeout: nil}
|
|
||||||
assert.Equal(t, O.None[*int](), optLens.Get(config))
|
|
||||||
|
|
||||||
timeout := 30
|
|
||||||
configWithTimeout := &Config{timeout: &timeout}
|
|
||||||
assert.True(t, O.IsSome(optLens.Get(configWithTimeout)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromNullablePropRef(t *testing.T) {
|
|
||||||
type Config struct {
|
|
||||||
timeout *int
|
|
||||||
}
|
|
||||||
|
|
||||||
timeoutLens := MakeLensRef(
|
|
||||||
func(c *Config) *int { return c.timeout },
|
|
||||||
func(c *Config, t *int) *Config {
|
|
||||||
c.timeout = t
|
|
||||||
return c
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
defaultTimeout := 30
|
|
||||||
safeLens := FromNullablePropRef[Config](O.FromNillable[int], &defaultTimeout)(timeoutLens)
|
|
||||||
|
|
||||||
config := &Config{timeout: nil}
|
|
||||||
assert.Equal(t, &defaultTimeout, safeLens.Get(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromOptionRef(t *testing.T) {
|
|
||||||
type Settings struct {
|
|
||||||
retries O.Option[int]
|
|
||||||
}
|
|
||||||
|
|
||||||
retriesLens := MakeLensRef(
|
|
||||||
func(s *Settings) O.Option[int] { return s.retries },
|
|
||||||
func(s *Settings, r O.Option[int]) *Settings {
|
|
||||||
s.retries = r
|
|
||||||
return s
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
safeLens := FromOptionRef[Settings](3)(retriesLens)
|
|
||||||
|
|
||||||
settings := &Settings{retries: O.None[int]()}
|
|
||||||
assert.Equal(t, 3, safeLens.Get(settings))
|
|
||||||
|
|
||||||
settingsWithRetries := &Settings{retries: O.Some(5)}
|
|
||||||
assert.Equal(t, 5, safeLens.Get(settingsWithRetries))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMakeLensCurried(t *testing.T) {
|
func TestMakeLensCurried(t *testing.T) {
|
||||||
nameLens := MakeLensCurried(
|
nameLens := MakeLensCurried(
|
||||||
func(s Street) string { return s.name },
|
func(s Street) string { return s.name },
|
||||||
|
|||||||
192
v2/optics/lens/option/compose.go
Normal file
192
v2/optics/lens/option/compose.go
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compose composes two lenses that both return optional values.
|
||||||
|
//
|
||||||
|
// This handles the case where both the intermediate structure A and the inner focus B are optional.
|
||||||
|
// The getter returns None[B] if either A or B is None. The setter behavior is:
|
||||||
|
// - Set(Some[B]) when A exists: Updates B in A
|
||||||
|
// - Set(Some[B]) when A doesn't exist: Creates A with defaultA and sets B
|
||||||
|
// - Set(None[B]) when A doesn't exist: Identity operation (no change)
|
||||||
|
// - Set(None[B]) when A exists: Removes B from A (sets it to None)
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: Outer structure type
|
||||||
|
// - B: Inner focus type (optional)
|
||||||
|
// - A: Intermediate structure type (optional)
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - defaultA: Default value for A when it doesn't exist but B needs to be set
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a LensO[A, B] and returns a function that takes a
|
||||||
|
// LensO[S, A] and returns a LensO[S, B]
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Settings struct {
|
||||||
|
// MaxRetries *int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type Config struct {
|
||||||
|
// Settings *Settings
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// settingsLens := lens.FromNillable(lens.MakeLens(
|
||||||
|
// func(c Config) *Settings { return c.Settings },
|
||||||
|
// func(c Config, s *Settings) Config { c.Settings = s; return c },
|
||||||
|
// ))
|
||||||
|
//
|
||||||
|
// retriesLens := lens.FromNillable(lens.MakeLensRef(
|
||||||
|
// func(s *Settings) *int { return s.MaxRetries },
|
||||||
|
// func(s *Settings, r *int) *Settings { s.MaxRetries = r; return s },
|
||||||
|
// ))
|
||||||
|
//
|
||||||
|
// defaultSettings := &Settings{}
|
||||||
|
// configRetriesLens := F.Pipe1(settingsLens,
|
||||||
|
// lens.Compose[Config, *int](defaultSettings)(retriesLens))
|
||||||
|
func Compose[S, B, A any](defaultA A) func(ab LensO[A, B]) func(LensO[S, A]) LensO[S, B] {
|
||||||
|
noneb := O.None[B]()
|
||||||
|
return func(ab LensO[A, B]) func(LensO[S, A]) LensO[S, B] {
|
||||||
|
abGet := ab.Get
|
||||||
|
abSetNone := ab.Set(noneb)
|
||||||
|
return func(sa LensO[S, A]) LensO[S, B] {
|
||||||
|
saGet := sa.Get
|
||||||
|
// Pre-compute setter for Some[A]
|
||||||
|
setSomeA := F.Flow2(O.Some[A], sa.Set)
|
||||||
|
return lens.MakeLensCurried(
|
||||||
|
F.Flow2(saGet, O.Chain(abGet)),
|
||||||
|
func(optB Option[B]) Endomorphism[S] {
|
||||||
|
return func(s S) S {
|
||||||
|
optA := saGet(s)
|
||||||
|
return O.MonadFold(
|
||||||
|
optB,
|
||||||
|
// optB is None
|
||||||
|
func() S {
|
||||||
|
return O.MonadFold(
|
||||||
|
optA,
|
||||||
|
// optA is None - no-op
|
||||||
|
F.Constant(s),
|
||||||
|
// optA is Some - unset B in A
|
||||||
|
func(a A) S {
|
||||||
|
return setSomeA(abSetNone(a))(s)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
// optB is Some
|
||||||
|
func(b B) S {
|
||||||
|
setB := ab.Set(O.Some(b))
|
||||||
|
return O.MonadFold(
|
||||||
|
optA,
|
||||||
|
// optA is None - create with defaultA
|
||||||
|
func() S {
|
||||||
|
return setSomeA(setB(defaultA))(s)
|
||||||
|
},
|
||||||
|
// optA is Some - update B in A
|
||||||
|
func(a A) S {
|
||||||
|
return setSomeA(setB(a))(s)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComposeOption composes a lens returning an optional value with a lens returning a definite value.
|
||||||
|
//
|
||||||
|
// This is useful when you have an optional intermediate structure and want to focus on a field
|
||||||
|
// within it. The getter returns Option[B] because the container A might not exist. The setter
|
||||||
|
// behavior depends on the input:
|
||||||
|
// - Set(Some[B]): Updates B in A, creating A with defaultA if it doesn't exist
|
||||||
|
// - Set(None[B]): Removes A entirely (sets it to None[A])
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: Outer structure type
|
||||||
|
// - B: Inner focus type (definite value)
|
||||||
|
// - A: Intermediate structure type (optional)
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - defaultA: Default value for A when it doesn't exist but B needs to be set
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a Lens[A, B] and returns a function that takes a
|
||||||
|
// LensO[S, A] and returns a LensO[S, B]
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Database struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type Config struct {
|
||||||
|
// Database *Database
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// dbLens := lens.FromNillable(lens.MakeLens(
|
||||||
|
// func(c Config) *Database { return c.Database },
|
||||||
|
// func(c Config, db *Database) Config { c.Database = db; return c },
|
||||||
|
// ))
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLensRef(
|
||||||
|
// func(db *Database) int { return db.Port },
|
||||||
|
// func(db *Database, port int) *Database { db.Port = port; return db },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// defaultDB := &Database{Host: "localhost", Port: 5432}
|
||||||
|
// configPortLens := F.Pipe1(dbLens, lens.ComposeOption[Config, int](defaultDB)(portLens))
|
||||||
|
//
|
||||||
|
// config := Config{Database: nil}
|
||||||
|
// port := configPortLens.Get(config) // None[int]
|
||||||
|
// updated := configPortLens.Set(O.Some(3306))(config)
|
||||||
|
// // updated.Database.Port == 3306, Host == "localhost" (from default)
|
||||||
|
func ComposeOption[S, B, A any](defaultA A) func(ab Lens[A, B]) func(LensO[S, A]) LensO[S, B] {
|
||||||
|
return func(ab Lens[A, B]) func(LensO[S, A]) LensO[S, B] {
|
||||||
|
abGet := ab.Get
|
||||||
|
abSet := ab.Set
|
||||||
|
return func(sa LensO[S, A]) LensO[S, B] {
|
||||||
|
saGet := sa.Get
|
||||||
|
saSet := sa.Set
|
||||||
|
// Pre-compute setters
|
||||||
|
setNoneA := saSet(O.None[A]())
|
||||||
|
setSomeA := func(a A) Endomorphism[S] {
|
||||||
|
return saSet(O.Some(a))
|
||||||
|
}
|
||||||
|
return lens.MakeLens(
|
||||||
|
func(s S) Option[B] {
|
||||||
|
return O.Map(abGet)(saGet(s))
|
||||||
|
},
|
||||||
|
func(s S, optB Option[B]) S {
|
||||||
|
return O.Fold(
|
||||||
|
// optB is None - remove A entirely
|
||||||
|
F.Constant(setNoneA(s)),
|
||||||
|
// optB is Some - set B
|
||||||
|
func(b B) S {
|
||||||
|
optA := saGet(s)
|
||||||
|
return O.Fold(
|
||||||
|
// optA is None - create with defaultA
|
||||||
|
func() S {
|
||||||
|
return setSomeA(abSet(b)(defaultA))(s)
|
||||||
|
},
|
||||||
|
// optA is Some - update B in A
|
||||||
|
func(a A) S {
|
||||||
|
return setSomeA(abSet(b)(a))(s)
|
||||||
|
},
|
||||||
|
)(optA)
|
||||||
|
},
|
||||||
|
)(optB)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
v2/optics/lens/option/coverage.out
Normal file
31
v2/optics/lens/option/coverage.out
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
mode: count
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:55.97,59.60 4 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:59.60,61.43 2 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:61.43,72.39 2 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:72.39,73.25 1 13
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:73.25,74.52 1 13
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:74.52,80.8 1 6
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:80.36,91.8 2 7
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:147.95,149.59 2 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:149.59,151.43 2 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:151.43,164.31 3 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:164.31,167.48 1 12
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/compose.go:167.48,183.8 2 7
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:12.188,14.41 2 15
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:14.41,16.70 2 15
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:16.70,22.4 1 60
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:28.93,30.2 1 12
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:34.105,36.2 1 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:40.65,42.2 1 10
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:46.70,48.2 1 2
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:51.188,52.40 1 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:52.40,57.23 1 3
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:57.23,59.4 1 4
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:65.110,67.2 1 2
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:70.115,72.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:75.153,76.41 1 2
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:76.41,80.23 1 2
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:80.23,82.4 1 2
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:88.75,90.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/from.go:107.87,109.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/optics/lens/option/option.go:63.67,65.2 1 1
|
||||||
138
v2/optics/lens/option/doc.go
Normal file
138
v2/optics/lens/option/doc.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package option provides utilities for working with lenses that focus on optional values.
|
||||||
|
//
|
||||||
|
// This package extends the lens optics pattern to handle Option types, enabling safe
|
||||||
|
// manipulation of potentially absent values in nested data structures. It provides
|
||||||
|
// functions for creating, composing, and transforming lenses that work with optional
|
||||||
|
// fields.
|
||||||
|
//
|
||||||
|
// # Core Concepts
|
||||||
|
//
|
||||||
|
// A LensO[S, A] is a Lens[S, Option[A]] - a lens that focuses on an optional value A
|
||||||
|
// within a structure S. This is particularly useful when dealing with nullable pointers,
|
||||||
|
// optional fields, or values that may not always be present.
|
||||||
|
//
|
||||||
|
// # Key Functions
|
||||||
|
//
|
||||||
|
// Creating Lenses from Optional Values:
|
||||||
|
// - FromNillable: Creates a lens from a nullable pointer field
|
||||||
|
// - FromNillableRef: Pointer-based version of FromNillable
|
||||||
|
// - FromPredicate: Creates a lens based on a predicate function
|
||||||
|
// - FromPredicateRef: Pointer-based version of FromPredicate
|
||||||
|
// - FromOption: Converts an optional lens to a definite lens with a default value
|
||||||
|
// - FromOptionRef: Pointer-based version of FromOption
|
||||||
|
// - FromNullableProp: Creates a lens with a default value for nullable properties
|
||||||
|
// - FromNullablePropRef: Pointer-based version of FromNullableProp
|
||||||
|
//
|
||||||
|
// Composing Lenses:
|
||||||
|
// - ComposeOption: Composes a lens returning Option[A] with a lens returning B
|
||||||
|
// - ComposeOptions: Composes two lenses that both return optional values
|
||||||
|
//
|
||||||
|
// Conversions:
|
||||||
|
// - AsTraversal: Converts a lens to a traversal for use with traversal operations
|
||||||
|
//
|
||||||
|
// # Usage Examples
|
||||||
|
//
|
||||||
|
// Working with nullable pointers:
|
||||||
|
//
|
||||||
|
// type Config struct {
|
||||||
|
// Database *DatabaseConfig
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type DatabaseConfig struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Create a lens for the optional database config
|
||||||
|
// dbLens := lens.FromNillable(lens.MakeLens(
|
||||||
|
// func(c Config) *DatabaseConfig { return c.Database },
|
||||||
|
// func(c Config, db *DatabaseConfig) Config { c.Database = db; return c },
|
||||||
|
// ))
|
||||||
|
//
|
||||||
|
// // Access the optional value
|
||||||
|
// config := Config{Database: nil}
|
||||||
|
// dbOpt := dbLens.Get(config) // Returns None[*DatabaseConfig]
|
||||||
|
//
|
||||||
|
// // Set a value
|
||||||
|
// newDB := &DatabaseConfig{Host: "localhost", Port: 5432}
|
||||||
|
// updated := dbLens.Set(O.Some(newDB))(config)
|
||||||
|
//
|
||||||
|
// Composing optional lenses:
|
||||||
|
//
|
||||||
|
// // Lens to access port through optional database
|
||||||
|
// portLens := lens.MakeLensRef(
|
||||||
|
// func(db *DatabaseConfig) int { return db.Port },
|
||||||
|
// func(db *DatabaseConfig, port int) *DatabaseConfig { db.Port = port; return db },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// defaultDB := &DatabaseConfig{Host: "localhost", Port: 5432}
|
||||||
|
// configPortLens := F.Pipe1(dbLens,
|
||||||
|
// lens.ComposeOption[Config, int](defaultDB)(portLens))
|
||||||
|
//
|
||||||
|
// // Get returns None if database is not set
|
||||||
|
// port := configPortLens.Get(config) // None[int]
|
||||||
|
//
|
||||||
|
// // Set creates the database with default values if needed
|
||||||
|
// withPort := configPortLens.Set(O.Some(3306))(config)
|
||||||
|
// // withPort.Database.Port == 3306, Host == "localhost"
|
||||||
|
//
|
||||||
|
// Working with predicates:
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ageLens := lens.MakeLensRef(
|
||||||
|
// func(p *Person) int { return p.Age },
|
||||||
|
// func(p *Person, age int) *Person { p.Age = age; return p },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Only consider adults (age >= 18)
|
||||||
|
// adultLens := lens.FromPredicateRef[Person](
|
||||||
|
// func(age int) bool { return age >= 18 },
|
||||||
|
// 0, // nil value for non-adults
|
||||||
|
// )(ageLens)
|
||||||
|
//
|
||||||
|
// adult := &Person{Age: 25}
|
||||||
|
// adultLens.Get(adult) // Some(25)
|
||||||
|
//
|
||||||
|
// minor := &Person{Age: 15}
|
||||||
|
// adultLens.Get(minor) // None[int]
|
||||||
|
//
|
||||||
|
// # Design Patterns
|
||||||
|
//
|
||||||
|
// The package follows functional programming principles:
|
||||||
|
// - Immutability: All operations return new values rather than modifying in place
|
||||||
|
// - Composition: Lenses can be composed to access deeply nested optional values
|
||||||
|
// - Type Safety: The type system ensures correct usage at compile time
|
||||||
|
// - Lawful: All lenses satisfy the lens laws (get-put, put-get, put-put)
|
||||||
|
//
|
||||||
|
// # Performance Considerations
|
||||||
|
//
|
||||||
|
// Lens operations are generally efficient, but composing many lenses can create
|
||||||
|
// function call overhead. For performance-critical code, consider:
|
||||||
|
// - Caching composed lenses rather than recreating them
|
||||||
|
// - Using direct field access for simple cases
|
||||||
|
// - Profiling to identify bottlenecks
|
||||||
|
//
|
||||||
|
// # Related Packages
|
||||||
|
//
|
||||||
|
// - github.com/IBM/fp-go/v2/optics/lens: Core lens functionality
|
||||||
|
// - github.com/IBM/fp-go/v2/option: Option type and operations
|
||||||
|
// - github.com/IBM/fp-go/v2/optics/traversal/option: Traversals for optional values
|
||||||
|
package option
|
||||||
109
v2/optics/lens/option/from.go
Normal file
109
v2/optics/lens/option/from.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
EM "github.com/IBM/fp-go/v2/endomorphism"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
||||||
|
// if the optional value is set then the nil value will be set instead
|
||||||
|
func fromPredicate[GET ~func(S) Option[A], SET ~func(S, Option[A]) S, S, A any](creator func(get GET, set SET) LensO[S, A], pred func(A) bool, nilValue A) func(sa Lens[S, A]) LensO[S, A] {
|
||||||
|
fromPred := O.FromPredicate(pred)
|
||||||
|
return func(sa Lens[S, A]) LensO[S, A] {
|
||||||
|
fold := O.Fold(F.Bind1of1(sa.Set)(nilValue), sa.Set)
|
||||||
|
return creator(F.Flow2(sa.Get, fromPred), func(s S, a Option[A]) S {
|
||||||
|
return F.Pipe2(
|
||||||
|
a,
|
||||||
|
fold,
|
||||||
|
EM.Ap(s),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
||||||
|
// if the optional value is set then the nil value will be set instead
|
||||||
|
func FromPredicate[S, A any](pred func(A) bool, nilValue A) func(sa Lens[S, A]) LensO[S, A] {
|
||||||
|
return fromPredicate(lens.MakeLens[func(S) Option[A], func(S, Option[A]) S], pred, nilValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPredicateRef returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
||||||
|
// if the optional value is set then the nil value will be set instead
|
||||||
|
func FromPredicateRef[S, A any](pred func(A) bool, nilValue A) func(sa Lens[*S, A]) Lens[*S, Option[A]] {
|
||||||
|
return fromPredicate(lens.MakeLensRef[func(*S) Option[A], func(*S, Option[A]) *S], pred, nilValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPredicate returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
||||||
|
// if the optional value is set then the `nil` value will be set instead
|
||||||
|
func FromNillable[S, A any](sa Lens[S, *A]) Lens[S, Option[*A]] {
|
||||||
|
return FromPredicate[S](F.IsNonNil[A], nil)(sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromNillableRef returns a `Lens` for a property accessibly as a getter and setter that can be optional
|
||||||
|
// if the optional value is set then the `nil` value will be set instead
|
||||||
|
func FromNillableRef[S, A any](sa Lens[*S, *A]) Lens[*S, Option[*A]] {
|
||||||
|
return FromPredicateRef[S](F.IsNonNil[A], nil)(sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items
|
||||||
|
func fromNullableProp[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], isNullable func(A) Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] {
|
||||||
|
return func(sa Lens[S, A]) Lens[S, A] {
|
||||||
|
return creator(F.Flow3(
|
||||||
|
sa.Get,
|
||||||
|
isNullable,
|
||||||
|
O.GetOrElse(F.Constant(defaultValue)),
|
||||||
|
), func(s S, a A) S {
|
||||||
|
return sa.Set(a)(s)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromNullableProp returns a `Lens` from a property that may be optional. The getter returns a default value for these items
|
||||||
|
func FromNullableProp[S, A any](isNullable func(A) Option[A], defaultValue A) func(sa Lens[S, A]) Lens[S, A] {
|
||||||
|
return fromNullableProp(lens.MakeLens[func(S) A, func(S, A) S], isNullable, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromNullablePropRef returns a `Lens` from a property that may be optional. The getter returns a default value for these items
|
||||||
|
func FromNullablePropRef[S, A any](isNullable func(A) Option[A], defaultValue A) func(sa Lens[*S, A]) Lens[*S, A] {
|
||||||
|
return fromNullableProp(lens.MakeLensRef[func(*S) A, func(*S, A) *S], isNullable, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
|
||||||
|
func fromOption[GET ~func(S) A, SET ~func(S, A) S, S, A any](creator func(get GET, set SET) Lens[S, A], defaultValue A) func(sa LensO[S, A]) Lens[S, A] {
|
||||||
|
return func(sa LensO[S, A]) Lens[S, A] {
|
||||||
|
return creator(F.Flow2(
|
||||||
|
sa.Get,
|
||||||
|
O.GetOrElse(F.Constant(defaultValue)),
|
||||||
|
), func(s S, a A) S {
|
||||||
|
return sa.Set(O.Some(a))(s)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromOption returns a `Lens` from an option property. The getter returns a default value the setter will always set the some option
|
||||||
|
func FromOption[S, A any](defaultValue A) func(sa LensO[S, A]) Lens[S, A] {
|
||||||
|
return fromOption(lens.MakeLens[func(S) A, func(S, A) S], defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromOptionRef creates a lens from an Option property with a default value for pointer structures.
|
||||||
|
//
|
||||||
|
// This is the pointer version of [FromOption], with automatic copying to ensure immutability.
|
||||||
|
// The getter returns the value inside Some[A], or the defaultValue if it's None[A].
|
||||||
|
// The setter always wraps the value in Some[A].
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: Structure type (will be used as *S)
|
||||||
|
// - A: Focus type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - defaultValue: Value to return when the Option is None
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a Lens[*S, Option[A]] and returns a Lens[*S, A]
|
||||||
|
func FromOptionRef[S, A any](defaultValue A) func(sa Lens[*S, Option[A]]) Lens[*S, A] {
|
||||||
|
return fromOption(lens.MakeLensRef[func(*S) A, func(*S, A) *S], defaultValue)
|
||||||
|
}
|
||||||
759
v2/optics/lens/option/lens_test.go
Normal file
759
v2/optics/lens/option/lens_test.go
Normal file
@@ -0,0 +1,759 @@
|
|||||||
|
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
EQT "github.com/IBM/fp-go/v2/eq/testing"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Street struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
Address struct {
|
||||||
|
street *Street
|
||||||
|
}
|
||||||
|
|
||||||
|
Inner struct {
|
||||||
|
Value int
|
||||||
|
Foo string
|
||||||
|
}
|
||||||
|
|
||||||
|
InnerOpt struct {
|
||||||
|
Value *int
|
||||||
|
Foo *string
|
||||||
|
}
|
||||||
|
|
||||||
|
Outer struct {
|
||||||
|
inner *Inner
|
||||||
|
}
|
||||||
|
|
||||||
|
OuterOpt struct {
|
||||||
|
inner *InnerOpt
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (outer Outer) GetInner() *Inner {
|
||||||
|
return outer.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (outer Outer) SetInner(inner *Inner) Outer {
|
||||||
|
outer.inner = inner
|
||||||
|
return outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (outer OuterOpt) GetInnerOpt() *InnerOpt {
|
||||||
|
return outer.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (outer OuterOpt) SetInnerOpt(inner *InnerOpt) OuterOpt {
|
||||||
|
outer.inner = inner
|
||||||
|
return outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *Inner) GetValue() int {
|
||||||
|
return inner.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *Inner) SetValue(value int) *Inner {
|
||||||
|
inner.Value = value
|
||||||
|
return inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *InnerOpt) GetValue() *int {
|
||||||
|
return inner.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *InnerOpt) SetValue(value *int) *InnerOpt {
|
||||||
|
inner.Value = value
|
||||||
|
return inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (street *Street) GetName() string {
|
||||||
|
return street.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (street *Street) SetName(name string) *Street {
|
||||||
|
street.name = name
|
||||||
|
return street
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *Address) GetStreet() *Street {
|
||||||
|
return addr.street
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *Address) SetStreet(s *Street) *Address {
|
||||||
|
addr.street = s
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
streetLens = L.MakeLensRef((*Street).GetName, (*Street).SetName)
|
||||||
|
addrLens = L.MakeLensRef((*Address).GetStreet, (*Address).SetStreet)
|
||||||
|
|
||||||
|
sampleStreet = Street{name: "Schönaicherstr"}
|
||||||
|
sampleAddress = Address{street: &sampleStreet}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestComposeOption(t *testing.T) {
|
||||||
|
// default inner object
|
||||||
|
defaultInner := &Inner{
|
||||||
|
Value: 0,
|
||||||
|
Foo: "foo",
|
||||||
|
}
|
||||||
|
// access to the value
|
||||||
|
value := L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
||||||
|
// access to inner
|
||||||
|
inner := FromNillable(L.MakeLens(Outer.GetInner, Outer.SetInner))
|
||||||
|
// compose lenses
|
||||||
|
lens := F.Pipe1(
|
||||||
|
inner,
|
||||||
|
ComposeOption[Outer, int](defaultInner)(value),
|
||||||
|
)
|
||||||
|
outer1 := Outer{inner: &Inner{Value: 1, Foo: "a"}}
|
||||||
|
// the checks
|
||||||
|
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(O.Some(1))(Outer{}))
|
||||||
|
assert.Equal(t, O.None[int](), lens.Get(Outer{}))
|
||||||
|
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(O.Some(1))(Outer{inner: &Inner{Value: 2, Foo: "foo"}}))
|
||||||
|
assert.Equal(t, O.Some(1), lens.Get(Outer{inner: &Inner{Value: 1, Foo: "foo"}}))
|
||||||
|
assert.Equal(t, outer1, L.Modify[Outer](F.Identity[Option[int]])(lens)(outer1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComposeOptions(t *testing.T) {
|
||||||
|
// default inner object
|
||||||
|
defaultValue1 := 1
|
||||||
|
defaultFoo1 := "foo1"
|
||||||
|
defaultInner := &InnerOpt{
|
||||||
|
Value: &defaultValue1,
|
||||||
|
Foo: &defaultFoo1,
|
||||||
|
}
|
||||||
|
// access to the value
|
||||||
|
value := FromNillable(L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue))
|
||||||
|
// access to inner
|
||||||
|
inner := FromNillable(L.MakeLens(OuterOpt.GetInnerOpt, OuterOpt.SetInnerOpt))
|
||||||
|
// compose lenses
|
||||||
|
lens := F.Pipe1(
|
||||||
|
inner,
|
||||||
|
Compose[OuterOpt, *int](defaultInner)(value),
|
||||||
|
)
|
||||||
|
// additional settings
|
||||||
|
defaultValue2 := 2
|
||||||
|
defaultFoo2 := "foo2"
|
||||||
|
outer1 := OuterOpt{inner: &InnerOpt{Value: &defaultValue2, Foo: &defaultFoo2}}
|
||||||
|
// the checks
|
||||||
|
assert.Equal(t, OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo1}}, lens.Set(O.Some(&defaultValue1))(OuterOpt{}))
|
||||||
|
assert.Equal(t, O.None[*int](), lens.Get(OuterOpt{}))
|
||||||
|
assert.Equal(t, OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo2}}, lens.Set(O.Some(&defaultValue1))(OuterOpt{inner: &InnerOpt{Value: &defaultValue2, Foo: &defaultFoo2}}))
|
||||||
|
assert.Equal(t, O.Some(&defaultValue1), lens.Get(OuterOpt{inner: &InnerOpt{Value: &defaultValue1, Foo: &defaultFoo1}}))
|
||||||
|
assert.Equal(t, outer1, L.Modify[OuterOpt](F.Identity[Option[*int]])(lens)(outer1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromNullableProp(t *testing.T) {
|
||||||
|
// default inner object
|
||||||
|
defaultInner := &Inner{
|
||||||
|
Value: 0,
|
||||||
|
Foo: "foo",
|
||||||
|
}
|
||||||
|
// access to the value
|
||||||
|
value := L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
||||||
|
// access to inner
|
||||||
|
inner := FromNullableProp[Outer](O.FromNillable[Inner], defaultInner)(L.MakeLens(Outer.GetInner, Outer.SetInner))
|
||||||
|
// compose
|
||||||
|
lens := F.Pipe1(
|
||||||
|
inner,
|
||||||
|
L.Compose[Outer](value),
|
||||||
|
)
|
||||||
|
outer1 := Outer{inner: &Inner{Value: 1, Foo: "a"}}
|
||||||
|
// the checks
|
||||||
|
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(1)(Outer{}))
|
||||||
|
assert.Equal(t, 0, lens.Get(Outer{}))
|
||||||
|
assert.Equal(t, Outer{inner: &Inner{Value: 1, Foo: "foo"}}, lens.Set(1)(Outer{inner: &Inner{Value: 2, Foo: "foo"}}))
|
||||||
|
assert.Equal(t, 1, lens.Get(Outer{inner: &Inner{Value: 1, Foo: "foo"}}))
|
||||||
|
assert.Equal(t, outer1, L.Modify[Outer](F.Identity[int])(lens)(outer1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromPredicateRef(t *testing.T) {
|
||||||
|
type Person struct {
|
||||||
|
age int
|
||||||
|
}
|
||||||
|
|
||||||
|
ageLens := L.MakeLensRef(
|
||||||
|
func(p *Person) int { return p.age },
|
||||||
|
func(p *Person, age int) *Person {
|
||||||
|
p.age = age
|
||||||
|
return p
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
adultLens := FromPredicateRef[Person](func(age int) bool { return age >= 18 }, 0)(ageLens)
|
||||||
|
|
||||||
|
adult := &Person{age: 25}
|
||||||
|
assert.Equal(t, O.Some(25), adultLens.Get(adult))
|
||||||
|
|
||||||
|
minor := &Person{age: 15}
|
||||||
|
assert.Equal(t, O.None[int](), adultLens.Get(minor))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromNillableRef(t *testing.T) {
|
||||||
|
type Config struct {
|
||||||
|
timeout *int
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutLens := L.MakeLensRef(
|
||||||
|
func(c *Config) *int { return c.timeout },
|
||||||
|
func(c *Config, t *int) *Config {
|
||||||
|
c.timeout = t
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
optLens := FromNillableRef(timeoutLens)
|
||||||
|
|
||||||
|
config := &Config{timeout: nil}
|
||||||
|
assert.Equal(t, O.None[*int](), optLens.Get(config))
|
||||||
|
|
||||||
|
timeout := 30
|
||||||
|
configWithTimeout := &Config{timeout: &timeout}
|
||||||
|
assert.True(t, O.IsSome(optLens.Get(configWithTimeout)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromNullablePropRef(t *testing.T) {
|
||||||
|
type Config struct {
|
||||||
|
timeout *int
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutLens := L.MakeLensRef(
|
||||||
|
func(c *Config) *int { return c.timeout },
|
||||||
|
func(c *Config, t *int) *Config {
|
||||||
|
c.timeout = t
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultTimeout := 30
|
||||||
|
safeLens := FromNullablePropRef[Config](O.FromNillable[int], &defaultTimeout)(timeoutLens)
|
||||||
|
|
||||||
|
config := &Config{timeout: nil}
|
||||||
|
assert.Equal(t, &defaultTimeout, safeLens.Get(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromOptionRef(t *testing.T) {
|
||||||
|
type Settings struct {
|
||||||
|
retries Option[int]
|
||||||
|
}
|
||||||
|
|
||||||
|
retriesLens := L.MakeLensRef(
|
||||||
|
func(s *Settings) Option[int] { return s.retries },
|
||||||
|
func(s *Settings, r Option[int]) *Settings {
|
||||||
|
s.retries = r
|
||||||
|
return s
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
safeLens := FromOptionRef[Settings](3)(retriesLens)
|
||||||
|
|
||||||
|
settings := &Settings{retries: O.None[int]()}
|
||||||
|
assert.Equal(t, 3, safeLens.Get(settings))
|
||||||
|
|
||||||
|
settingsWithRetries := &Settings{retries: O.Some(5)}
|
||||||
|
assert.Equal(t, 5, safeLens.Get(settingsWithRetries))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromOption(t *testing.T) {
|
||||||
|
type Config struct {
|
||||||
|
retries Option[int]
|
||||||
|
}
|
||||||
|
|
||||||
|
retriesLens := L.MakeLens(
|
||||||
|
func(c Config) Option[int] { return c.retries },
|
||||||
|
func(c Config, r Option[int]) Config { c.retries = r; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultRetries := 3
|
||||||
|
safeLens := FromOption[Config](defaultRetries)(retriesLens)
|
||||||
|
|
||||||
|
// Test with None - should return default
|
||||||
|
config := Config{retries: O.None[int]()}
|
||||||
|
assert.Equal(t, defaultRetries, safeLens.Get(config))
|
||||||
|
|
||||||
|
// Test with Some - should return the value
|
||||||
|
configWithRetries := Config{retries: O.Some(5)}
|
||||||
|
assert.Equal(t, 5, safeLens.Get(configWithRetries))
|
||||||
|
|
||||||
|
// Test setter - should always set Some
|
||||||
|
updated := safeLens.Set(10)(config)
|
||||||
|
assert.Equal(t, O.Some(10), updated.retries)
|
||||||
|
|
||||||
|
// Test setter on existing Some - should replace
|
||||||
|
updated2 := safeLens.Set(7)(configWithRetries)
|
||||||
|
assert.Equal(t, O.Some(7), updated2.retries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsTraversal(t *testing.T) {
|
||||||
|
type Data struct {
|
||||||
|
value int
|
||||||
|
}
|
||||||
|
|
||||||
|
valueLens := L.MakeLens(
|
||||||
|
func(d Data) int { return d.value },
|
||||||
|
func(d Data, v int) Data { d.value = v; return d },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Convert lens to traversal
|
||||||
|
traversal := AsTraversal[Data, int]()(valueLens)
|
||||||
|
|
||||||
|
// Test that traversal is created (basic smoke test)
|
||||||
|
assert.NotNil(t, traversal)
|
||||||
|
|
||||||
|
// The traversal should work with the data
|
||||||
|
data := Data{value: 42}
|
||||||
|
|
||||||
|
// Verify the traversal can be used (it's a function that takes a functor)
|
||||||
|
// This is a basic smoke test to ensure the conversion works
|
||||||
|
assert.NotNil(t, data)
|
||||||
|
assert.Equal(t, 42, valueLens.Get(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComposeOptionsEdgeCases(t *testing.T) {
|
||||||
|
// Test setting None when inner doesn't exist
|
||||||
|
defaultValue1 := 1
|
||||||
|
defaultFoo1 := "foo1"
|
||||||
|
defaultInner := &InnerOpt{
|
||||||
|
Value: &defaultValue1,
|
||||||
|
Foo: &defaultFoo1,
|
||||||
|
}
|
||||||
|
|
||||||
|
value := FromNillable(L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue))
|
||||||
|
inner := FromNillable(L.MakeLens(OuterOpt.GetInnerOpt, OuterOpt.SetInnerOpt))
|
||||||
|
lens := F.Pipe1(
|
||||||
|
inner,
|
||||||
|
Compose[OuterOpt, *int](defaultInner)(value),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Setting None when inner doesn't exist should be a no-op
|
||||||
|
emptyOuter := OuterOpt{}
|
||||||
|
result := lens.Set(O.None[*int]())(emptyOuter)
|
||||||
|
assert.Equal(t, O.None[*InnerOpt](), inner.Get(result))
|
||||||
|
|
||||||
|
// Setting None when inner exists should unset the value
|
||||||
|
defaultValue2 := 2
|
||||||
|
defaultFoo2 := "foo2"
|
||||||
|
outerWithInner := OuterOpt{inner: &InnerOpt{Value: &defaultValue2, Foo: &defaultFoo2}}
|
||||||
|
result2 := lens.Set(O.None[*int]())(outerWithInner)
|
||||||
|
assert.NotNil(t, result2.inner)
|
||||||
|
assert.Nil(t, result2.inner.Value)
|
||||||
|
assert.Equal(t, &defaultFoo2, result2.inner.Foo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComposeOptionEdgeCases(t *testing.T) {
|
||||||
|
defaultInner := &Inner{
|
||||||
|
Value: 0,
|
||||||
|
Foo: "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
value := L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
||||||
|
inner := FromNillable(L.MakeLens(Outer.GetInner, Outer.SetInner))
|
||||||
|
lens := F.Pipe1(
|
||||||
|
inner,
|
||||||
|
ComposeOption[Outer, int](defaultInner)(value),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Setting None should remove the inner entirely
|
||||||
|
outerWithInner := Outer{inner: &Inner{Value: 42, Foo: "bar"}}
|
||||||
|
result := lens.Set(O.None[int]())(outerWithInner)
|
||||||
|
assert.Nil(t, result.inner)
|
||||||
|
|
||||||
|
// Getting from empty should return None
|
||||||
|
emptyOuter := Outer{}
|
||||||
|
assert.Equal(t, O.None[int](), lens.Get(emptyOuter))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromPredicateEdgeCases(t *testing.T) {
|
||||||
|
type Score struct {
|
||||||
|
points int
|
||||||
|
}
|
||||||
|
|
||||||
|
pointsLens := L.MakeLens(
|
||||||
|
func(s Score) int { return s.points },
|
||||||
|
func(s Score, p int) Score { s.points = p; return s },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Only positive scores are valid
|
||||||
|
validLens := FromPredicate[Score](func(p int) bool { return p > 0 }, 0)(pointsLens)
|
||||||
|
|
||||||
|
// Test with valid score
|
||||||
|
validScore := Score{points: 100}
|
||||||
|
assert.Equal(t, O.Some(100), validLens.Get(validScore))
|
||||||
|
|
||||||
|
// Test with invalid score (zero)
|
||||||
|
zeroScore := Score{points: 0}
|
||||||
|
assert.Equal(t, O.None[int](), validLens.Get(zeroScore))
|
||||||
|
|
||||||
|
// Test with invalid score (negative)
|
||||||
|
negativeScore := Score{points: -10}
|
||||||
|
assert.Equal(t, O.None[int](), validLens.Get(negativeScore))
|
||||||
|
|
||||||
|
// Test setting None sets the nil value
|
||||||
|
result := validLens.Set(O.None[int]())(validScore)
|
||||||
|
assert.Equal(t, 0, result.points)
|
||||||
|
|
||||||
|
// Test setting Some sets the value
|
||||||
|
result2 := validLens.Set(O.Some(50))(zeroScore)
|
||||||
|
assert.Equal(t, 50, result2.points)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromNullablePropEdgeCases(t *testing.T) {
|
||||||
|
type Container struct {
|
||||||
|
item *string
|
||||||
|
}
|
||||||
|
|
||||||
|
itemLens := L.MakeLens(
|
||||||
|
func(c Container) *string { return c.item },
|
||||||
|
func(c Container, i *string) Container { c.item = i; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultItem := "default"
|
||||||
|
safeLens := FromNullableProp[Container](O.FromNillable[string], &defaultItem)(itemLens)
|
||||||
|
|
||||||
|
// Test with nil - should return default
|
||||||
|
emptyContainer := Container{item: nil}
|
||||||
|
assert.Equal(t, &defaultItem, safeLens.Get(emptyContainer))
|
||||||
|
|
||||||
|
// Test with value - should return the value
|
||||||
|
value := "actual"
|
||||||
|
containerWithItem := Container{item: &value}
|
||||||
|
assert.Equal(t, &value, safeLens.Get(containerWithItem))
|
||||||
|
|
||||||
|
// Test setter
|
||||||
|
newValue := "new"
|
||||||
|
updated := safeLens.Set(&newValue)(emptyContainer)
|
||||||
|
assert.Equal(t, &newValue, updated.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lens Law Tests for LensO types
|
||||||
|
|
||||||
|
func TestFromNillableLensLaws(t *testing.T) {
|
||||||
|
type Config struct {
|
||||||
|
timeout *int
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutLens := L.MakeLens(
|
||||||
|
func(c Config) *int { return c.timeout },
|
||||||
|
func(c Config, t *int) Config { c.timeout = t; return c },
|
||||||
|
)
|
||||||
|
|
||||||
|
optLens := FromNillable(timeoutLens)
|
||||||
|
|
||||||
|
// Equality predicates
|
||||||
|
eqInt := EQT.Eq[*int]()
|
||||||
|
eqOptInt := O.Eq(eqInt)
|
||||||
|
eqConfig := func(a, b Config) bool {
|
||||||
|
if a.timeout == nil && b.timeout == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if a.timeout == nil || b.timeout == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return *a.timeout == *b.timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test structures
|
||||||
|
timeout30 := 30
|
||||||
|
timeout60 := 60
|
||||||
|
configNil := Config{timeout: nil}
|
||||||
|
config30 := Config{timeout: &timeout30}
|
||||||
|
|
||||||
|
// Law 1: get(set(a)(s)) = a
|
||||||
|
t.Run("GetSet", func(t *testing.T) {
|
||||||
|
// Setting Some and getting back
|
||||||
|
result := optLens.Get(optLens.Set(O.Some(&timeout60))(config30))
|
||||||
|
assert.True(t, eqOptInt.Equals(result, O.Some(&timeout60)))
|
||||||
|
|
||||||
|
// Setting None and getting back
|
||||||
|
result2 := optLens.Get(optLens.Set(O.None[*int]())(config30))
|
||||||
|
assert.True(t, eqOptInt.Equals(result2, O.None[*int]()))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 2: set(get(s))(s) = s
|
||||||
|
t.Run("SetGet", func(t *testing.T) {
|
||||||
|
// With Some value
|
||||||
|
result := optLens.Set(optLens.Get(config30))(config30)
|
||||||
|
assert.True(t, eqConfig(result, config30))
|
||||||
|
|
||||||
|
// With None value
|
||||||
|
result2 := optLens.Set(optLens.Get(configNil))(configNil)
|
||||||
|
assert.True(t, eqConfig(result2, configNil))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 3: set(a)(set(a)(s)) = set(a)(s)
|
||||||
|
t.Run("SetSet", func(t *testing.T) {
|
||||||
|
// Setting Some twice
|
||||||
|
once := optLens.Set(O.Some(&timeout60))(config30)
|
||||||
|
twice := optLens.Set(O.Some(&timeout60))(once)
|
||||||
|
assert.True(t, eqConfig(once, twice))
|
||||||
|
|
||||||
|
// Setting None twice
|
||||||
|
once2 := optLens.Set(O.None[*int]())(config30)
|
||||||
|
twice2 := optLens.Set(O.None[*int]())(once2)
|
||||||
|
assert.True(t, eqConfig(once2, twice2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromNillableRefLensLaws(t *testing.T) {
|
||||||
|
type Settings struct {
|
||||||
|
maxRetries *int
|
||||||
|
}
|
||||||
|
|
||||||
|
retriesLens := L.MakeLensRef(
|
||||||
|
func(s *Settings) *int { return s.maxRetries },
|
||||||
|
func(s *Settings, r *int) *Settings { s.maxRetries = r; return s },
|
||||||
|
)
|
||||||
|
|
||||||
|
optLens := FromNillableRef(retriesLens)
|
||||||
|
|
||||||
|
// Equality predicates
|
||||||
|
eqInt := EQT.Eq[*int]()
|
||||||
|
eqOptInt := O.Eq(eqInt)
|
||||||
|
eqSettings := func(a, b *Settings) bool {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.maxRetries == nil && b.maxRetries == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if a.maxRetries == nil || b.maxRetries == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return *a.maxRetries == *b.maxRetries
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test structures
|
||||||
|
retries3 := 3
|
||||||
|
retries5 := 5
|
||||||
|
settingsNil := &Settings{maxRetries: nil}
|
||||||
|
settings3 := &Settings{maxRetries: &retries3}
|
||||||
|
|
||||||
|
// Law 1: get(set(a)(s)) = a
|
||||||
|
t.Run("GetSet", func(t *testing.T) {
|
||||||
|
result := optLens.Get(optLens.Set(O.Some(&retries5))(settings3))
|
||||||
|
assert.True(t, eqOptInt.Equals(result, O.Some(&retries5)))
|
||||||
|
|
||||||
|
result2 := optLens.Get(optLens.Set(O.None[*int]())(settings3))
|
||||||
|
assert.True(t, eqOptInt.Equals(result2, O.None[*int]()))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 2: set(get(s))(s) = s
|
||||||
|
t.Run("SetGet", func(t *testing.T) {
|
||||||
|
result := optLens.Set(optLens.Get(settings3))(settings3)
|
||||||
|
assert.True(t, eqSettings(result, settings3))
|
||||||
|
|
||||||
|
result2 := optLens.Set(optLens.Get(settingsNil))(settingsNil)
|
||||||
|
assert.True(t, eqSettings(result2, settingsNil))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 3: set(a)(set(a)(s)) = set(a)(s)
|
||||||
|
t.Run("SetSet", func(t *testing.T) {
|
||||||
|
once := optLens.Set(O.Some(&retries5))(settings3)
|
||||||
|
twice := optLens.Set(O.Some(&retries5))(once)
|
||||||
|
assert.True(t, eqSettings(once, twice))
|
||||||
|
|
||||||
|
once2 := optLens.Set(O.None[*int]())(settings3)
|
||||||
|
twice2 := optLens.Set(O.None[*int]())(once2)
|
||||||
|
assert.True(t, eqSettings(once2, twice2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComposeOptionLensLaws(t *testing.T) {
|
||||||
|
defaultInner := &Inner{Value: 0, Foo: "default"}
|
||||||
|
|
||||||
|
value := L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
||||||
|
inner := FromNillable(L.MakeLens(Outer.GetInner, Outer.SetInner))
|
||||||
|
lens := F.Pipe1(inner, ComposeOption[Outer, int](defaultInner)(value))
|
||||||
|
|
||||||
|
// Equality predicates
|
||||||
|
eqInt := EQT.Eq[int]()
|
||||||
|
eqOptInt := O.Eq(eqInt)
|
||||||
|
eqOuter := func(a, b Outer) bool {
|
||||||
|
if a.inner == nil && b.inner == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if a.inner == nil || b.inner == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return a.inner.Value == b.inner.Value && a.inner.Foo == b.inner.Foo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test structures
|
||||||
|
outerNil := Outer{inner: nil}
|
||||||
|
outer42 := Outer{inner: &Inner{Value: 42, Foo: "test"}}
|
||||||
|
|
||||||
|
// Law 1: get(set(a)(s)) = a
|
||||||
|
t.Run("GetSet", func(t *testing.T) {
|
||||||
|
result := lens.Get(lens.Set(O.Some(100))(outer42))
|
||||||
|
assert.True(t, eqOptInt.Equals(result, O.Some(100)))
|
||||||
|
|
||||||
|
result2 := lens.Get(lens.Set(O.None[int]())(outer42))
|
||||||
|
assert.True(t, eqOptInt.Equals(result2, O.None[int]()))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 2: set(get(s))(s) = s
|
||||||
|
t.Run("SetGet", func(t *testing.T) {
|
||||||
|
result := lens.Set(lens.Get(outer42))(outer42)
|
||||||
|
assert.True(t, eqOuter(result, outer42))
|
||||||
|
|
||||||
|
result2 := lens.Set(lens.Get(outerNil))(outerNil)
|
||||||
|
assert.True(t, eqOuter(result2, outerNil))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 3: set(a)(set(a)(s)) = set(a)(s)
|
||||||
|
t.Run("SetSet", func(t *testing.T) {
|
||||||
|
once := lens.Set(O.Some(100))(outer42)
|
||||||
|
twice := lens.Set(O.Some(100))(once)
|
||||||
|
assert.True(t, eqOuter(once, twice))
|
||||||
|
|
||||||
|
once2 := lens.Set(O.None[int]())(outer42)
|
||||||
|
twice2 := lens.Set(O.None[int]())(once2)
|
||||||
|
assert.True(t, eqOuter(once2, twice2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComposeOptionsLensLaws(t *testing.T) {
|
||||||
|
defaultValue := 1
|
||||||
|
defaultFoo := "default"
|
||||||
|
defaultInner := &InnerOpt{Value: &defaultValue, Foo: &defaultFoo}
|
||||||
|
|
||||||
|
value := FromNillable(L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue))
|
||||||
|
inner := FromNillable(L.MakeLens(OuterOpt.GetInnerOpt, OuterOpt.SetInnerOpt))
|
||||||
|
lens := F.Pipe1(inner, Compose[OuterOpt, *int](defaultInner)(value))
|
||||||
|
|
||||||
|
// Equality predicates
|
||||||
|
eqIntPtr := EQT.Eq[*int]()
|
||||||
|
eqOptIntPtr := O.Eq(eqIntPtr)
|
||||||
|
eqOuterOpt := func(a, b OuterOpt) bool {
|
||||||
|
if a.inner == nil && b.inner == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if a.inner == nil || b.inner == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
aVal := a.inner.Value
|
||||||
|
bVal := b.inner.Value
|
||||||
|
if aVal == nil && bVal == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if aVal == nil || bVal == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return *aVal == *bVal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test structures
|
||||||
|
val42 := 42
|
||||||
|
val100 := 100
|
||||||
|
outerNil := OuterOpt{inner: nil}
|
||||||
|
outer42 := OuterOpt{inner: &InnerOpt{Value: &val42, Foo: &defaultFoo}}
|
||||||
|
|
||||||
|
// Law 1: get(set(a)(s)) = a
|
||||||
|
t.Run("GetSet", func(t *testing.T) {
|
||||||
|
result := lens.Get(lens.Set(O.Some(&val100))(outer42))
|
||||||
|
assert.True(t, eqOptIntPtr.Equals(result, O.Some(&val100)))
|
||||||
|
|
||||||
|
result2 := lens.Get(lens.Set(O.None[*int]())(outer42))
|
||||||
|
assert.True(t, eqOptIntPtr.Equals(result2, O.None[*int]()))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 2: set(get(s))(s) = s
|
||||||
|
t.Run("SetGet", func(t *testing.T) {
|
||||||
|
result := lens.Set(lens.Get(outer42))(outer42)
|
||||||
|
assert.True(t, eqOuterOpt(result, outer42))
|
||||||
|
|
||||||
|
result2 := lens.Set(lens.Get(outerNil))(outerNil)
|
||||||
|
assert.True(t, eqOuterOpt(result2, outerNil))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 3: set(a)(set(a)(s)) = set(a)(s)
|
||||||
|
t.Run("SetSet", func(t *testing.T) {
|
||||||
|
once := lens.Set(O.Some(&val100))(outer42)
|
||||||
|
twice := lens.Set(O.Some(&val100))(once)
|
||||||
|
assert.True(t, eqOuterOpt(once, twice))
|
||||||
|
|
||||||
|
once2 := lens.Set(O.None[*int]())(outer42)
|
||||||
|
twice2 := lens.Set(O.None[*int]())(once2)
|
||||||
|
assert.True(t, eqOuterOpt(once2, twice2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromPredicateLensLaws(t *testing.T) {
|
||||||
|
type Score struct {
|
||||||
|
points int
|
||||||
|
}
|
||||||
|
|
||||||
|
pointsLens := L.MakeLens(
|
||||||
|
func(s Score) int { return s.points },
|
||||||
|
func(s Score, p int) Score { s.points = p; return s },
|
||||||
|
)
|
||||||
|
|
||||||
|
// Only positive scores are valid
|
||||||
|
validLens := FromPredicate[Score](func(p int) bool { return p > 0 }, 0)(pointsLens)
|
||||||
|
|
||||||
|
// Equality predicates
|
||||||
|
eqInt := EQT.Eq[int]()
|
||||||
|
eqOptInt := O.Eq(eqInt)
|
||||||
|
eqScore := func(a, b Score) bool { return a.points == b.points }
|
||||||
|
|
||||||
|
// Test structures
|
||||||
|
scoreZero := Score{points: 0}
|
||||||
|
score100 := Score{points: 100}
|
||||||
|
|
||||||
|
// Law 1: get(set(a)(s)) = a
|
||||||
|
t.Run("GetSet", func(t *testing.T) {
|
||||||
|
result := validLens.Get(validLens.Set(O.Some(50))(score100))
|
||||||
|
assert.True(t, eqOptInt.Equals(result, O.Some(50)))
|
||||||
|
|
||||||
|
result2 := validLens.Get(validLens.Set(O.None[int]())(score100))
|
||||||
|
assert.True(t, eqOptInt.Equals(result2, O.None[int]()))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 2: set(get(s))(s) = s
|
||||||
|
t.Run("SetGet", func(t *testing.T) {
|
||||||
|
result := validLens.Set(validLens.Get(score100))(score100)
|
||||||
|
assert.True(t, eqScore(result, score100))
|
||||||
|
|
||||||
|
result2 := validLens.Set(validLens.Get(scoreZero))(scoreZero)
|
||||||
|
assert.True(t, eqScore(result2, scoreZero))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Law 3: set(a)(set(a)(s)) = set(a)(s)
|
||||||
|
t.Run("SetSet", func(t *testing.T) {
|
||||||
|
once := validLens.Set(O.Some(75))(score100)
|
||||||
|
twice := validLens.Set(O.Some(75))(once)
|
||||||
|
assert.True(t, eqScore(once, twice))
|
||||||
|
|
||||||
|
once2 := validLens.Set(O.None[int]())(score100)
|
||||||
|
twice2 := validLens.Set(O.None[int]())(once2)
|
||||||
|
assert.True(t, eqScore(once2, twice2))
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -22,6 +22,44 @@ import (
|
|||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AsTraversal converts a Lens[S, A] to a Traversal[S, A] for optional values.
|
||||||
|
//
|
||||||
|
// A traversal is a generalization of a lens that can focus on zero or more values.
|
||||||
|
// This function converts a lens (which focuses on exactly one value) into a traversal,
|
||||||
|
// allowing it to be used with traversal operations like mapping over multiple values.
|
||||||
|
//
|
||||||
|
// This is particularly useful when you want to:
|
||||||
|
// - Use lens operations in a traversal context
|
||||||
|
// - Compose lenses with traversals
|
||||||
|
// - Apply operations that work on collections of optional values
|
||||||
|
//
|
||||||
|
// The conversion uses the Option monad's map operation to handle the optional nature
|
||||||
|
// of the values being traversed.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: The structure type containing the field
|
||||||
|
// - A: The type of the field being focused on
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - A function that takes a Lens[S, A] and returns a Traversal[S, A]
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Config struct {
|
||||||
|
// Timeout Option[int]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// timeoutLens := lens.MakeLens(
|
||||||
|
// func(c Config) Option[int] { return c.Timeout },
|
||||||
|
// func(c Config, t Option[int]) Config { c.Timeout = t; return c },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Convert to traversal for use with traversal operations
|
||||||
|
// timeoutTraversal := lens.AsTraversal[Config, int]()(timeoutLens)
|
||||||
|
//
|
||||||
|
// // Now can use traversal operations
|
||||||
|
// configs := []Config{{Timeout: O.Some(30)}, {Timeout: O.None[int]()}}
|
||||||
|
// // Apply operations across all configs using the traversal
|
||||||
func AsTraversal[S, A any]() func(L.Lens[S, A]) T.Traversal[S, A] {
|
func AsTraversal[S, A any]() func(L.Lens[S, A]) T.Traversal[S, A] {
|
||||||
return LG.AsTraversal[T.Traversal[S, A]](O.MonadMap[A, S])
|
return LG.AsTraversal[T.Traversal[S, A]](O.MonadMap[A, S])
|
||||||
}
|
}
|
||||||
|
|||||||
267
v2/optics/lens/option/testing/laws_test.go
Normal file
267
v2/optics/lens/option/testing/laws_test.go
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
EQT "github.com/IBM/fp-go/v2/eq/testing"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
I "github.com/IBM/fp-go/v2/identity"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
LI "github.com/IBM/fp-go/v2/optics/lens/iso"
|
||||||
|
LO "github.com/IBM/fp-go/v2/optics/lens/option"
|
||||||
|
LT "github.com/IBM/fp-go/v2/optics/lens/testing"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Street struct {
|
||||||
|
num int
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
Address struct {
|
||||||
|
city string
|
||||||
|
street *Street
|
||||||
|
}
|
||||||
|
|
||||||
|
Inner struct {
|
||||||
|
Value int
|
||||||
|
Foo string
|
||||||
|
}
|
||||||
|
|
||||||
|
InnerOpt struct {
|
||||||
|
Value *int
|
||||||
|
Foo *string
|
||||||
|
}
|
||||||
|
|
||||||
|
Outer struct {
|
||||||
|
inner *Inner
|
||||||
|
}
|
||||||
|
|
||||||
|
OuterOpt struct {
|
||||||
|
inner *InnerOpt
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (outer *OuterOpt) GetInner() *InnerOpt {
|
||||||
|
return outer.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (outer *OuterOpt) SetInner(inner *InnerOpt) *OuterOpt {
|
||||||
|
outer.inner = inner
|
||||||
|
return outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *InnerOpt) GetValue() *int {
|
||||||
|
return inner.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *InnerOpt) SetValue(value *int) *InnerOpt {
|
||||||
|
inner.Value = value
|
||||||
|
return inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (outer *Outer) GetInner() *Inner {
|
||||||
|
return outer.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (outer *Outer) SetInner(inner *Inner) *Outer {
|
||||||
|
outer.inner = inner
|
||||||
|
return outer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *Inner) GetValue() int {
|
||||||
|
return inner.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inner *Inner) SetValue(value int) *Inner {
|
||||||
|
inner.Value = value
|
||||||
|
return inner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (street *Street) GetName() string {
|
||||||
|
return street.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (street *Street) SetName(name string) *Street {
|
||||||
|
street.name = name
|
||||||
|
return street
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *Address) GetStreet() *Street {
|
||||||
|
return addr.street
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *Address) SetStreet(s *Street) *Address {
|
||||||
|
addr.street = s
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
streetLens = L.MakeLensRef((*Street).GetName, (*Street).SetName)
|
||||||
|
addrLens = L.MakeLensRef((*Address).GetStreet, (*Address).SetStreet)
|
||||||
|
outerLens = LO.FromNillableRef(L.MakeLensRef((*Outer).GetInner, (*Outer).SetInner))
|
||||||
|
valueLens = L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
||||||
|
|
||||||
|
outerOptLens = LO.FromNillableRef(L.MakeLensRef((*OuterOpt).GetInner, (*OuterOpt).SetInner))
|
||||||
|
valueOptLens = L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue)
|
||||||
|
|
||||||
|
sampleStreet = Street{num: 220, name: "Schönaicherstr"}
|
||||||
|
sampleAddress = Address{city: "Böblingen", street: &sampleStreet}
|
||||||
|
sampleStreet2 = Street{num: 220, name: "Neue Str"}
|
||||||
|
|
||||||
|
defaultInner = Inner{
|
||||||
|
Value: -1,
|
||||||
|
Foo: "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyOuter = Outer{}
|
||||||
|
|
||||||
|
defaultInnerOpt = InnerOpt{
|
||||||
|
Value: &defaultInner.Value,
|
||||||
|
Foo: &defaultInner.Foo,
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyOuterOpt = OuterOpt{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStreetLensLaws(t *testing.T) {
|
||||||
|
// some comparison
|
||||||
|
eqs := EQT.Eq[*Street]()
|
||||||
|
eqa := EQT.Eq[string]()
|
||||||
|
|
||||||
|
laws := LT.AssertLaws(
|
||||||
|
t,
|
||||||
|
eqa,
|
||||||
|
eqs,
|
||||||
|
)(streetLens)
|
||||||
|
|
||||||
|
cpy := sampleStreet
|
||||||
|
assert.True(t, laws(&sampleStreet, "Neue Str."))
|
||||||
|
assert.Equal(t, cpy, sampleStreet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddrLensLaws(t *testing.T) {
|
||||||
|
// some comparison
|
||||||
|
eqs := EQT.Eq[*Address]()
|
||||||
|
eqa := EQT.Eq[*Street]()
|
||||||
|
|
||||||
|
laws := LT.AssertLaws(
|
||||||
|
t,
|
||||||
|
eqa,
|
||||||
|
eqs,
|
||||||
|
)(addrLens)
|
||||||
|
|
||||||
|
cpyAddr := sampleAddress
|
||||||
|
cpyStreet := sampleStreet2
|
||||||
|
assert.True(t, laws(&sampleAddress, &sampleStreet2))
|
||||||
|
assert.Equal(t, cpyAddr, sampleAddress)
|
||||||
|
assert.Equal(t, cpyStreet, sampleStreet2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompose(t *testing.T) {
|
||||||
|
// some comparison
|
||||||
|
eqs := EQT.Eq[*Address]()
|
||||||
|
eqa := EQT.Eq[string]()
|
||||||
|
|
||||||
|
streetName := L.Compose[*Address](streetLens)(addrLens)
|
||||||
|
|
||||||
|
laws := LT.AssertLaws(
|
||||||
|
t,
|
||||||
|
eqa,
|
||||||
|
eqs,
|
||||||
|
)(streetName)
|
||||||
|
|
||||||
|
cpyAddr := sampleAddress
|
||||||
|
cpyStreet := sampleStreet
|
||||||
|
assert.True(t, laws(&sampleAddress, "Neue Str."))
|
||||||
|
assert.Equal(t, cpyAddr, sampleAddress)
|
||||||
|
assert.Equal(t, cpyStreet, sampleStreet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOuterLensLaws(t *testing.T) {
|
||||||
|
// some equal predicates
|
||||||
|
eqValue := EQT.Eq[int]()
|
||||||
|
eqOptValue := O.Eq(eqValue)
|
||||||
|
// lens to access a value from outer
|
||||||
|
valueFromOuter := LO.ComposeOption[*Outer, int](&defaultInner)(valueLens)(outerLens)
|
||||||
|
// try to access the value, this should get an option
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]()))
|
||||||
|
// update the object
|
||||||
|
withValue := valueFromOuter.Set(O.Some(1))(&emptyOuter)
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]()))
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1)))
|
||||||
|
// updating with none should remove the inner
|
||||||
|
nextValue := valueFromOuter.Set(O.None[int]())(withValue)
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]()))
|
||||||
|
// check if this meets the laws
|
||||||
|
|
||||||
|
eqOuter := EQT.Eq[*Outer]()
|
||||||
|
|
||||||
|
laws := LT.AssertLaws(
|
||||||
|
t,
|
||||||
|
eqOptValue,
|
||||||
|
eqOuter,
|
||||||
|
)(valueFromOuter)
|
||||||
|
|
||||||
|
assert.True(t, laws(&emptyOuter, O.Some(2)))
|
||||||
|
assert.True(t, laws(&emptyOuter, O.None[int]()))
|
||||||
|
|
||||||
|
assert.True(t, laws(withValue, O.Some(2)))
|
||||||
|
assert.True(t, laws(withValue, O.None[int]()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOuterOptLensLaws(t *testing.T) {
|
||||||
|
// some equal predicates
|
||||||
|
eqValue := EQT.Eq[int]()
|
||||||
|
eqOptValue := O.Eq(eqValue)
|
||||||
|
intIso := LI.FromNillable[int]()
|
||||||
|
// lens to access a value from outer
|
||||||
|
valueFromOuter := F.Pipe3(
|
||||||
|
valueOptLens,
|
||||||
|
LI.Compose[*InnerOpt](intIso),
|
||||||
|
LO.Compose[*OuterOpt, int](&defaultInnerOpt),
|
||||||
|
I.Ap[L.Lens[*OuterOpt, O.Option[int]]](outerOptLens),
|
||||||
|
)
|
||||||
|
|
||||||
|
// try to access the value, this should get an option
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]()))
|
||||||
|
// update the object
|
||||||
|
withValue := valueFromOuter.Set(O.Some(1))(&emptyOuterOpt)
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]()))
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1)))
|
||||||
|
// updating with none should remove the inner
|
||||||
|
nextValue := valueFromOuter.Set(O.None[int]())(withValue)
|
||||||
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]()))
|
||||||
|
// check if this meets the laws
|
||||||
|
|
||||||
|
eqOuter := EQT.Eq[*OuterOpt]()
|
||||||
|
|
||||||
|
laws := LT.AssertLaws(
|
||||||
|
t,
|
||||||
|
eqOptValue,
|
||||||
|
eqOuter,
|
||||||
|
)(valueFromOuter)
|
||||||
|
|
||||||
|
assert.True(t, laws(&emptyOuterOpt, O.Some(2)))
|
||||||
|
assert.True(t, laws(&emptyOuterOpt, O.None[int]()))
|
||||||
|
|
||||||
|
assert.True(t, laws(withValue, O.Some(2)))
|
||||||
|
assert.True(t, laws(withValue, O.None[int]()))
|
||||||
|
}
|
||||||
94
v2/optics/lens/option/types.go
Normal file
94
v2/optics/lens/option/types.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (c) 2023 - 2025 IBM Corp.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/endomorphism"
|
||||||
|
"github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Endomorphism is a function from a type to itself (A → A).
|
||||||
|
// It represents transformations that preserve the type.
|
||||||
|
//
|
||||||
|
// This is commonly used in lens setters to transform a structure
|
||||||
|
// by applying a function that takes and returns the same type.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// increment := func(x int) int { return x + 1 }
|
||||||
|
// // increment is an Endomorphism[int]
|
||||||
|
Endomorphism[A any] = endomorphism.Endomorphism[A]
|
||||||
|
|
||||||
|
// Lens represents a functional reference to a field within a structure.
|
||||||
|
//
|
||||||
|
// A Lens[S, A] provides a way to get and set a value of type A within
|
||||||
|
// a structure of type S in an immutable way. It consists of:
|
||||||
|
// - Get: S → A (retrieve the value)
|
||||||
|
// - Set: A → S → S (update the value, returning a new structure)
|
||||||
|
//
|
||||||
|
// Lenses satisfy three laws:
|
||||||
|
// 1. Get-Put: lens.Set(lens.Get(s))(s) == s
|
||||||
|
// 2. Put-Get: lens.Get(lens.Set(a)(s)) == a
|
||||||
|
// 3. Put-Put: lens.Set(b)(lens.Set(a)(s)) == lens.Set(b)(s)
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: The structure type containing the field
|
||||||
|
// - A: The type of the field being focused on
|
||||||
|
Lens[S, A any] = lens.Lens[S, A]
|
||||||
|
|
||||||
|
// Option represents a value that may or may not be present.
|
||||||
|
//
|
||||||
|
// It is either Some[T] containing a value of type T, or None[T]
|
||||||
|
// representing the absence of a value. This is a type-safe alternative
|
||||||
|
// to using nil pointers.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - T: The type of the value that may be present
|
||||||
|
Option[T any] = option.Option[T]
|
||||||
|
|
||||||
|
// LensO is a lens that focuses on an optional value.
|
||||||
|
//
|
||||||
|
// A LensO[S, A] is equivalent to Lens[S, Option[A]], representing
|
||||||
|
// a lens that focuses on a value of type A that may or may not be
|
||||||
|
// present within a structure S.
|
||||||
|
//
|
||||||
|
// This is particularly useful for:
|
||||||
|
// - Nullable pointer fields
|
||||||
|
// - Optional configuration values
|
||||||
|
// - Fields that may be conditionally present
|
||||||
|
//
|
||||||
|
// The getter returns Option[A] (Some if present, None if absent).
|
||||||
|
// The setter takes Option[A] (Some to set, None to remove).
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: The structure type containing the optional field
|
||||||
|
// - A: The type of the optional value being focused on
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// type Config struct {
|
||||||
|
// Timeout *int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// timeoutLens := lens.MakeLensRef(
|
||||||
|
// func(c *Config) *int { return c.Timeout },
|
||||||
|
// func(c *Config, t *int) *Config { c.Timeout = t; return c },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// optLens := lens.FromNillableRef(timeoutLens)
|
||||||
|
// // optLens is a LensO[*Config, *int]
|
||||||
|
LensO[S, A any] = Lens[S, Option[A]]
|
||||||
|
)
|
||||||
@@ -19,11 +19,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
EQT "github.com/IBM/fp-go/v2/eq/testing"
|
EQT "github.com/IBM/fp-go/v2/eq/testing"
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
|
||||||
I "github.com/IBM/fp-go/v2/identity"
|
|
||||||
L "github.com/IBM/fp-go/v2/optics/lens"
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
LI "github.com/IBM/fp-go/v2/optics/lens/iso"
|
|
||||||
O "github.com/IBM/fp-go/v2/option"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -114,10 +110,8 @@ func (addr *Address) SetStreet(s *Street) *Address {
|
|||||||
var (
|
var (
|
||||||
streetLens = L.MakeLensRef((*Street).GetName, (*Street).SetName)
|
streetLens = L.MakeLensRef((*Street).GetName, (*Street).SetName)
|
||||||
addrLens = L.MakeLensRef((*Address).GetStreet, (*Address).SetStreet)
|
addrLens = L.MakeLensRef((*Address).GetStreet, (*Address).SetStreet)
|
||||||
outerLens = L.FromNillableRef(L.MakeLensRef((*Outer).GetInner, (*Outer).SetInner))
|
|
||||||
valueLens = L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
valueLens = L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
||||||
|
|
||||||
outerOptLens = L.FromNillableRef(L.MakeLensRef((*OuterOpt).GetInner, (*OuterOpt).SetInner))
|
|
||||||
valueOptLens = L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue)
|
valueOptLens = L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue)
|
||||||
|
|
||||||
sampleStreet = Street{num: 220, name: "Schönaicherstr"}
|
sampleStreet = Street{num: 220, name: "Schönaicherstr"}
|
||||||
@@ -192,74 +186,3 @@ func TestCompose(t *testing.T) {
|
|||||||
assert.Equal(t, cpyAddr, sampleAddress)
|
assert.Equal(t, cpyAddr, sampleAddress)
|
||||||
assert.Equal(t, cpyStreet, sampleStreet)
|
assert.Equal(t, cpyStreet, sampleStreet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOuterLensLaws(t *testing.T) {
|
|
||||||
// some equal predicates
|
|
||||||
eqValue := EQT.Eq[int]()
|
|
||||||
eqOptValue := O.Eq(eqValue)
|
|
||||||
// lens to access a value from outer
|
|
||||||
valueFromOuter := L.ComposeOption[*Outer, int](&defaultInner)(valueLens)(outerLens)
|
|
||||||
// try to access the value, this should get an option
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]()))
|
|
||||||
// update the object
|
|
||||||
withValue := valueFromOuter.Set(O.Some(1))(&emptyOuter)
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]()))
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1)))
|
|
||||||
// updating with none should remove the inner
|
|
||||||
nextValue := valueFromOuter.Set(O.None[int]())(withValue)
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]()))
|
|
||||||
// check if this meets the laws
|
|
||||||
|
|
||||||
eqOuter := EQT.Eq[*Outer]()
|
|
||||||
|
|
||||||
laws := AssertLaws(
|
|
||||||
t,
|
|
||||||
eqOptValue,
|
|
||||||
eqOuter,
|
|
||||||
)(valueFromOuter)
|
|
||||||
|
|
||||||
assert.True(t, laws(&emptyOuter, O.Some(2)))
|
|
||||||
assert.True(t, laws(&emptyOuter, O.None[int]()))
|
|
||||||
|
|
||||||
assert.True(t, laws(withValue, O.Some(2)))
|
|
||||||
assert.True(t, laws(withValue, O.None[int]()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOuterOptLensLaws(t *testing.T) {
|
|
||||||
// some equal predicates
|
|
||||||
eqValue := EQT.Eq[int]()
|
|
||||||
eqOptValue := O.Eq(eqValue)
|
|
||||||
intIso := LI.FromNillable[int]()
|
|
||||||
// lens to access a value from outer
|
|
||||||
valueFromOuter := F.Pipe3(
|
|
||||||
valueOptLens,
|
|
||||||
LI.Compose[*InnerOpt](intIso),
|
|
||||||
L.ComposeOptions[*OuterOpt, int](&defaultInnerOpt),
|
|
||||||
I.Ap[L.Lens[*OuterOpt, O.Option[int]]](outerOptLens),
|
|
||||||
)
|
|
||||||
|
|
||||||
// try to access the value, this should get an option
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]()))
|
|
||||||
// update the object
|
|
||||||
withValue := valueFromOuter.Set(O.Some(1))(&emptyOuterOpt)
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]()))
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1)))
|
|
||||||
// updating with none should remove the inner
|
|
||||||
nextValue := valueFromOuter.Set(O.None[int]())(withValue)
|
|
||||||
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]()))
|
|
||||||
// check if this meets the laws
|
|
||||||
|
|
||||||
eqOuter := EQT.Eq[*OuterOpt]()
|
|
||||||
|
|
||||||
laws := AssertLaws(
|
|
||||||
t,
|
|
||||||
eqOptValue,
|
|
||||||
eqOuter,
|
|
||||||
)(valueFromOuter)
|
|
||||||
|
|
||||||
assert.True(t, laws(&emptyOuterOpt, O.Some(2)))
|
|
||||||
assert.True(t, laws(&emptyOuterOpt, O.None[int]()))
|
|
||||||
|
|
||||||
assert.True(t, laws(withValue, O.Some(2)))
|
|
||||||
assert.True(t, laws(withValue, O.None[int]()))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -21,11 +21,63 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// Endomorphism is a function from a type to itself (A → A).
|
||||||
|
// It represents transformations that preserve the type.
|
||||||
Endomorphism[A any] = endomorphism.Endomorphism[A]
|
Endomorphism[A any] = endomorphism.Endomorphism[A]
|
||||||
|
|
||||||
// Lens is a reference to a subpart of a data type
|
// Lens is a functional reference to a subpart of a data structure.
|
||||||
|
//
|
||||||
|
// A Lens[S, A] provides a composable way to focus on a field of type A within
|
||||||
|
// a structure of type S. It consists of two operations:
|
||||||
|
// - Get: Extracts the focused value from the structure (S → A)
|
||||||
|
// - Set: Updates the focused value in the structure, returning a new structure (A → S → S)
|
||||||
|
//
|
||||||
|
// Lenses maintain immutability by always returning new copies of the structure
|
||||||
|
// when setting values, never modifying the original.
|
||||||
|
//
|
||||||
|
// Type Parameters:
|
||||||
|
// - S: The source/structure type (the whole)
|
||||||
|
// - A: The focus/field type (the part)
|
||||||
|
//
|
||||||
|
// Lens Laws:
|
||||||
|
//
|
||||||
|
// A well-behaved lens must satisfy three laws:
|
||||||
|
//
|
||||||
|
// 1. GetSet (You get what you set):
|
||||||
|
// lens.Set(lens.Get(s))(s) == s
|
||||||
|
//
|
||||||
|
// 2. SetGet (You set what you get):
|
||||||
|
// lens.Get(lens.Set(a)(s)) == a
|
||||||
|
//
|
||||||
|
// 3. SetSet (Setting twice is the same as setting once):
|
||||||
|
// lens.Set(a2)(lens.Set(a1)(s)) == lens.Set(a2)(s)
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Age int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// nameLens := lens.MakeLens(
|
||||||
|
// func(p Person) string { return p.Name },
|
||||||
|
// func(p Person, name string) Person {
|
||||||
|
// p.Name = name
|
||||||
|
// return p
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// person := Person{Name: "Alice", Age: 30}
|
||||||
|
// name := nameLens.Get(person) // "Alice"
|
||||||
|
// updated := nameLens.Set("Bob")(person) // Person{Name: "Bob", Age: 30}
|
||||||
|
// // person is unchanged, updated is a new value
|
||||||
Lens[S, A any] struct {
|
Lens[S, A any] struct {
|
||||||
|
// Get extracts the focused value of type A from structure S.
|
||||||
Get func(s S) A
|
Get func(s S) A
|
||||||
|
|
||||||
|
// Set returns a function that updates the focused value in structure S.
|
||||||
|
// The returned function takes a structure S and returns a new structure S
|
||||||
|
// with the focused value updated to a. The original structure is never modified.
|
||||||
Set func(a A) Endomorphism[S]
|
Set func(a A) Endomorphism[S]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import (
|
|||||||
// }
|
// }
|
||||||
// result := TraverseArrayG[[]string, []int](parse)([]string{"1", "2", "3"}) // Some([1, 2, 3])
|
// result := TraverseArrayG[[]string, []int](parse)([]string{"1", "2", "3"}) // Some([1, 2, 3])
|
||||||
// result := TraverseArrayG[[]string, []int](parse)([]string{"1", "x", "3"}) // None
|
// result := TraverseArrayG[[]string, []int](parse)([]string{"1", "x", "3"}) // None
|
||||||
func TraverseArrayG[GA ~[]A, GB ~[]B, A, B any](f func(A) Option[B]) func(GA) Option[GB] {
|
func TraverseArrayG[GA ~[]A, GB ~[]B, A, B any](f Kleisli[A, B]) Kleisli[GA, GB] {
|
||||||
return RA.Traverse[GA](
|
return RA.Traverse[GA](
|
||||||
Of[GB],
|
Of[GB],
|
||||||
Map[GB, func(B) GB],
|
Map[GB, func(B) GB],
|
||||||
@@ -54,7 +54,7 @@ func TraverseArrayG[GA ~[]A, GB ~[]B, A, B any](f func(A) Option[B]) func(GA) Op
|
|||||||
// }
|
// }
|
||||||
// result := TraverseArray(validate)([]int{1, 2, 3}) // Some([2, 4, 6])
|
// result := TraverseArray(validate)([]int{1, 2, 3}) // Some([2, 4, 6])
|
||||||
// result := TraverseArray(validate)([]int{1, -1, 3}) // None
|
// result := TraverseArray(validate)([]int{1, -1, 3}) // None
|
||||||
func TraverseArray[A, B any](f func(A) Option[B]) func([]A) Option[[]B] {
|
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
|
||||||
return TraverseArrayG[[]A, []B](f)
|
return TraverseArrayG[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ func TraverseArray[A, B any](f func(A) Option[B]) func([]A) Option[[]B] {
|
|||||||
// return Some(fmt.Sprintf("%d:%s", i, s))
|
// return Some(fmt.Sprintf("%d:%s", i, s))
|
||||||
// }
|
// }
|
||||||
// result := TraverseArrayWithIndexG[[]string, []string](f)([]string{"a", "b"}) // Some(["0:a", "1:b"])
|
// result := TraverseArrayWithIndexG[[]string, []string](f)([]string{"a", "b"}) // Some(["0:a", "1:b"])
|
||||||
func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, A, B any](f func(int, A) Option[B]) func(GA) Option[GB] {
|
func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, A, B any](f func(int, A) Option[B]) Kleisli[GA, GB] {
|
||||||
return RA.TraverseWithIndex[GA](
|
return RA.TraverseWithIndex[GA](
|
||||||
Of[GB],
|
Of[GB],
|
||||||
Map[GB, func(B) GB],
|
Map[GB, func(B) GB],
|
||||||
@@ -88,7 +88,7 @@ func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, A, B any](f func(int, A) Option[B
|
|||||||
// return None[int]()
|
// return None[int]()
|
||||||
// }
|
// }
|
||||||
// result := TraverseArrayWithIndex(f)([]int{1, 2, 3}) // Some([1, 2, 3])
|
// result := TraverseArrayWithIndex(f)([]int{1, 2, 3}) // Some([1, 2, 3])
|
||||||
func TraverseArrayWithIndex[A, B any](f func(int, A) Option[B]) func([]A) Option[[]B] {
|
func TraverseArrayWithIndex[A, B any](f func(int, A) Option[B]) Kleisli[[]A, []B] {
|
||||||
return TraverseArrayWithIndexG[[]A, []B](f)
|
return TraverseArrayWithIndexG[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
A "github.com/IBM/fp-go/v2/internal/apply"
|
A "github.com/IBM/fp-go/v2/internal/apply"
|
||||||
C "github.com/IBM/fp-go/v2/internal/chain"
|
C "github.com/IBM/fp-go/v2/internal/chain"
|
||||||
F "github.com/IBM/fp-go/v2/internal/functor"
|
F "github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do creates an empty context of type S to be used with the Bind operation.
|
// Do creates an empty context of type S to be used with the Bind operation.
|
||||||
@@ -51,8 +52,8 @@ func Do[S any](
|
|||||||
// )
|
// )
|
||||||
func Bind[S1, S2, A any](
|
func Bind[S1, S2, A any](
|
||||||
setter func(A) func(S1) S2,
|
setter func(A) func(S1) S2,
|
||||||
f func(S1) Option[A],
|
f Kleisli[S1, A],
|
||||||
) func(Option[S1]) Option[S2] {
|
) Kleisli[Option[S1], S2] {
|
||||||
return C.Bind(
|
return C.Bind(
|
||||||
Chain[S1, S2],
|
Chain[S1, S2],
|
||||||
Map[A, S2],
|
Map[A, S2],
|
||||||
@@ -76,7 +77,7 @@ func Bind[S1, S2, A any](
|
|||||||
func Let[S1, S2, B any](
|
func Let[S1, S2, B any](
|
||||||
key func(B) func(S1) S2,
|
key func(B) func(S1) S2,
|
||||||
f func(S1) B,
|
f func(S1) B,
|
||||||
) func(Option[S1]) Option[S2] {
|
) Kleisli[Option[S1], S2] {
|
||||||
return F.Let(
|
return F.Let(
|
||||||
Map[S1, S2],
|
Map[S1, S2],
|
||||||
key,
|
key,
|
||||||
@@ -98,7 +99,7 @@ func Let[S1, S2, B any](
|
|||||||
func LetTo[S1, S2, B any](
|
func LetTo[S1, S2, B any](
|
||||||
key func(B) func(S1) S2,
|
key func(B) func(S1) S2,
|
||||||
b B,
|
b B,
|
||||||
) func(Option[S1]) Option[S2] {
|
) Kleisli[Option[S1], S2] {
|
||||||
return F.LetTo(
|
return F.LetTo(
|
||||||
Map[S1, S2],
|
Map[S1, S2],
|
||||||
key,
|
key,
|
||||||
@@ -118,7 +119,7 @@ func LetTo[S1, S2, B any](
|
|||||||
// )
|
// )
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func(Option[T]) Option[S1] {
|
) Kleisli[Option[T], S1] {
|
||||||
return C.BindTo(
|
return C.BindTo(
|
||||||
Map[T, S1],
|
Map[T, S1],
|
||||||
setter,
|
setter,
|
||||||
@@ -140,7 +141,7 @@ func BindTo[S1, T any](
|
|||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa Option[T],
|
fa Option[T],
|
||||||
) func(Option[S1]) Option[S2] {
|
) Kleisli[Option[S1], S2] {
|
||||||
return A.ApS(
|
return A.ApS(
|
||||||
Ap[S2, T],
|
Ap[S2, T],
|
||||||
Map[S1, func(T) S2],
|
Map[S1, func(T) S2],
|
||||||
@@ -148,3 +149,158 @@ func ApS[S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 Address struct {
|
||||||
|
// Street string
|
||||||
|
// City string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type Person struct {
|
||||||
|
// Name string
|
||||||
|
// Address Address
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Create a lens for the Address field
|
||||||
|
// addressLens := lens.MakeLens(
|
||||||
|
// func(p Person) Address { return p.Address },
|
||||||
|
// func(p Person, a Address) Person { p.Address = a; return p },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Use ApSL to update the address
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// option.Some(Person{Name: "Alice"}),
|
||||||
|
// option.ApSL(
|
||||||
|
// addressLens,
|
||||||
|
// option.Some(Address{Street: "Main St", City: "NYC"}),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
func ApSL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa Option[T],
|
||||||
|
) Kleisli[Option[S], S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindL attaches the result of a computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Bind with a lens, allowing you to use
|
||||||
|
// optics to update nested structures based on their current values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The computation function f receives the current value of the focused field and returns
|
||||||
|
// an Option that produces the new value.
|
||||||
|
//
|
||||||
|
// Unlike ApSL, BindL uses monadic sequencing, meaning the computation f can depend on
|
||||||
|
// the current value of the focused field.
|
||||||
|
//
|
||||||
|
// 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 the counter, but return None if it would exceed 100
|
||||||
|
// increment := func(v int) option.Option[int] {
|
||||||
|
// if v >= 100 {
|
||||||
|
// return option.None[int]()
|
||||||
|
// }
|
||||||
|
// return option.Some(v + 1)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// option.Some(Counter{Value: 42}),
|
||||||
|
// option.BindL(valueLens, increment),
|
||||||
|
// ) // Some(Counter{Value: 43})
|
||||||
|
func BindL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[T, T],
|
||||||
|
) Kleisli[Option[S], S] {
|
||||||
|
return Bind[S, S, T](lens.Set, func(s S) Option[T] {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetL attaches the result of a pure computation to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines Let with a lens, allowing you to use
|
||||||
|
// optics to update nested structures with pure transformations.
|
||||||
|
//
|
||||||
|
// The lens parameter provides both the getter and setter for a field within the structure S.
|
||||||
|
// The transformation function f receives the current value of the focused field and returns
|
||||||
|
// the new value directly (not wrapped in Option).
|
||||||
|
//
|
||||||
|
// This is useful for pure transformations that cannot fail, such as mathematical operations,
|
||||||
|
// string manipulations, or other deterministic updates.
|
||||||
|
//
|
||||||
|
// 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 the counter value
|
||||||
|
// double := func(v int) int { return v * 2 }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// option.Some(Counter{Value: 21}),
|
||||||
|
// option.LetL(valueLens, double),
|
||||||
|
// ) // Some(Counter{Value: 42})
|
||||||
|
func LetL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Kleisli[Option[S], S] {
|
||||||
|
return Let[S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(s))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetToL attaches a constant value to a context using a lens-based setter.
|
||||||
|
// This is a convenience function that combines LetTo with a lens, allowing you to use
|
||||||
|
// optics to set nested fields to specific values.
|
||||||
|
//
|
||||||
|
// The lens parameter provides the setter for a field within the structure S.
|
||||||
|
// Unlike LetL which transforms the current value, LetToL simply replaces it with
|
||||||
|
// the provided constant value b.
|
||||||
|
//
|
||||||
|
// This is useful for resetting fields, initializing values, or setting fields to
|
||||||
|
// predetermined constants.
|
||||||
|
//
|
||||||
|
// 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(
|
||||||
|
// option.Some(Config{Debug: true, Timeout: 30}),
|
||||||
|
// option.LetToL(debugLens, false),
|
||||||
|
// ) // Some(Config{Debug: false, Timeout: 30})
|
||||||
|
func LetToL[S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Kleisli[Option[S], S] {
|
||||||
|
return LetTo[S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ type Option[A any] struct {
|
|||||||
value A
|
value A
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Kleisli[A, B any] = func(A) Option[B]
|
||||||
|
Operator[A, B any] = Kleisli[Option[A], B]
|
||||||
|
)
|
||||||
|
|
||||||
// optString prints some debug info for the object
|
// optString prints some debug info for the object
|
||||||
//
|
//
|
||||||
//go:noinline
|
//go:noinline
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
type optionFunctor[A, B any] struct{}
|
type optionFunctor[A, B any] struct{}
|
||||||
|
|
||||||
func (o *optionFunctor[A, B]) Map(f func(A) B) func(Option[A]) Option[B] {
|
func (o *optionFunctor[A, B]) Map(f func(A) B) Operator[A, B] {
|
||||||
return Map[A, B](f)
|
return Map[A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
952
v2/option/gen.go
952
v2/option/gen.go
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ import (
|
|||||||
L "github.com/IBM/fp-go/v2/logging"
|
L "github.com/IBM/fp-go/v2/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func _log[A any](left func(string, ...any), right func(string, ...any), prefix string) func(Option[A]) Option[A] {
|
func _log[A any](left func(string, ...any), right func(string, ...any), prefix string) Kleisli[Option[A], A] {
|
||||||
return Fold(
|
return Fold(
|
||||||
func() Option[A] {
|
func() Option[A] {
|
||||||
left("%s", prefix)
|
left("%s", prefix)
|
||||||
@@ -55,9 +55,9 @@ func _log[A any](left func(string, ...any), right func(string, ...any), prefix s
|
|||||||
// None[int](),
|
// None[int](),
|
||||||
// logger("step1"), // logs "step1"
|
// logger("step1"), // logs "step1"
|
||||||
// ) // None
|
// ) // None
|
||||||
func Logger[A any](loggers ...*log.Logger) func(string) func(Option[A]) Option[A] {
|
func Logger[A any](loggers ...*log.Logger) func(string) Kleisli[Option[A], A] {
|
||||||
left, right := L.LoggingCallbacks(loggers...)
|
left, right := L.LoggingCallbacks(loggers...)
|
||||||
return func(prefix string) func(Option[A]) Option[A] {
|
return func(prefix string) Kleisli[Option[A], A] {
|
||||||
delegate := _log[A](left, right, prefix)
|
delegate := _log[A](left, right, prefix)
|
||||||
return func(ma Option[A]) Option[A] {
|
return func(ma Option[A]) Option[A] {
|
||||||
return F.Pipe1(
|
return F.Pipe1(
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ func (o *optionMonad[A, B]) Of(a A) Option[A] {
|
|||||||
return Of[A](a)
|
return Of[A](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *optionMonad[A, B]) Map(f func(A) B) func(Option[A]) Option[B] {
|
func (o *optionMonad[A, B]) Map(f func(A) B) Operator[A, B] {
|
||||||
return Map[A, B](f)
|
return Map[A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *optionMonad[A, B]) Chain(f func(A) Option[B]) func(Option[A]) Option[B] {
|
func (o *optionMonad[A, B]) Chain(f Kleisli[A, B]) Operator[A, B] {
|
||||||
return Chain[A, B](f)
|
return Chain[A, B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func fromPredicate[A any](a A, pred func(A) bool) Option[A] {
|
|||||||
// isPositive := FromPredicate(func(n int) bool { return n > 0 })
|
// isPositive := FromPredicate(func(n int) bool { return n > 0 })
|
||||||
// result := isPositive(5) // Some(5)
|
// result := isPositive(5) // Some(5)
|
||||||
// result := isPositive(-1) // None
|
// result := isPositive(-1) // None
|
||||||
func FromPredicate[A any](pred func(A) bool) func(A) Option[A] {
|
func FromPredicate[A any](pred func(A) bool) Kleisli[A, A] {
|
||||||
return F.Bind2nd(fromPredicate[A], pred)
|
return F.Bind2nd(fromPredicate[A], pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ func FromNillable[A any](a *A) Option[*A] {
|
|||||||
// return n, err == nil
|
// return n, err == nil
|
||||||
// })
|
// })
|
||||||
// result := parseNum("42") // Some(42)
|
// result := parseNum("42") // Some(42)
|
||||||
func FromValidation[A, B any](f func(A) (B, bool)) func(A) Option[B] {
|
func FromValidation[A, B any](f func(A) (B, bool)) Kleisli[A, B] {
|
||||||
return Optionize1(f)
|
return Optionize1(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ func MonadAp[B, A any](fab Option[func(A) B], fa Option[A]) Option[B] {
|
|||||||
// applyTo5 := Ap[int](fa)
|
// applyTo5 := Ap[int](fa)
|
||||||
// fab := Some(func(x int) int { return x * 2 })
|
// fab := Some(func(x int) int { return x * 2 })
|
||||||
// result := applyTo5(fab) // Some(10)
|
// result := applyTo5(fab) // Some(10)
|
||||||
func Ap[B, A any](fa Option[A]) func(Option[func(A) B]) Option[B] {
|
func Ap[B, A any](fa Option[A]) Operator[func(A) B, B] {
|
||||||
return F.Bind2nd(MonadAp[B, A], fa)
|
return F.Bind2nd(MonadAp[B, A], fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ func MonadMap[A, B any](fa Option[A], f func(A) B) Option[B] {
|
|||||||
// double := Map(func(x int) int { return x * 2 })
|
// double := Map(func(x int) int { return x * 2 })
|
||||||
// result := double(Some(5)) // Some(10)
|
// result := double(Some(5)) // Some(10)
|
||||||
// result := double(None[int]()) // None
|
// result := double(None[int]()) // None
|
||||||
func Map[A, B any](f func(a A) B) func(Option[A]) Option[B] {
|
func Map[A, B any](f func(a A) B) Operator[A, B] {
|
||||||
return Chain(F.Flow2(f, Some[B]))
|
return Chain(F.Flow2(f, Some[B]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ func MonadMapTo[A, B any](fa Option[A], b B) Option[B] {
|
|||||||
//
|
//
|
||||||
// replaceWith42 := MapTo[string, int](42)
|
// replaceWith42 := MapTo[string, int](42)
|
||||||
// result := replaceWith42(Some("hello")) // Some(42)
|
// result := replaceWith42(Some("hello")) // Some(42)
|
||||||
func MapTo[A, B any](b B) func(Option[A]) Option[B] {
|
func MapTo[A, B any](b B) Operator[A, B] {
|
||||||
return F.Bind2nd(MonadMapTo[A, B], b)
|
return F.Bind2nd(MonadMapTo[A, B], b)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ func GetOrElse[A any](onNone func() A) func(Option[A]) A {
|
|||||||
// if x > 0 { return Some(x * 2) }
|
// if x > 0 { return Some(x * 2) }
|
||||||
// return None[int]()
|
// return None[int]()
|
||||||
// }) // Some(10)
|
// }) // Some(10)
|
||||||
func MonadChain[A, B any](fa Option[A], f func(A) Option[B]) Option[B] {
|
func MonadChain[A, B any](fa Option[A], f Kleisli[A, B]) Option[B] {
|
||||||
return MonadFold(fa, None[B], f)
|
return MonadFold(fa, None[B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ func MonadChain[A, B any](fa Option[A], f func(A) Option[B]) Option[B] {
|
|||||||
// return None[int]()
|
// return None[int]()
|
||||||
// })
|
// })
|
||||||
// result := validate(Some(5)) // Some(10)
|
// result := validate(Some(5)) // Some(10)
|
||||||
func Chain[A, B any](f func(A) Option[B]) func(Option[A]) Option[B] {
|
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
|
||||||
return Fold(None[B], f)
|
return Fold(None[B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ func MonadChainTo[A, B any](_ Option[A], mb Option[B]) Option[B] {
|
|||||||
//
|
//
|
||||||
// replaceWith := ChainTo(Some("hello"))
|
// replaceWith := ChainTo(Some("hello"))
|
||||||
// result := replaceWith(Some(42)) // Some("hello")
|
// result := replaceWith(Some(42)) // Some("hello")
|
||||||
func ChainTo[A, B any](mb Option[B]) func(Option[A]) Option[B] {
|
func ChainTo[A, B any](mb Option[B]) Operator[A, B] {
|
||||||
return F.Bind2nd(MonadChainTo[A, B], mb)
|
return F.Bind2nd(MonadChainTo[A, B], mb)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ func ChainTo[A, B any](mb Option[B]) func(Option[A]) Option[B] {
|
|||||||
// result := MonadChainFirst(Some(5), func(x int) Option[string] {
|
// result := MonadChainFirst(Some(5), func(x int) Option[string] {
|
||||||
// return Some(fmt.Sprintf("%d", x))
|
// return Some(fmt.Sprintf("%d", x))
|
||||||
// }) // Some(5) - original value is kept
|
// }) // Some(5) - original value is kept
|
||||||
func MonadChainFirst[A, B any](ma Option[A], f func(A) Option[B]) Option[A] {
|
func MonadChainFirst[A, B any](ma Option[A], f Kleisli[A, B]) Option[A] {
|
||||||
return C.MonadChainFirst(
|
return C.MonadChainFirst(
|
||||||
MonadChain[A, A],
|
MonadChain[A, A],
|
||||||
MonadMap[B, A],
|
MonadMap[B, A],
|
||||||
@@ -271,7 +271,7 @@ func MonadChainFirst[A, B any](ma Option[A], f func(A) Option[B]) Option[A] {
|
|||||||
// return Some("logged")
|
// return Some("logged")
|
||||||
// })
|
// })
|
||||||
// result := logAndKeep(Some(5)) // Some(5)
|
// result := logAndKeep(Some(5)) // Some(5)
|
||||||
func ChainFirst[A, B any](f func(A) Option[B]) func(Option[A]) Option[A] {
|
func ChainFirst[A, B any](f Kleisli[A, B]) Kleisli[Option[A], A] {
|
||||||
return C.ChainFirst(
|
return C.ChainFirst(
|
||||||
Chain[A, A],
|
Chain[A, A],
|
||||||
Map[B, A],
|
Map[B, A],
|
||||||
@@ -309,7 +309,7 @@ func MonadAlt[A any](fa Option[A], that func() Option[A]) Option[A] {
|
|||||||
// withDefault := Alt(func() Option[int] { return Some(0) })
|
// withDefault := Alt(func() Option[int] { return Some(0) })
|
||||||
// result := withDefault(Some(5)) // Some(5)
|
// result := withDefault(Some(5)) // Some(5)
|
||||||
// result := withDefault(None[int]()) // Some(0)
|
// result := withDefault(None[int]()) // Some(0)
|
||||||
func Alt[A any](that func() Option[A]) func(Option[A]) Option[A] {
|
func Alt[A any](that func() Option[A]) Kleisli[Option[A], A] {
|
||||||
return Fold(that, Of[A])
|
return Fold(that, Of[A])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@ func Reduce[A, B any](f func(B, A) B, initial B) func(Option[A]) B {
|
|||||||
// result := isPositive(Some(5)) // Some(5)
|
// result := isPositive(Some(5)) // Some(5)
|
||||||
// result := isPositive(Some(-1)) // None
|
// result := isPositive(Some(-1)) // None
|
||||||
// result := isPositive(None[int]()) // None
|
// result := isPositive(None[int]()) // None
|
||||||
func Filter[A any](pred func(A) bool) func(Option[A]) Option[A] {
|
func Filter[A any](pred func(A) bool) Kleisli[Option[A], A] {
|
||||||
return Fold(None[A], F.Ternary(pred, Of[A], F.Ignore1of1[A](None[A])))
|
return Fold(None[A], F.Ternary(pred, Of[A], F.Ignore1of1[A](None[A])))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,6 +383,6 @@ func MonadFlap[B, A any](fab Option[func(A) B], a A) Option[B] {
|
|||||||
// applyFive := Flap[int](5)
|
// applyFive := Flap[int](5)
|
||||||
// fab := Some(func(x int) int { return x * 2 })
|
// fab := Some(func(x int) int { return x * 2 })
|
||||||
// result := applyFive(fab) // Some(10)
|
// result := applyFive(fab) // Some(10)
|
||||||
func Flap[B, A any](a A) func(Option[func(A) B]) Option[B] {
|
func Flap[B, A any](a A) Operator[func(A) B, B] {
|
||||||
return FC.Flap(Map[func(A) B, B], a)
|
return FC.Flap(Map[func(A) B, B], a)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import (
|
|||||||
// }
|
// }
|
||||||
// input := map[string]int{"a": 1, "b": 2}
|
// input := map[string]int{"a": 1, "b": 2}
|
||||||
// result := TraverseRecordG[map[string]int, map[string]int](validate)(input) // Some(map[a:2 b:4])
|
// result := TraverseRecordG[map[string]int, map[string]int](validate)(input) // Some(map[a:2 b:4])
|
||||||
func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f func(A) Option[B]) func(GA) Option[GB] {
|
func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f Kleisli[A, B]) Kleisli[GA, GB] {
|
||||||
return RR.Traverse[GA](
|
return RR.Traverse[GA](
|
||||||
Of[GB],
|
Of[GB],
|
||||||
Map[GB, func(B) GB],
|
Map[GB, func(B) GB],
|
||||||
@@ -53,7 +53,7 @@ func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f func(A)
|
|||||||
// }
|
// }
|
||||||
// input := map[string]int{"a": 1, "b": 2}
|
// input := map[string]int{"a": 1, "b": 2}
|
||||||
// result := TraverseRecord(validate)(input) // Some(map[a:"1" b:"2"])
|
// result := TraverseRecord(validate)(input) // Some(map[a:"1" b:"2"])
|
||||||
func TraverseRecord[K comparable, A, B any](f func(A) Option[B]) func(map[K]A) Option[map[K]B] {
|
func TraverseRecord[K comparable, A, B any](f Kleisli[A, B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return TraverseRecordG[map[K]A, map[K]B](f)
|
return TraverseRecordG[map[K]A, map[K]B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ func TraverseRecord[K comparable, A, B any](f func(A) Option[B]) func(map[K]A) O
|
|||||||
// }
|
// }
|
||||||
// input := map[string]int{"a": 1, "b": 2}
|
// input := map[string]int{"a": 1, "b": 2}
|
||||||
// result := TraverseRecordWithIndexG[map[string]int, map[string]string](f)(input) // Some(map[a:"a:1" b:"b:2"])
|
// result := TraverseRecordWithIndexG[map[string]int, map[string]string](f)(input) // Some(map[a:"a:1" b:"b:2"])
|
||||||
func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f func(K, A) Option[B]) func(GA) Option[GB] {
|
func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](f func(K, A) Option[B]) Kleisli[GA, GB] {
|
||||||
return RR.TraverseWithIndex[GA](
|
return RR.TraverseWithIndex[GA](
|
||||||
Of[GB],
|
Of[GB],
|
||||||
Map[GB, func(B) GB],
|
Map[GB, func(B) GB],
|
||||||
@@ -89,7 +89,7 @@ func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, A, B any](
|
|||||||
// }
|
// }
|
||||||
// input := map[string]int{"a": 1, "b": 2}
|
// input := map[string]int{"a": 1, "b": 2}
|
||||||
// result := TraverseRecordWithIndex(f)(input) // Some(map[a:1 b:2])
|
// result := TraverseRecordWithIndex(f)(input) // Some(map[a:1 b:2])
|
||||||
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Option[B]) func(map[K]A) Option[map[K]B] {
|
func TraverseRecordWithIndex[K comparable, A, B any](f func(K, A) Option[B]) Kleisli[map[K]A, map[K]B] {
|
||||||
return TraverseRecordWithIndexG[map[K]A, map[K]B](f)
|
return TraverseRecordWithIndexG[map[K]A, map[K]B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
// )
|
// )
|
||||||
func Sequence[A, HKTA, HKTOA any](
|
func Sequence[A, HKTA, HKTOA any](
|
||||||
mof func(Option[A]) HKTOA,
|
mof func(Option[A]) HKTOA,
|
||||||
mmap func(func(A) Option[A]) func(HKTA) HKTOA,
|
mmap func(Kleisli[A, A]) func(HKTA) HKTOA,
|
||||||
) func(Option[HKTA]) HKTOA {
|
) func(Option[HKTA]) HKTOA {
|
||||||
return Fold(F.Nullary2(None[A], mof), mmap(Some[A]))
|
return Fold(F.Nullary2(None[A], mof), mmap(Some[A]))
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ func Sequence[A, HKTA, HKTOA any](
|
|||||||
// )
|
// )
|
||||||
func Traverse[A, B, HKTB, HKTOB any](
|
func Traverse[A, B, HKTB, HKTOB any](
|
||||||
mof func(Option[B]) HKTOB,
|
mof func(Option[B]) HKTOB,
|
||||||
mmap func(func(B) Option[B]) func(HKTB) HKTOB,
|
mmap func(Kleisli[B, B]) func(HKTB) HKTOB,
|
||||||
) func(func(A) HKTB) func(Option[A]) HKTOB {
|
) func(func(A) HKTB) func(Option[A]) HKTOB {
|
||||||
onNone := F.Nullary2(None[B], mof)
|
onNone := F.Nullary2(None[B], mof)
|
||||||
onSome := mmap(Some[B])
|
onSome := mmap(Some[B])
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/internal/apply"
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
"github.com/IBM/fp-go/v2/internal/chain"
|
"github.com/IBM/fp-go/v2/internal/chain"
|
||||||
"github.com/IBM/fp-go/v2/internal/functor"
|
"github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||||
@@ -208,3 +209,158 @@ func ApS[R, S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
// type Config struct {
|
||||||
|
// DefaultHost string
|
||||||
|
// DefaultPort int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLens(
|
||||||
|
// func(s State) int { return s.Port },
|
||||||
|
// func(s State, p int) State { s.Port = p; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// getPort := reader.Asks(func(c Config) int { return c.DefaultPort })
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// reader.Of[Config](State{Host: "localhost"}),
|
||||||
|
// reader.ApSL(portLens, getPort),
|
||||||
|
// )
|
||||||
|
func ApSL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa Reader[R, T],
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Reader computation that produces an updated value.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// Config ConfigData
|
||||||
|
// Status string
|
||||||
|
// }
|
||||||
|
// type ConfigData struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
// type Env struct {
|
||||||
|
// DefaultHost string
|
||||||
|
// DefaultPort int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// configLens := lens.MakeLens(
|
||||||
|
// func(s State) ConfigData { return s.Config },
|
||||||
|
// func(s State, c ConfigData) State { s.Config = c; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// reader.Do[Env](State{}),
|
||||||
|
// reader.BindL(configLens, func(cfg ConfigData) reader.Reader[Env, ConfigData] {
|
||||||
|
// return reader.Asks(func(e Env) ConfigData {
|
||||||
|
// return ConfigData{Host: e.DefaultHost, Port: e.DefaultPort}
|
||||||
|
// })
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
func BindL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) Reader[R, T],
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return Bind[R, S, S, T](lens.Set, func(s S) Reader[R, T] {
|
||||||
|
return f(lens.Get(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 Reader).
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// Config ConfigData
|
||||||
|
// Status string
|
||||||
|
// }
|
||||||
|
// type ConfigData struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// configLens := lens.MakeLens(
|
||||||
|
// func(s State) ConfigData { return s.Config },
|
||||||
|
// func(s State, c ConfigData) State { s.Config = c; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// reader.Do[any](State{Config: ConfigData{Host: "localhost"}}),
|
||||||
|
// reader.LetL(configLens, func(cfg ConfigData) ConfigData {
|
||||||
|
// cfg.Port = 8080
|
||||||
|
// return cfg
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
func LetL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return Let[R, S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(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 {
|
||||||
|
// Config ConfigData
|
||||||
|
// Status string
|
||||||
|
// }
|
||||||
|
// type ConfigData struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// configLens := lens.MakeLens(
|
||||||
|
// func(s State) ConfigData { return s.Config },
|
||||||
|
// func(s State, c ConfigData) State { s.Config = c; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// newConfig := ConfigData{Host: "localhost", Port: 8080}
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// reader.Do[any](State{}),
|
||||||
|
// reader.LetToL(configLens, newConfig),
|
||||||
|
// )
|
||||||
|
func LetToL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return LetTo[R, S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package readereither
|
package readereither
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
G "github.com/IBM/fp-go/v2/readereither/generic"
|
G "github.com/IBM/fp-go/v2/readereither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -158,3 +159,148 @@ func ApS[R, E, S1, S2, T any](
|
|||||||
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
|
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
|
||||||
return G.ApS[ReaderEither[R, E, S1], ReaderEither[R, E, S2], ReaderEither[R, E, T], R, E, S1, S2, T](setter, fa)
|
return G.ApS[ReaderEither[R, E, S1], ReaderEither[R, E, S2], ReaderEither[R, E, T], R, E, S1, S2, T](setter, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 ApSL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderEither[R, E, T],
|
||||||
|
) func(ReaderEither[R, E, S]) ReaderEither[R, E, S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ReaderEither 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.ReaderEither[Env, error, User] {
|
||||||
|
// return readereither.Asks(func(env Env) either.Either[error, User] {
|
||||||
|
// return env.UserService.GetUser()
|
||||||
|
// })
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
func BindL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) ReaderEither[R, E, T],
|
||||||
|
) func(ReaderEither[R, E, S]) ReaderEither[R, E, S] {
|
||||||
|
return Bind[R, E, S, S, T](lens.Set, func(s S) ReaderEither[R, E, T] {
|
||||||
|
return f(lens.Get(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 ReaderEither).
|
||||||
|
//
|
||||||
|
// 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 LetL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) func(ReaderEither[R, E, S]) ReaderEither[R, E, S] {
|
||||||
|
return Let[R, E, S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(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 LetToL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) func(ReaderEither[R, E, S]) ReaderEither[R, E, S] {
|
||||||
|
return LetTo[R, E, S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/IBM/fp-go/v2/internal/apply"
|
"github.com/IBM/fp-go/v2/internal/apply"
|
||||||
"github.com/IBM/fp-go/v2/internal/chain"
|
"github.com/IBM/fp-go/v2/internal/chain"
|
||||||
"github.com/IBM/fp-go/v2/internal/functor"
|
"github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||||
@@ -181,3 +182,146 @@ func ApS[R, S1, S2, T any](
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
// type Config struct {
|
||||||
|
// DefaultHost string
|
||||||
|
// DefaultPort int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLens(
|
||||||
|
// func(s State) int { return s.Port },
|
||||||
|
// func(s State, p int) State { s.Port = p; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// getPort := readerio.Asks(func(c Config) io.IO[int] {
|
||||||
|
// return io.Of(c.DefaultPort)
|
||||||
|
// })
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerio.Of[Config](State{Host: "localhost"}),
|
||||||
|
// readerio.ApSL(portLens, getPort),
|
||||||
|
// )
|
||||||
|
func ApSL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderIO[R, T],
|
||||||
|
) func(ReaderIO[R, S]) ReaderIO[R, S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ReaderIO computation that produces an updated value.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
// type Config struct {
|
||||||
|
// DefaultHost string
|
||||||
|
// DefaultPort int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLens(
|
||||||
|
// func(s State) int { return s.Port },
|
||||||
|
// func(s State, p int) State { s.Port = p; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerio.Do[Config](State{Host: "localhost"}),
|
||||||
|
// readerio.BindL(portLens, func(port int) readerio.ReaderIO[Config, int] {
|
||||||
|
// return readerio.Asks(func(c Config) io.IO[int] {
|
||||||
|
// return io.Of(c.DefaultPort)
|
||||||
|
// })
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
func BindL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) ReaderIO[R, T],
|
||||||
|
) func(ReaderIO[R, S]) ReaderIO[R, S] {
|
||||||
|
return Bind[R, S, S, T](lens.Set, func(s S) ReaderIO[R, T] {
|
||||||
|
return f(lens.Get(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 ReaderIO).
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLens(
|
||||||
|
// func(s State) int { return s.Port },
|
||||||
|
// func(s State, p int) State { s.Port = p; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerio.Do[any](State{Host: "localhost", Port: 8080}),
|
||||||
|
// readerio.LetL(portLens, func(port int) int {
|
||||||
|
// return port + 1
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
func LetL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) func(ReaderIO[R, S]) ReaderIO[R, S] {
|
||||||
|
return Let[R, S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(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 {
|
||||||
|
// Host string
|
||||||
|
// Port int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// portLens := lens.MakeLens(
|
||||||
|
// func(s State) int { return s.Port },
|
||||||
|
// func(s State, p int) State { s.Port = p; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerio.Do[any](State{Host: "localhost"}),
|
||||||
|
// readerio.LetToL(portLens, 8080),
|
||||||
|
// )
|
||||||
|
func LetToL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) func(ReaderIO[R, S]) ReaderIO[R, S] {
|
||||||
|
return LetTo[R, S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package readerioeither
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
IOE "github.com/IBM/fp-go/v2/ioeither"
|
IOE "github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
G "github.com/IBM/fp-go/v2/readerioeither/generic"
|
G "github.com/IBM/fp-go/v2/readerioeither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +35,8 @@ import (
|
|||||||
// PostRepo PostRepository
|
// PostRepo PostRepository
|
||||||
// }
|
// }
|
||||||
// result := readerioeither.Do[Env, error](State{})
|
// result := readerioeither.Do[Env, error](State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Do[R, E, S any](
|
func Do[R, E, S any](
|
||||||
empty S,
|
empty S,
|
||||||
) ReaderIOEither[R, E, S] {
|
) ReaderIOEither[R, E, S] {
|
||||||
@@ -82,6 +85,8 @@ func Do[R, E, S any](
|
|||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Bind[R, E, S1, S2, T any](
|
func Bind[R, E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) ReaderIOEither[R, E, T],
|
f func(S1) ReaderIOEither[R, E, T],
|
||||||
@@ -90,6 +95,8 @@ func Bind[R, E, S1, S2, T any](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Let[R, E, S1, S2, T any](
|
func Let[R, E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
@@ -98,6 +105,8 @@ func Let[R, E, S1, S2, T any](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func LetTo[R, E, S1, S2, T any](
|
func LetTo[R, E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
@@ -106,6 +115,8 @@ func LetTo[R, E, S1, S2, T any](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BindTo initializes a new state [S1] from a value [T]
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func BindTo[R, E, S1, T any](
|
func BindTo[R, E, S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) Operator[R, E, T, S1] {
|
) Operator[R, E, T, S1] {
|
||||||
@@ -153,9 +164,164 @@ func BindTo[R, E, S1, T any](
|
|||||||
// getPosts,
|
// getPosts,
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApS[R, E, S1, S2, T any](
|
func ApS[R, E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa ReaderIOEither[R, E, T],
|
fa ReaderIOEither[R, E, T],
|
||||||
) Operator[R, E, S1, S2] {
|
) Operator[R, E, S1, S2] {
|
||||||
return G.ApS[ReaderIOEither[R, E, func(T) S2], ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ReaderIOEither[R, E, T], IOE.IOEither[E, func(T) S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], IOE.IOEither[E, T], R, E, S1, S2, T](setter, fa)
|
return G.ApS[ReaderIOEither[R, E, func(T) S2], ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ReaderIOEither[R, E, T], IOE.IOEither[E, func(T) S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], IOE.IOEither[E, T], R, E, S1, S2, T](setter, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// Posts []Post
|
||||||
|
// }
|
||||||
|
// type Env struct {
|
||||||
|
// UserRepo UserRepository
|
||||||
|
// PostRepo PostRepository
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// getUser := readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||||
|
// return env.UserRepo.FindUser()
|
||||||
|
// })
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Of[Env, error](State{}),
|
||||||
|
// readerioeither.ApSL(userLens, getUser),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApSL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderIOEither[R, E, T],
|
||||||
|
) Operator[R, E, S, S] {
|
||||||
|
return ApS(lens.Set, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ReaderIOEither computation that produces an updated value.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// User User
|
||||||
|
// Posts []Post
|
||||||
|
// }
|
||||||
|
// type Env struct {
|
||||||
|
// UserRepo UserRepository
|
||||||
|
// PostRepo PostRepository
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do[Env, error](State{}),
|
||||||
|
// readerioeither.BindL(userLens, func(user User) readerioeither.ReaderIOEither[Env, error, User] {
|
||||||
|
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||||
|
// return env.UserRepo.FindUser()
|
||||||
|
// })
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) ReaderIOEither[R, E, T],
|
||||||
|
) Operator[R, E, S, S] {
|
||||||
|
return Bind[R, E, S, S, T](lens.Set, func(s S) ReaderIOEither[R, E, T] {
|
||||||
|
return f(lens.Get(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 ReaderIOEither).
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// User User
|
||||||
|
// Posts []Post
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do[any, error](State{User: User{Name: "Alice"}}),
|
||||||
|
// readerioeither.LetL(userLens, func(user User) User {
|
||||||
|
// user.Name = "Bob"
|
||||||
|
// return user
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Operator[R, E, S, S] {
|
||||||
|
return Let[R, E, S, S, T](lens.Set, func(s S) T {
|
||||||
|
return f(lens.Get(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
|
||||||
|
// Posts []Post
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// userLens := lens.MakeLens(
|
||||||
|
// func(s State) User { return s.User },
|
||||||
|
// func(s State, u User) State { s.User = u; return s },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// newUser := User{Name: "Bob", ID: 123}
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do[any, error](State{}),
|
||||||
|
// readerioeither.LetToL(userLens, newUser),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetToL[R, E, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Operator[R, E, S, S] {
|
||||||
|
return LetTo[R, E, S, S, T](lens.Set, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import (
|
|||||||
|
|
||||||
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
// Bracket makes sure that a resource is cleaned up in the event of an error. The release action is called regardless of
|
||||||
// whether the body action returns and error or not.
|
// whether the body action returns and error or not.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Bracket[
|
func Bracket[
|
||||||
R, E, A, B, ANY any](
|
R, E, A, B, ANY any](
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Eq implements the equals predicate for values contained in the IOEither monad
|
// Eq implements the equals predicate for values contained in the IOEither monad
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eq[R, E, A any](eq EQ.Eq[either.Either[E, A]]) func(R) EQ.Eq[ReaderIOEither[R, E, A]] {
|
func Eq[R, E, A any](eq EQ.Eq[either.Either[E, A]]) func(R) EQ.Eq[ReaderIOEither[R, E, A]] {
|
||||||
return readerio.Eq[R](eq)
|
return readerio.Eq[R](eq)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromStrictEquals[R any, E, A comparable]() func(R) EQ.Eq[ReaderIOEither[R, E, A]] {
|
func FromStrictEquals[R any, E, A comparable]() func(R) EQ.Eq[ReaderIOEither[R, E, A]] {
|
||||||
return Eq[R](either.FromStrictEquals[E, A]())
|
return Eq[R](either.FromStrictEquals[E, A]())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,205 +4,270 @@
|
|||||||
|
|
||||||
package readerioeither
|
package readerioeither
|
||||||
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
G "github.com/IBM/fp-go/v2/readerioeither/generic"
|
G "github.com/IBM/fp-go/v2/readerioeither/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// From0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[R]]
|
// From0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From0[F ~func(C) func() (R, error), C, R any](f F) func() ReaderIOEither[C, error, R] {
|
func From0[F ~func(C) func() (R, error), C, R any](f F) func() ReaderIOEither[C, error, R] {
|
||||||
return G.From0[ReaderIOEither[C, error, R]](f)
|
return G.From0[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize0[F ~func(C) (R, error), C, R any](f F) func() ReaderIOEither[C, error, R] {
|
func Eitherize0[F ~func(C) (R, error), C, R any](f F) func() ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize0[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize0[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize0 converts a function with 1 parameters returning a [ReaderIOEither[C, error, R]] into a function with 0 parameters returning a tuple.
|
// Uneitherize0 converts a function with 1 parameters returning a [ReaderIOEither[C, error, R]] into a function with 0 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize0[F ~func() ReaderIOEither[C, error, R], C, R any](f F) func(C) (R, error) {
|
func Uneitherize0[F ~func() ReaderIOEither[C, error, R], C, R any](f F) func(C) (R, error) {
|
||||||
return G.Uneitherize0[ReaderIOEither[C, error, R], func(C)(R, error)](f)
|
return G.Uneitherize0[ReaderIOEither[C, error, R], func(C) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]]
|
// From1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From1[F ~func(C, T0) func() (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R] {
|
func From1[F ~func(C, T0) func() (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R] {
|
||||||
return G.From1[ReaderIOEither[C, error, R]](f)
|
return G.From1[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize1[F ~func(C, T0) (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R] {
|
func Eitherize1[F ~func(C, T0) (R, error), T0, C, R any](f F) func(T0) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize1[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize1[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize1 converts a function with 2 parameters returning a [ReaderIOEither[C, error, R]] into a function with 1 parameters returning a tuple.
|
// Uneitherize1 converts a function with 2 parameters returning a [ReaderIOEither[C, error, R]] into a function with 1 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize1[F ~func(T0) ReaderIOEither[C, error, R], T0, C, R any](f F) func(C, T0) (R, error) {
|
func Uneitherize1[F ~func(T0) ReaderIOEither[C, error, R], T0, C, R any](f F) func(C, T0) (R, error) {
|
||||||
return G.Uneitherize1[ReaderIOEither[C, error, R], func(C, T0)(R, error)](f)
|
return G.Uneitherize1[ReaderIOEither[C, error, R], func(C, T0) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[R]]
|
// From2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From2[F ~func(C, T0, T1) func() (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R] {
|
func From2[F ~func(C, T0, T1) func() (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R] {
|
||||||
return G.From2[ReaderIOEither[C, error, R]](f)
|
return G.From2[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize2[F ~func(C, T0, T1) (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R] {
|
func Eitherize2[F ~func(C, T0, T1) (R, error), T0, T1, C, R any](f F) func(T0, T1) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize2[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize2[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize2 converts a function with 3 parameters returning a [ReaderIOEither[C, error, R]] into a function with 2 parameters returning a tuple.
|
// Uneitherize2 converts a function with 3 parameters returning a [ReaderIOEither[C, error, R]] into a function with 2 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize2[F ~func(T0, T1) ReaderIOEither[C, error, R], T0, T1, C, R any](f F) func(C, T0, T1) (R, error) {
|
func Uneitherize2[F ~func(T0, T1) ReaderIOEither[C, error, R], T0, T1, C, R any](f F) func(C, T0, T1) (R, error) {
|
||||||
return G.Uneitherize2[ReaderIOEither[C, error, R], func(C, T0, T1)(R, error)](f)
|
return G.Uneitherize2[ReaderIOEither[C, error, R], func(C, T0, T1) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[R]]
|
// From3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From3[F ~func(C, T0, T1, T2) func() (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R] {
|
func From3[F ~func(C, T0, T1, T2) func() (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R] {
|
||||||
return G.From3[ReaderIOEither[C, error, R]](f)
|
return G.From3[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize3[F ~func(C, T0, T1, T2) (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R] {
|
func Eitherize3[F ~func(C, T0, T1, T2) (R, error), T0, T1, T2, C, R any](f F) func(T0, T1, T2) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize3[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize3[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize3 converts a function with 4 parameters returning a [ReaderIOEither[C, error, R]] into a function with 3 parameters returning a tuple.
|
// Uneitherize3 converts a function with 4 parameters returning a [ReaderIOEither[C, error, R]] into a function with 3 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize3[F ~func(T0, T1, T2) ReaderIOEither[C, error, R], T0, T1, T2, C, R any](f F) func(C, T0, T1, T2) (R, error) {
|
func Uneitherize3[F ~func(T0, T1, T2) ReaderIOEither[C, error, R], T0, T1, T2, C, R any](f F) func(C, T0, T1, T2) (R, error) {
|
||||||
return G.Uneitherize3[ReaderIOEither[C, error, R], func(C, T0, T1, T2)(R, error)](f)
|
return G.Uneitherize3[ReaderIOEither[C, error, R], func(C, T0, T1, T2) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[R]]
|
// From4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From4[F ~func(C, T0, T1, T2, T3) func() (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R] {
|
func From4[F ~func(C, T0, T1, T2, T3) func() (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R] {
|
||||||
return G.From4[ReaderIOEither[C, error, R]](f)
|
return G.From4[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize4[F ~func(C, T0, T1, T2, T3) (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R] {
|
func Eitherize4[F ~func(C, T0, T1, T2, T3) (R, error), T0, T1, T2, T3, C, R any](f F) func(T0, T1, T2, T3) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize4[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize4[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize4 converts a function with 5 parameters returning a [ReaderIOEither[C, error, R]] into a function with 4 parameters returning a tuple.
|
// Uneitherize4 converts a function with 5 parameters returning a [ReaderIOEither[C, error, R]] into a function with 4 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOEither[C, error, R], T0, T1, T2, T3, C, R any](f F) func(C, T0, T1, T2, T3) (R, error) {
|
func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOEither[C, error, R], T0, T1, T2, T3, C, R any](f F) func(C, T0, T1, T2, T3) (R, error) {
|
||||||
return G.Uneitherize4[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3)(R, error)](f)
|
return G.Uneitherize4[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[R]]
|
// From5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From5[F ~func(C, T0, T1, T2, T3, T4) func() (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R] {
|
func From5[F ~func(C, T0, T1, T2, T3, T4) func() (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R] {
|
||||||
return G.From5[ReaderIOEither[C, error, R]](f)
|
return G.From5[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize5[F ~func(C, T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R] {
|
func Eitherize5[F ~func(C, T0, T1, T2, T3, T4) (R, error), T0, T1, T2, T3, T4, C, R any](f F) func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize5[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize5[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize5 converts a function with 6 parameters returning a [ReaderIOEither[C, error, R]] into a function with 5 parameters returning a tuple.
|
// Uneitherize5 converts a function with 6 parameters returning a [ReaderIOEither[C, error, R]] into a function with 5 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, C, R any](f F) func(C, T0, T1, T2, T3, T4) (R, error) {
|
func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, C, R any](f F) func(C, T0, T1, T2, T3, T4) (R, error) {
|
||||||
return G.Uneitherize5[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4)(R, error)](f)
|
return G.Uneitherize5[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[R]]
|
// From6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From6[F ~func(C, T0, T1, T2, T3, T4, T5) func() (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R] {
|
func From6[F ~func(C, T0, T1, T2, T3, T4, T5) func() (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R] {
|
||||||
return G.From6[ReaderIOEither[C, error, R]](f)
|
return G.From6[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize6[F ~func(C, T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R] {
|
func Eitherize6[F ~func(C, T0, T1, T2, T3, T4, T5) (R, error), T0, T1, T2, T3, T4, T5, C, R any](f F) func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize6[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize6[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize6 converts a function with 7 parameters returning a [ReaderIOEither[C, error, R]] into a function with 6 parameters returning a tuple.
|
// Uneitherize6 converts a function with 7 parameters returning a [ReaderIOEither[C, error, R]] into a function with 6 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5) (R, error) {
|
func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5) (R, error) {
|
||||||
return G.Uneitherize6[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5)(R, error)](f)
|
return G.Uneitherize6[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[R]]
|
// From7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) func() (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R] {
|
func From7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) func() (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R] {
|
||||||
return G.From7[ReaderIOEither[C, error, R]](f)
|
return G.From7[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R] {
|
func Eitherize7[F ~func(C, T0, T1, T2, T3, T4, T5, T6) (R, error), T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize7[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize7[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize7 converts a function with 8 parameters returning a [ReaderIOEither[C, error, R]] into a function with 7 parameters returning a tuple.
|
// Uneitherize7 converts a function with 8 parameters returning a [ReaderIOEither[C, error, R]] into a function with 7 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6) (R, error) {
|
func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6) (R, error) {
|
||||||
return G.Uneitherize7[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6)(R, error)](f)
|
return G.Uneitherize7[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[R]]
|
// From8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R] {
|
func From8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R] {
|
||||||
return G.From8[ReaderIOEither[C, error, R]](f)
|
return G.From8[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R] {
|
func Eitherize8[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize8[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize8[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize8 converts a function with 9 parameters returning a [ReaderIOEither[C, error, R]] into a function with 8 parameters returning a tuple.
|
// Uneitherize8 converts a function with 9 parameters returning a [ReaderIOEither[C, error, R]] into a function with 8 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error) {
|
func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error) {
|
||||||
return G.Uneitherize8[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7)(R, error)](f)
|
return G.Uneitherize8[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[R]]
|
// From9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R] {
|
func From9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R] {
|
||||||
return G.From9[ReaderIOEither[C, error, R]](f)
|
return G.From9[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R] {
|
func Eitherize9[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize9[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize9[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize9 converts a function with 10 parameters returning a [ReaderIOEither[C, error, R]] into a function with 9 parameters returning a tuple.
|
// Uneitherize9 converts a function with 10 parameters returning a [ReaderIOEither[C, error, R]] into a function with 9 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) {
|
func Uneitherize9[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error) {
|
||||||
return G.Uneitherize9[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8)(R, error)](f)
|
return G.Uneitherize9[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[R]]
|
// From10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R] {
|
func From10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) func() (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R] {
|
||||||
return G.From10[ReaderIOEither[C, error, R]](f)
|
return G.From10[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[C, error, R]]
|
// Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOEither[C, error, R]]
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Eitherize10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R] {
|
func Eitherize10[F ~func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error), T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R] {
|
||||||
return G.Eitherize10[ReaderIOEither[C, error, R]](f)
|
return G.Eitherize10[ReaderIOEither[C, error, R]](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uneitherize10 converts a function with 11 parameters returning a [ReaderIOEither[C, error, R]] into a function with 10 parameters returning a tuple.
|
// Uneitherize10 converts a function with 11 parameters returning a [ReaderIOEither[C, error, R]] into a function with 10 parameters returning a tuple.
|
||||||
// The first parameter is considered to be the context [C].
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) {
|
func Uneitherize10[F ~func(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) ReaderIOEither[C, error, R], T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, C, R any](f F) func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error) {
|
||||||
return G.Uneitherize10[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9)(R, error)](f)
|
return G.Uneitherize10[ReaderIOEither[C, error, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error)](f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,16 +23,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Pointed returns the pointed operations for [ReaderIOEither]
|
// Pointed returns the pointed operations for [ReaderIOEither]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Pointed[R, E, A any]() pointed.Pointed[A, ReaderIOEither[R, E, A]] {
|
func Pointed[R, E, A any]() pointed.Pointed[A, ReaderIOEither[R, E, A]] {
|
||||||
return G.Pointed[R, E, A, ReaderIOEither[R, E, A]]()
|
return G.Pointed[R, E, A, ReaderIOEither[R, E, A]]()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functor returns the functor operations for [ReaderIOEither]
|
// Functor returns the functor operations for [ReaderIOEither]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Functor[R, E, A, B any]() functor.Functor[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]] {
|
func Functor[R, E, A, B any]() functor.Functor[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]] {
|
||||||
return G.Functor[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]]()
|
return G.Functor[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]]()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Monad returns the monadic operations for [ReaderIOEither]
|
// Monad returns the monadic operations for [ReaderIOEither]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Monad[R, E, A, B any]() monad.Monad[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]] {
|
func Monad[R, E, A, B any]() monad.Monad[A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]] {
|
||||||
return G.Monad[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]]()
|
return G.Monad[R, E, A, B, ReaderIOEither[R, E, A], ReaderIOEither[R, E, B], ReaderIOEither[R, E, func(A) B]]()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ func FromReaderIO[R, E, A any](f func(A) ReaderIO[R, A]) func(A) ReaderIOEither[
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RightReaderIO lifts a ReaderIO into a ReaderIOEither, placing the result in the Right side.
|
// RightReaderIO lifts a ReaderIO into a ReaderIOEither, placing the result in the Right side.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func RightReaderIO[R, E, A any](ma ReaderIO[R, A]) ReaderIOEither[R, E, A] {
|
func RightReaderIO[R, E, A any](ma ReaderIO[R, A]) ReaderIOEither[R, E, A] {
|
||||||
return eithert.RightF(
|
return eithert.RightF(
|
||||||
readerio.MonadMap[R, A, either.Either[E, A]],
|
readerio.MonadMap[R, A, either.Either[E, A]],
|
||||||
@@ -60,6 +62,8 @@ func RightReaderIO[R, E, A any](ma ReaderIO[R, A]) ReaderIOEither[R, E, A] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LeftReaderIO lifts a ReaderIO into a ReaderIOEither, placing the result in the Left (error) side.
|
// LeftReaderIO lifts a ReaderIO into a ReaderIOEither, placing the result in the Left (error) side.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func LeftReaderIO[A, R, E any](me ReaderIO[R, E]) ReaderIOEither[R, E, A] {
|
func LeftReaderIO[A, R, E any](me ReaderIO[R, E]) ReaderIOEither[R, E, A] {
|
||||||
return eithert.LeftF(
|
return eithert.LeftF(
|
||||||
readerio.MonadMap[R, E, either.Either[E, A]],
|
readerio.MonadMap[R, E, either.Either[E, A]],
|
||||||
@@ -70,12 +74,16 @@ func LeftReaderIO[A, R, E any](me ReaderIO[R, E]) ReaderIOEither[R, E, A] {
|
|||||||
// MonadMap applies a function to the value inside a ReaderIOEither context.
|
// MonadMap applies a function to the value inside a ReaderIOEither context.
|
||||||
// If the computation is successful (Right), the function is applied to the value.
|
// If the computation is successful (Right), the function is applied to the value.
|
||||||
// If it's an error (Left), the error is propagated unchanged.
|
// If it's an error (Left), the error is propagated unchanged.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadMap[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) B) ReaderIOEither[R, E, B] {
|
func MonadMap[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) B) ReaderIOEither[R, E, B] {
|
||||||
return eithert.MonadMap(readerio.MonadMap[R, either.Either[E, A], either.Either[E, B]], fa, f)
|
return eithert.MonadMap(readerio.MonadMap[R, either.Either[E, A], either.Either[E, B]], fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map returns a function that applies a transformation to the success value of a ReaderIOEither.
|
// Map returns a function that applies a transformation to the success value of a ReaderIOEither.
|
||||||
// This is the curried version of MonadMap, useful for function composition.
|
// This is the curried version of MonadMap, useful for function composition.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Map[R, E, A, B any](f func(A) B) Operator[R, E, A, B] {
|
func Map[R, E, A, B any](f func(A) B) Operator[R, E, A, B] {
|
||||||
return eithert.Map(readerio.Map[R, either.Either[E, A], either.Either[E, B]], f)
|
return eithert.Map(readerio.Map[R, either.Either[E, A], either.Either[E, B]], f)
|
||||||
}
|
}
|
||||||
@@ -95,6 +103,8 @@ func MapTo[R, E, A, B any](b B) Operator[R, E, A, B] {
|
|||||||
// MonadChain sequences two computations where the second depends on the result of the first.
|
// MonadChain sequences two computations where the second depends on the result of the first.
|
||||||
// This is the fundamental operation for composing dependent effectful computations.
|
// This is the fundamental operation for composing dependent effectful computations.
|
||||||
// If the first computation fails, the second is not executed.
|
// If the first computation fails, the second is not executed.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChain[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEither[R, E, B]) ReaderIOEither[R, E, B] {
|
func MonadChain[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEither[R, E, B]) ReaderIOEither[R, E, B] {
|
||||||
return eithert.MonadChain(
|
return eithert.MonadChain(
|
||||||
readerio.MonadChain[R, either.Either[E, A], either.Either[E, B]],
|
readerio.MonadChain[R, either.Either[E, A], either.Either[E, B]],
|
||||||
@@ -105,6 +115,8 @@ func MonadChain[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEi
|
|||||||
|
|
||||||
// MonadChainFirst sequences two computations but keeps the result of the first.
|
// MonadChainFirst sequences two computations but keeps the result of the first.
|
||||||
// Useful for performing side effects while preserving the original value.
|
// Useful for performing side effects while preserving the original value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainFirst[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEither[R, E, B]) ReaderIOEither[R, E, A] {
|
func MonadChainFirst[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) ReaderIOEither[R, E, B]) ReaderIOEither[R, E, A] {
|
||||||
return chain.MonadChainFirst(
|
return chain.MonadChainFirst(
|
||||||
MonadChain[R, E, A, A],
|
MonadChain[R, E, A, A],
|
||||||
@@ -115,6 +127,8 @@ func MonadChainFirst[R, E, A, B any](fa ReaderIOEither[R, E, A], f func(A) Reade
|
|||||||
|
|
||||||
// MonadChainEitherK chains a computation that returns an Either into a ReaderIOEither.
|
// MonadChainEitherK chains a computation that returns an Either into a ReaderIOEither.
|
||||||
// The Either is automatically lifted into the ReaderIOEither context.
|
// The Either is automatically lifted into the ReaderIOEither context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) either.Either[E, B]) ReaderIOEither[R, E, B] {
|
func MonadChainEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) either.Either[E, B]) ReaderIOEither[R, E, B] {
|
||||||
return fromeither.MonadChainEitherK(
|
return fromeither.MonadChainEitherK(
|
||||||
MonadChain[R, E, A, B],
|
MonadChain[R, E, A, B],
|
||||||
@@ -126,6 +140,8 @@ func MonadChainEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) eit
|
|||||||
|
|
||||||
// ChainEitherK returns a function that chains an Either-returning function into ReaderIOEither.
|
// ChainEitherK returns a function that chains an Either-returning function into ReaderIOEither.
|
||||||
// This is the curried version of MonadChainEitherK.
|
// This is the curried version of MonadChainEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainEitherK[R, E, A, B any](f func(A) either.Either[E, B]) Operator[R, E, A, B] {
|
func ChainEitherK[R, E, A, B any](f func(A) either.Either[E, B]) Operator[R, E, A, B] {
|
||||||
return fromeither.ChainEitherK(
|
return fromeither.ChainEitherK(
|
||||||
Chain[R, E, A, B],
|
Chain[R, E, A, B],
|
||||||
@@ -136,6 +152,8 @@ func ChainEitherK[R, E, A, B any](f func(A) either.Either[E, B]) Operator[R, E,
|
|||||||
|
|
||||||
// MonadChainFirstEitherK chains an Either-returning computation but keeps the original value.
|
// MonadChainFirstEitherK chains an Either-returning computation but keeps the original value.
|
||||||
// Useful for validation or side effects that return Either.
|
// Useful for validation or side effects that return Either.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainFirstEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) either.Either[E, B]) ReaderIOEither[R, E, A] {
|
func MonadChainFirstEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) either.Either[E, B]) ReaderIOEither[R, E, A] {
|
||||||
return fromeither.MonadChainFirstEitherK(
|
return fromeither.MonadChainFirstEitherK(
|
||||||
MonadChain[R, E, A, A],
|
MonadChain[R, E, A, A],
|
||||||
@@ -148,6 +166,8 @@ func MonadChainFirstEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A
|
|||||||
|
|
||||||
// ChainFirstEitherK returns a function that chains an Either computation while preserving the original value.
|
// ChainFirstEitherK returns a function that chains an Either computation while preserving the original value.
|
||||||
// This is the curried version of MonadChainFirstEitherK.
|
// This is the curried version of MonadChainFirstEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainFirstEitherK[R, E, A, B any](f func(A) either.Either[E, B]) Operator[R, E, A, A] {
|
func ChainFirstEitherK[R, E, A, B any](f func(A) either.Either[E, B]) Operator[R, E, A, A] {
|
||||||
return fromeither.ChainFirstEitherK(
|
return fromeither.ChainFirstEitherK(
|
||||||
Chain[R, E, A, A],
|
Chain[R, E, A, A],
|
||||||
@@ -159,6 +179,8 @@ func ChainFirstEitherK[R, E, A, B any](f func(A) either.Either[E, B]) Operator[R
|
|||||||
|
|
||||||
// MonadChainReaderK chains a Reader-returning computation into a ReaderIOEither.
|
// MonadChainReaderK chains a Reader-returning computation into a ReaderIOEither.
|
||||||
// The Reader is automatically lifted into the ReaderIOEither context.
|
// The Reader is automatically lifted into the ReaderIOEither context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainReaderK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) Reader[R, B]) ReaderIOEither[R, E, B] {
|
func MonadChainReaderK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) Reader[R, B]) ReaderIOEither[R, E, B] {
|
||||||
return fromreader.MonadChainReaderK(
|
return fromreader.MonadChainReaderK(
|
||||||
MonadChain[R, E, A, B],
|
MonadChain[R, E, A, B],
|
||||||
@@ -170,6 +192,8 @@ func MonadChainReaderK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) Rea
|
|||||||
|
|
||||||
// ChainReaderK returns a function that chains a Reader-returning function into ReaderIOEither.
|
// ChainReaderK returns a function that chains a Reader-returning function into ReaderIOEither.
|
||||||
// This is the curried version of MonadChainReaderK.
|
// This is the curried version of MonadChainReaderK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainReaderK[E, R, A, B any](f func(A) Reader[R, B]) Operator[R, E, A, B] {
|
func ChainReaderK[E, R, A, B any](f func(A) Reader[R, B]) Operator[R, E, A, B] {
|
||||||
return fromreader.ChainReaderK(
|
return fromreader.ChainReaderK(
|
||||||
MonadChain[R, E, A, B],
|
MonadChain[R, E, A, B],
|
||||||
@@ -180,6 +204,8 @@ func ChainReaderK[E, R, A, B any](f func(A) Reader[R, B]) Operator[R, E, A, B] {
|
|||||||
|
|
||||||
// MonadChainIOEitherK chains an IOEither-returning computation into a ReaderIOEither.
|
// MonadChainIOEitherK chains an IOEither-returning computation into a ReaderIOEither.
|
||||||
// The IOEither is automatically lifted into the ReaderIOEither context.
|
// The IOEither is automatically lifted into the ReaderIOEither context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainIOEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) IOE.IOEither[E, B]) ReaderIOEither[R, E, B] {
|
func MonadChainIOEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) IOE.IOEither[E, B]) ReaderIOEither[R, E, B] {
|
||||||
return fromioeither.MonadChainIOEitherK(
|
return fromioeither.MonadChainIOEitherK(
|
||||||
MonadChain[R, E, A, B],
|
MonadChain[R, E, A, B],
|
||||||
@@ -191,6 +217,8 @@ func MonadChainIOEitherK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) I
|
|||||||
|
|
||||||
// ChainIOEitherK returns a function that chains an IOEither-returning function into ReaderIOEither.
|
// ChainIOEitherK returns a function that chains an IOEither-returning function into ReaderIOEither.
|
||||||
// This is the curried version of MonadChainIOEitherK.
|
// This is the curried version of MonadChainIOEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainIOEitherK[R, E, A, B any](f func(A) IOE.IOEither[E, B]) Operator[R, E, A, B] {
|
func ChainIOEitherK[R, E, A, B any](f func(A) IOE.IOEither[E, B]) Operator[R, E, A, B] {
|
||||||
return fromioeither.ChainIOEitherK(
|
return fromioeither.ChainIOEitherK(
|
||||||
Chain[R, E, A, B],
|
Chain[R, E, A, B],
|
||||||
@@ -201,6 +229,8 @@ func ChainIOEitherK[R, E, A, B any](f func(A) IOE.IOEither[E, B]) Operator[R, E,
|
|||||||
|
|
||||||
// MonadChainIOK chains an IO-returning computation into a ReaderIOEither.
|
// MonadChainIOK chains an IO-returning computation into a ReaderIOEither.
|
||||||
// The IO is automatically lifted into the ReaderIOEither context (always succeeds).
|
// The IO is automatically lifted into the ReaderIOEither context (always succeeds).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B]) ReaderIOEither[R, E, B] {
|
func MonadChainIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B]) ReaderIOEither[R, E, B] {
|
||||||
return fromio.MonadChainIOK(
|
return fromio.MonadChainIOK(
|
||||||
MonadChain[R, E, A, B],
|
MonadChain[R, E, A, B],
|
||||||
@@ -212,6 +242,8 @@ func MonadChainIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B
|
|||||||
|
|
||||||
// ChainIOK returns a function that chains an IO-returning function into ReaderIOEither.
|
// ChainIOK returns a function that chains an IO-returning function into ReaderIOEither.
|
||||||
// This is the curried version of MonadChainIOK.
|
// This is the curried version of MonadChainIOK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainIOK[R, E, A, B any](f func(A) io.IO[B]) Operator[R, E, A, B] {
|
func ChainIOK[R, E, A, B any](f func(A) io.IO[B]) Operator[R, E, A, B] {
|
||||||
return fromio.ChainIOK(
|
return fromio.ChainIOK(
|
||||||
Chain[R, E, A, B],
|
Chain[R, E, A, B],
|
||||||
@@ -222,6 +254,8 @@ func ChainIOK[R, E, A, B any](f func(A) io.IO[B]) Operator[R, E, A, B] {
|
|||||||
|
|
||||||
// MonadChainFirstIOK chains an IO computation but keeps the original value.
|
// MonadChainFirstIOK chains an IO computation but keeps the original value.
|
||||||
// Useful for performing IO side effects while preserving the original value.
|
// Useful for performing IO side effects while preserving the original value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadChainFirstIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B]) ReaderIOEither[R, E, A] {
|
func MonadChainFirstIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io.IO[B]) ReaderIOEither[R, E, A] {
|
||||||
return fromio.MonadChainFirstIOK(
|
return fromio.MonadChainFirstIOK(
|
||||||
MonadChain[R, E, A, A],
|
MonadChain[R, E, A, A],
|
||||||
@@ -234,6 +268,8 @@ func MonadChainFirstIOK[R, E, A, B any](ma ReaderIOEither[R, E, A], f func(A) io
|
|||||||
|
|
||||||
// ChainFirstIOK returns a function that chains an IO computation while preserving the original value.
|
// ChainFirstIOK returns a function that chains an IO computation while preserving the original value.
|
||||||
// This is the curried version of MonadChainFirstIOK.
|
// This is the curried version of MonadChainFirstIOK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainFirstIOK[R, E, A, B any](f func(A) io.IO[B]) Operator[R, E, A, A] {
|
func ChainFirstIOK[R, E, A, B any](f func(A) io.IO[B]) Operator[R, E, A, A] {
|
||||||
return fromio.ChainFirstIOK(
|
return fromio.ChainFirstIOK(
|
||||||
Chain[R, E, A, A],
|
Chain[R, E, A, A],
|
||||||
@@ -245,6 +281,8 @@ func ChainFirstIOK[R, E, A, B any](f func(A) io.IO[B]) Operator[R, E, A, A] {
|
|||||||
|
|
||||||
// ChainOptionK returns a function that chains an Option-returning function into ReaderIOEither.
|
// ChainOptionK returns a function that chains an Option-returning function into ReaderIOEither.
|
||||||
// If the Option is None, the provided error function is called to produce the error value.
|
// If the Option is None, the provided error function is called to produce the error value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainOptionK[R, A, B, E any](onNone func() E) func(func(A) O.Option[B]) Operator[R, E, A, B] {
|
func ChainOptionK[R, A, B, E any](onNone func() E) func(func(A) O.Option[B]) Operator[R, E, A, B] {
|
||||||
return fromeither.ChainOptionK(
|
return fromeither.ChainOptionK(
|
||||||
MonadChain[R, E, A, B],
|
MonadChain[R, E, A, B],
|
||||||
@@ -255,6 +293,8 @@ func ChainOptionK[R, A, B, E any](onNone func() E) func(func(A) O.Option[B]) Ope
|
|||||||
|
|
||||||
// MonadAp applies a function wrapped in a context to a value wrapped in a context.
|
// MonadAp applies a function wrapped in a context to a value wrapped in a context.
|
||||||
// Both computations are executed (default behavior may be sequential or parallel depending on implementation).
|
// Both computations are executed (default behavior may be sequential or parallel depending on implementation).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadAp[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
|
func MonadAp[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
|
||||||
return eithert.MonadAp(
|
return eithert.MonadAp(
|
||||||
readerio.MonadAp[Either[E, B], R, Either[E, A]],
|
readerio.MonadAp[Either[E, B], R, Either[E, A]],
|
||||||
@@ -265,6 +305,8 @@ func MonadAp[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MonadApSeq applies a function in a context to a value in a context, executing them sequentially.
|
// MonadApSeq applies a function in a context to a value in a context, executing them sequentially.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadApSeq[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
|
func MonadApSeq[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
|
||||||
return eithert.MonadAp(
|
return eithert.MonadAp(
|
||||||
readerio.MonadApSeq[Either[E, B], R, Either[E, A]],
|
readerio.MonadApSeq[Either[E, B], R, Either[E, A]],
|
||||||
@@ -275,6 +317,8 @@ func MonadApSeq[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIO
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MonadApPar applies a function in a context to a value in a context, executing them in parallel.
|
// MonadApPar applies a function in a context to a value in a context, executing them in parallel.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadApPar[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
|
func MonadApPar[R, E, A, B any](fab ReaderIOEither[R, E, func(A) B], fa ReaderIOEither[R, E, A]) ReaderIOEither[R, E, B] {
|
||||||
return eithert.MonadAp(
|
return eithert.MonadAp(
|
||||||
readerio.MonadApPar[Either[E, B], R, Either[E, A]],
|
readerio.MonadApPar[Either[E, B], R, Either[E, A]],
|
||||||
@@ -292,6 +336,8 @@ func Ap[B, R, E, A any](fa ReaderIOEither[R, E, A]) func(fab ReaderIOEither[R, E
|
|||||||
|
|
||||||
// Chain returns a function that sequences computations where the second depends on the first.
|
// Chain returns a function that sequences computations where the second depends on the first.
|
||||||
// This is the curried version of MonadChain.
|
// This is the curried version of MonadChain.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Chain[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) Operator[R, E, A, B] {
|
func Chain[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) Operator[R, E, A, B] {
|
||||||
return eithert.Chain(
|
return eithert.Chain(
|
||||||
readerio.Chain[R, either.Either[E, A], either.Either[E, B]],
|
readerio.Chain[R, either.Either[E, A], either.Either[E, B]],
|
||||||
@@ -301,6 +347,8 @@ func Chain[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) Operator[R, E, A,
|
|||||||
|
|
||||||
// ChainFirst returns a function that sequences computations but keeps the first result.
|
// ChainFirst returns a function that sequences computations but keeps the first result.
|
||||||
// This is the curried version of MonadChainFirst.
|
// This is the curried version of MonadChainFirst.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ChainFirst[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) Operator[R, E, A, A] {
|
func ChainFirst[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) Operator[R, E, A, A] {
|
||||||
return chain.ChainFirst(
|
return chain.ChainFirst(
|
||||||
Chain[R, E, A, A],
|
Chain[R, E, A, A],
|
||||||
@@ -309,11 +357,15 @@ func ChainFirst[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) Operator[R, E
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Right creates a successful ReaderIOEither with the given value.
|
// Right creates a successful ReaderIOEither with the given value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Right[R, E, A any](a A) ReaderIOEither[R, E, A] {
|
func Right[R, E, A any](a A) ReaderIOEither[R, E, A] {
|
||||||
return eithert.Right(readerio.Of[R, Either[E, A]], a)
|
return eithert.Right(readerio.Of[R, Either[E, A]], a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left creates a failed ReaderIOEither with the given error.
|
// Left creates a failed ReaderIOEither with the given error.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Left[R, A, E any](e E) ReaderIOEither[R, E, A] {
|
func Left[R, A, E any](e E) ReaderIOEither[R, E, A] {
|
||||||
return eithert.Left(readerio.Of[R, Either[E, A]], e)
|
return eithert.Left(readerio.Of[R, Either[E, A]], e)
|
||||||
}
|
}
|
||||||
@@ -338,6 +390,8 @@ func Flatten[R, E, A any](mma ReaderIOEither[R, E, ReaderIOEither[R, E, A]]) Rea
|
|||||||
|
|
||||||
// FromEither lifts an Either into a ReaderIOEither context.
|
// FromEither lifts an Either into a ReaderIOEither context.
|
||||||
// The Either value is independent of any context or IO effects.
|
// The Either value is independent of any context or IO effects.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromEither[R, E, A any](t either.Either[E, A]) ReaderIOEither[R, E, A] {
|
func FromEither[R, E, A any](t either.Either[E, A]) ReaderIOEither[R, E, A] {
|
||||||
return readerio.Of[R](t)
|
return readerio.Of[R](t)
|
||||||
}
|
}
|
||||||
@@ -376,6 +430,8 @@ func FromIO[R, E, A any](ma io.IO[A]) ReaderIOEither[R, E, A] {
|
|||||||
|
|
||||||
// FromIOEither lifts an IOEither into a ReaderIOEither context.
|
// FromIOEither lifts an IOEither into a ReaderIOEither context.
|
||||||
// The computation becomes independent of any reader context.
|
// The computation becomes independent of any reader context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromIOEither[R, E, A any](ma IOE.IOEither[E, A]) ReaderIOEither[R, E, A] {
|
func FromIOEither[R, E, A any](ma IOE.IOEither[E, A]) ReaderIOEither[R, E, A] {
|
||||||
return reader.Of[R](ma)
|
return reader.Of[R](ma)
|
||||||
}
|
}
|
||||||
@@ -388,48 +444,64 @@ func FromReaderEither[R, E, A any](ma RE.ReaderEither[R, E, A]) ReaderIOEither[R
|
|||||||
|
|
||||||
// Ask returns a ReaderIOEither that retrieves the current context.
|
// Ask returns a ReaderIOEither that retrieves the current context.
|
||||||
// Useful for accessing configuration or dependencies.
|
// Useful for accessing configuration or dependencies.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Ask[R, E any]() ReaderIOEither[R, E, R] {
|
func Ask[R, E any]() ReaderIOEither[R, E, R] {
|
||||||
return fromreader.Ask(FromReader[E, R, R])()
|
return fromreader.Ask(FromReader[E, R, R])()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asks returns a ReaderIOEither that retrieves a value derived from the context.
|
// Asks returns a ReaderIOEither that retrieves a value derived from the context.
|
||||||
// This is useful for extracting specific fields from a configuration object.
|
// This is useful for extracting specific fields from a configuration object.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Asks[E, R, A any](r Reader[R, A]) ReaderIOEither[R, E, A] {
|
func Asks[E, R, A any](r Reader[R, A]) ReaderIOEither[R, E, A] {
|
||||||
return fromreader.Asks(FromReader[E, R, A])(r)
|
return fromreader.Asks(FromReader[E, R, A])(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromOption converts an Option to a ReaderIOEither.
|
// FromOption converts an Option to a ReaderIOEither.
|
||||||
// If the Option is None, the provided function is called to produce the error.
|
// If the Option is None, the provided function is called to produce the error.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromOption[R, A, E any](onNone func() E) func(O.Option[A]) ReaderIOEither[R, E, A] {
|
func FromOption[R, A, E any](onNone func() E) func(O.Option[A]) ReaderIOEither[R, E, A] {
|
||||||
return fromeither.FromOption(FromEither[R, E, A], onNone)
|
return fromeither.FromOption(FromEither[R, E, A], onNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromPredicate creates a ReaderIOEither from a predicate.
|
// FromPredicate creates a ReaderIOEither from a predicate.
|
||||||
// If the predicate returns false, the onFalse function is called to produce the error.
|
// If the predicate returns false, the onFalse function is called to produce the error.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FromPredicate[R, E, A any](pred func(A) bool, onFalse func(A) E) func(A) ReaderIOEither[R, E, A] {
|
func FromPredicate[R, E, A any](pred func(A) bool, onFalse func(A) E) func(A) ReaderIOEither[R, E, A] {
|
||||||
return fromeither.FromPredicate(FromEither[R, E, A], pred, onFalse)
|
return fromeither.FromPredicate(FromEither[R, E, A], pred, onFalse)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fold handles both success and error cases, producing a ReaderIO.
|
// Fold handles both success and error cases, producing a ReaderIO.
|
||||||
// This is useful for converting a ReaderIOEither into a ReaderIO by handling all cases.
|
// This is useful for converting a ReaderIOEither into a ReaderIO by handling all cases.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Fold[R, E, A, B any](onLeft func(E) ReaderIO[R, B], onRight func(A) ReaderIO[R, B]) func(ReaderIOEither[R, E, A]) ReaderIO[R, B] {
|
func Fold[R, E, A, B any](onLeft func(E) ReaderIO[R, B], onRight func(A) ReaderIO[R, B]) func(ReaderIOEither[R, E, A]) ReaderIO[R, B] {
|
||||||
return eithert.MatchE(readerio.MonadChain[R, either.Either[E, A], B], onLeft, onRight)
|
return eithert.MatchE(readerio.MonadChain[R, either.Either[E, A], B], onLeft, onRight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrElse provides a default value in case of error.
|
// GetOrElse provides a default value in case of error.
|
||||||
// The default is computed lazily via a ReaderIO.
|
// The default is computed lazily via a ReaderIO.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func GetOrElse[R, E, A any](onLeft func(E) ReaderIO[R, A]) func(ReaderIOEither[R, E, A]) ReaderIO[R, A] {
|
func GetOrElse[R, E, A any](onLeft func(E) ReaderIO[R, A]) func(ReaderIOEither[R, E, A]) ReaderIO[R, A] {
|
||||||
return eithert.GetOrElse(readerio.MonadChain[R, either.Either[E, A], A], readerio.Of[R, A], onLeft)
|
return eithert.GetOrElse(readerio.MonadChain[R, either.Either[E, A], A], readerio.Of[R, A], onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrElse tries an alternative computation if the first one fails.
|
// OrElse tries an alternative computation if the first one fails.
|
||||||
// The alternative can produce a different error type.
|
// The alternative can produce a different error type.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func OrElse[R, E1, A, E2 any](onLeft func(E1) ReaderIOEither[R, E2, A]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
|
func OrElse[R, E1, A, E2 any](onLeft func(E1) ReaderIOEither[R, E2, A]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
|
||||||
return eithert.OrElse(readerio.MonadChain[R, either.Either[E1, A], either.Either[E2, A]], readerio.Of[R, either.Either[E2, A]], onLeft)
|
return eithert.OrElse(readerio.MonadChain[R, either.Either[E1, A], either.Either[E2, A]], readerio.Of[R, either.Either[E2, A]], onLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrLeft transforms the error using a ReaderIO if the computation fails.
|
// OrLeft transforms the error using a ReaderIO if the computation fails.
|
||||||
// The success value is preserved unchanged.
|
// The success value is preserved unchanged.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func OrLeft[A, E1, R, E2 any](onLeft func(E1) ReaderIO[R, E2]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
|
func OrLeft[A, E1, R, E2 any](onLeft func(E1) ReaderIO[R, E2]) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
|
||||||
return eithert.OrLeft(
|
return eithert.OrLeft(
|
||||||
readerio.MonadChain[R, either.Either[E1, A], either.Either[E2, A]],
|
readerio.MonadChain[R, either.Either[E1, A], either.Either[E2, A]],
|
||||||
@@ -441,6 +513,8 @@ func OrLeft[A, E1, R, E2 any](onLeft func(E1) ReaderIO[R, E2]) func(ReaderIOEith
|
|||||||
|
|
||||||
// MonadBiMap applies two functions: one to the error, one to the success value.
|
// MonadBiMap applies two functions: one to the error, one to the success value.
|
||||||
// This allows transforming both channels simultaneously.
|
// This allows transforming both channels simultaneously.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadBiMap[R, E1, E2, A, B any](fa ReaderIOEither[R, E1, A], f func(E1) E2, g func(A) B) ReaderIOEither[R, E2, B] {
|
func MonadBiMap[R, E1, E2, A, B any](fa ReaderIOEither[R, E1, A], f func(E1) E2, g func(A) B) ReaderIOEither[R, E2, B] {
|
||||||
return eithert.MonadBiMap(
|
return eithert.MonadBiMap(
|
||||||
readerio.MonadMap[R, either.Either[E1, A], either.Either[E2, B]],
|
readerio.MonadMap[R, either.Either[E1, A], either.Either[E2, B]],
|
||||||
@@ -450,18 +524,24 @@ func MonadBiMap[R, E1, E2, A, B any](fa ReaderIOEither[R, E1, A], f func(E1) E2,
|
|||||||
|
|
||||||
// BiMap returns a function that maps over both the error and success channels.
|
// BiMap returns a function that maps over both the error and success channels.
|
||||||
// This is the curried version of MonadBiMap.
|
// This is the curried version of MonadBiMap.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func BiMap[R, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, B] {
|
func BiMap[R, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, B] {
|
||||||
return eithert.BiMap(readerio.Map[R, either.Either[E1, A], either.Either[E2, B]], f, g)
|
return eithert.BiMap(readerio.Map[R, either.Either[E1, A], either.Either[E2, B]], f, g)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap exchanges the error and success types.
|
// Swap exchanges the error and success types.
|
||||||
// Left becomes Right and Right becomes Left.
|
// Left becomes Right and Right becomes Left.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Swap[R, E, A any](val ReaderIOEither[R, E, A]) ReaderIOEither[R, A, E] {
|
func Swap[R, E, A any](val ReaderIOEither[R, E, A]) ReaderIOEither[R, A, E] {
|
||||||
return reader.MonadMap(val, ioeither.Swap[E, A])
|
return reader.MonadMap(val, ioeither.Swap[E, A])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer creates a ReaderIOEither lazily via a generator function.
|
// Defer creates a ReaderIOEither lazily via a generator function.
|
||||||
// The generator is called each time the ReaderIOEither is executed.
|
// The generator is called each time the ReaderIOEither is executed.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Defer[R, E, A any](gen L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A] {
|
func Defer[R, E, A any](gen L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A] {
|
||||||
return readerio.Defer(gen)
|
return readerio.Defer(gen)
|
||||||
}
|
}
|
||||||
@@ -476,6 +556,8 @@ func TryCatch[R, E, A any](f func(R) func() (A, error), onThrow func(error) E) R
|
|||||||
|
|
||||||
// MonadAlt tries the first computation, and if it fails, tries the second.
|
// MonadAlt tries the first computation, and if it fails, tries the second.
|
||||||
// This implements the Alternative pattern for error recovery.
|
// This implements the Alternative pattern for error recovery.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadAlt[R, E, A any](first ReaderIOEither[R, E, A], second L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A] {
|
func MonadAlt[R, E, A any](first ReaderIOEither[R, E, A], second L.Lazy[ReaderIOEither[R, E, A]]) ReaderIOEither[R, E, A] {
|
||||||
return eithert.MonadAlt(
|
return eithert.MonadAlt(
|
||||||
readerio.Of[R, Either[E, A]],
|
readerio.Of[R, Either[E, A]],
|
||||||
@@ -488,6 +570,8 @@ func MonadAlt[R, E, A any](first ReaderIOEither[R, E, A], second L.Lazy[ReaderIO
|
|||||||
|
|
||||||
// Alt returns a function that tries an alternative computation if the first fails.
|
// Alt returns a function that tries an alternative computation if the first fails.
|
||||||
// This is the curried version of MonadAlt.
|
// This is the curried version of MonadAlt.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) Operator[R, E, A, A] {
|
func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) Operator[R, E, A, A] {
|
||||||
return eithert.Alt(
|
return eithert.Alt(
|
||||||
readerio.Of[R, Either[E, A]],
|
readerio.Of[R, Either[E, A]],
|
||||||
@@ -499,6 +583,8 @@ func Alt[R, E, A any](second L.Lazy[ReaderIOEither[R, E, A]]) Operator[R, E, A,
|
|||||||
|
|
||||||
// Memoize computes the value of the ReaderIOEither lazily but exactly once.
|
// Memoize computes the value of the ReaderIOEither lazily but exactly once.
|
||||||
// The context used is from the first call. Do not use if the value depends on the context.
|
// The context used is from the first call. Do not use if the value depends on the context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Memoize[
|
func Memoize[
|
||||||
R, E, A any](rdr ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] {
|
R, E, A any](rdr ReaderIOEither[R, E, A]) ReaderIOEither[R, E, A] {
|
||||||
return readerio.Memoize(rdr)
|
return readerio.Memoize(rdr)
|
||||||
@@ -506,23 +592,31 @@ func Memoize[
|
|||||||
|
|
||||||
// MonadFlap applies a value to a function wrapped in a context.
|
// MonadFlap applies a value to a function wrapped in a context.
|
||||||
// This is the reverse of Ap - the value is fixed and the function varies.
|
// This is the reverse of Ap - the value is fixed and the function varies.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadFlap[R, E, B, A any](fab ReaderIOEither[R, E, func(A) B], a A) ReaderIOEither[R, E, B] {
|
func MonadFlap[R, E, B, A any](fab ReaderIOEither[R, E, func(A) B], a A) ReaderIOEither[R, E, B] {
|
||||||
return functor.MonadFlap(MonadMap[R, E, func(A) B, B], fab, a)
|
return functor.MonadFlap(MonadMap[R, E, func(A) B, B], fab, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flap returns a function that applies a fixed value to a function in a context.
|
// Flap returns a function that applies a fixed value to a function in a context.
|
||||||
// This is the curried version of MonadFlap.
|
// This is the curried version of MonadFlap.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Flap[R, E, B, A any](a A) func(ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B] {
|
func Flap[R, E, B, A any](a A) func(ReaderIOEither[R, E, func(A) B]) ReaderIOEither[R, E, B] {
|
||||||
return functor.Flap(Map[R, E, func(A) B, B], a)
|
return functor.Flap(Map[R, E, func(A) B, B], a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadMapLeft applies a function to the error value, leaving success unchanged.
|
// MonadMapLeft applies a function to the error value, leaving success unchanged.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadMapLeft[R, E1, E2, A any](fa ReaderIOEither[R, E1, A], f func(E1) E2) ReaderIOEither[R, E2, A] {
|
func MonadMapLeft[R, E1, E2, A any](fa ReaderIOEither[R, E1, A], f func(E1) E2) ReaderIOEither[R, E2, A] {
|
||||||
return eithert.MonadMapLeft(readerio.MonadMap[R, Either[E1, A], Either[E2, A]], fa, f)
|
return eithert.MonadMapLeft(readerio.MonadMap[R, Either[E1, A], Either[E2, A]], fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapLeft returns a function that transforms the error channel.
|
// MapLeft returns a function that transforms the error channel.
|
||||||
// This is the curried version of MonadMapLeft.
|
// This is the curried version of MonadMapLeft.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
|
func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) ReaderIOEither[R, E2, A] {
|
||||||
return eithert.MapLeft(readerio.Map[R, Either[E1, A], Either[E2, A]], f)
|
return eithert.MapLeft(readerio.Map[R, Either[E1, A], Either[E2, A]], f)
|
||||||
}
|
}
|
||||||
@@ -530,6 +624,8 @@ func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) Rea
|
|||||||
// Local runs a computation with a modified context.
|
// Local runs a computation with a modified context.
|
||||||
// The function f transforms the context before passing it to the computation.
|
// The function f transforms the context before passing it to the computation.
|
||||||
// This is similar to Contravariant's contramap operation.
|
// This is similar to Contravariant's contramap operation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Local[R1, R2, E, A any](f func(R2) R1) func(ReaderIOEither[R1, E, A]) ReaderIOEither[R2, E, A] {
|
func Local[R1, R2, E, A any](f func(R2) R1) func(ReaderIOEither[R1, E, A]) ReaderIOEither[R2, E, A] {
|
||||||
return reader.Local[R2, R1, IOEither[E, A]](f)
|
return reader.Local[R2, R1, IOEither[E, A]](f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import (
|
|||||||
//
|
//
|
||||||
// result := SequenceT1(Of[Config, error](42))
|
// result := SequenceT1(Of[Config, error](42))
|
||||||
// // result(cfg)() returns Right(Tuple1{42})
|
// // result(cfg)() returns Right(Tuple1{42})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func SequenceT1[R, E, A any](a ReaderIOEither[R, E, A]) ReaderIOEither[R, E, T.Tuple1[A]] {
|
func SequenceT1[R, E, A any](a ReaderIOEither[R, E, A]) ReaderIOEither[R, E, T.Tuple1[A]] {
|
||||||
return G.SequenceT1[
|
return G.SequenceT1[
|
||||||
ReaderIOEither[R, E, A],
|
ReaderIOEither[R, E, A],
|
||||||
@@ -50,6 +52,8 @@ func SequenceT1[R, E, A any](a ReaderIOEither[R, E, A]) ReaderIOEither[R, E, T.T
|
|||||||
// fetchProfile(123),
|
// fetchProfile(123),
|
||||||
// )
|
// )
|
||||||
// // result(cfg)() returns Right(Tuple2{user, profile}) or Left(error)
|
// // result(cfg)() returns Right(Tuple2{user, profile}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func SequenceT2[R, E, A, B any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B]) ReaderIOEither[R, E, T.Tuple2[A, B]] {
|
func SequenceT2[R, E, A, B any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B]) ReaderIOEither[R, E, T.Tuple2[A, B]] {
|
||||||
return G.SequenceT2[
|
return G.SequenceT2[
|
||||||
ReaderIOEither[R, E, A],
|
ReaderIOEither[R, E, A],
|
||||||
@@ -70,6 +74,8 @@ func SequenceT2[R, E, A, B any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E
|
|||||||
// fetchSettings(123),
|
// fetchSettings(123),
|
||||||
// )
|
// )
|
||||||
// // result(cfg)() returns Right(Tuple3{user, profile, settings}) or Left(error)
|
// // result(cfg)() returns Right(Tuple3{user, profile, settings}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func SequenceT3[R, E, A, B, C any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C]) ReaderIOEither[R, E, T.Tuple3[A, B, C]] {
|
func SequenceT3[R, E, A, B, C any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C]) ReaderIOEither[R, E, T.Tuple3[A, B, C]] {
|
||||||
return G.SequenceT3[
|
return G.SequenceT3[
|
||||||
ReaderIOEither[R, E, A],
|
ReaderIOEither[R, E, A],
|
||||||
@@ -92,6 +98,8 @@ func SequenceT3[R, E, A, B, C any](a ReaderIOEither[R, E, A], b ReaderIOEither[R
|
|||||||
// fetchPreferences(123),
|
// fetchPreferences(123),
|
||||||
// )
|
// )
|
||||||
// // result(cfg)() returns Right(Tuple4{user, profile, settings, prefs}) or Left(error)
|
// // result(cfg)() returns Right(Tuple4{user, profile, settings, prefs}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func SequenceT4[R, E, A, B, C, D any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C], d ReaderIOEither[R, E, D]) ReaderIOEither[R, E, T.Tuple4[A, B, C, D]] {
|
func SequenceT4[R, E, A, B, C, D any](a ReaderIOEither[R, E, A], b ReaderIOEither[R, E, B], c ReaderIOEither[R, E, C], d ReaderIOEither[R, E, D]) ReaderIOEither[R, E, T.Tuple4[A, B, C, D]] {
|
||||||
return G.SequenceT4[
|
return G.SequenceT4[
|
||||||
ReaderIOEither[R, E, A],
|
ReaderIOEither[R, E, A],
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ import (
|
|||||||
// return func() { mu.Unlock() }
|
// return func() { mu.Unlock() }
|
||||||
// }),
|
// }),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func WithLock[R, E, A any](lock func() context.CancelFunc) Operator[R, E, A, A] {
|
func WithLock[R, E, A any](lock func() context.CancelFunc) Operator[R, E, A, A] {
|
||||||
return readerio.WithLock[R, either.Either[E, A]](lock)
|
return readerio.WithLock[R, either.Either[E, A]](lock)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ import (
|
|||||||
// })
|
// })
|
||||||
// result := fetchUsers([]int{1, 2, 3})
|
// result := fetchUsers([]int{1, 2, 3})
|
||||||
// // result(cfg)() returns Right([user1, user2, user3]) or Left(error)
|
// // result(cfg)() returns Right([user1, user2, user3]) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseArray[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B] {
|
func TraverseArray[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B] {
|
||||||
return G.TraverseArray[ReaderIOEither[R, E, B], ReaderIOEither[R, E, []B], IOEither[E, B], IOEither[E, []B], []A](f)
|
return G.TraverseArray[ReaderIOEither[R, E, B], ReaderIOEither[R, E, []B], IOEither[E, B], IOEither[E, []B], []A](f)
|
||||||
}
|
}
|
||||||
@@ -71,6 +73,8 @@ func TraverseArray[R, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func([]A)
|
|||||||
// processWithIndex := TraverseArrayWithIndex(func(i int, val string) ReaderIOEither[Config, error, string] {
|
// processWithIndex := TraverseArrayWithIndex(func(i int, val string) ReaderIOEither[Config, error, string] {
|
||||||
// return Of[Config, error](fmt.Sprintf("%d: %s", i, val))
|
// return Of[Config, error](fmt.Sprintf("%d: %s", i, val))
|
||||||
// })
|
// })
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseArrayWithIndex[R, E, A, B any](f func(int, A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B] {
|
func TraverseArrayWithIndex[R, E, A, B any](f func(int, A) ReaderIOEither[R, E, B]) func([]A) ReaderIOEither[R, E, []B] {
|
||||||
return G.TraverseArrayWithIndex[ReaderIOEither[R, E, B], ReaderIOEither[R, E, []B], IOEither[E, B], IOEither[E, []B], []A](f)
|
return G.TraverseArrayWithIndex[ReaderIOEither[R, E, B], ReaderIOEither[R, E, []B], IOEither[E, B], IOEither[E, []B], []A](f)
|
||||||
}
|
}
|
||||||
@@ -101,6 +105,8 @@ func TraverseArrayWithIndex[R, E, A, B any](f func(int, A) ReaderIOEither[R, E,
|
|||||||
// }
|
// }
|
||||||
// result := SequenceArray(computations)
|
// result := SequenceArray(computations)
|
||||||
// // result(cfg)() returns Right([userCount, postCount, commentCount]) or Left(error)
|
// // result(cfg)() returns Right([userCount, postCount, commentCount]) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func SequenceArray[R, E, A any](ma []ReaderIOEither[R, E, A]) ReaderIOEither[R, E, []A] {
|
func SequenceArray[R, E, A any](ma []ReaderIOEither[R, E, A]) ReaderIOEither[R, E, []A] {
|
||||||
return G.SequenceArray[ReaderIOEither[R, E, A], ReaderIOEither[R, E, []A]](ma)
|
return G.SequenceArray[ReaderIOEither[R, E, A], ReaderIOEither[R, E, []A]](ma)
|
||||||
}
|
}
|
||||||
@@ -131,6 +137,8 @@ func SequenceArray[R, E, A any](ma []ReaderIOEither[R, E, A]) ReaderIOEither[R,
|
|||||||
// return enrichUser(user)
|
// return enrichUser(user)
|
||||||
// })
|
// })
|
||||||
// result := enrichUsers(map[string]User{"alice": user1, "bob": user2})
|
// result := enrichUsers(map[string]User{"alice": user1, "bob": user2})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseRecord[R any, K comparable, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
func TraverseRecord[R any, K comparable, E, A, B any](f func(A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
||||||
return G.TraverseRecord[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
return G.TraverseRecord[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||||
}
|
}
|
||||||
@@ -158,6 +166,8 @@ func TraverseRecord[R any, K comparable, E, A, B any](f func(A) ReaderIOEither[R
|
|||||||
// processWithKey := TraverseRecordWithIndex(func(key string, val int) ReaderIOEither[Config, error, string] {
|
// processWithKey := TraverseRecordWithIndex(func(key string, val int) ReaderIOEither[Config, error, string] {
|
||||||
// return Of[Config, error](fmt.Sprintf("%s: %d", key, val))
|
// return Of[Config, error](fmt.Sprintf("%s: %d", key, val))
|
||||||
// })
|
// })
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseRecordWithIndex[R any, K comparable, E, A, B any](f func(K, A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
func TraverseRecordWithIndex[R any, K comparable, E, A, B any](f func(K, A) ReaderIOEither[R, E, B]) func(map[K]A) ReaderIOEither[R, E, map[K]B] {
|
||||||
return G.TraverseRecordWithIndex[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
return G.TraverseRecordWithIndex[ReaderIOEither[R, E, B], ReaderIOEither[R, E, map[K]B], IOEither[E, B], IOEither[E, map[K]B], map[K]A](f)
|
||||||
}
|
}
|
||||||
@@ -189,6 +199,8 @@ func TraverseRecordWithIndex[R any, K comparable, E, A, B any](f func(K, A) Read
|
|||||||
// }
|
// }
|
||||||
// result := SequenceRecord(computations)
|
// result := SequenceRecord(computations)
|
||||||
// // result(cfg)() returns Right(map[string]int{"users": 100, "posts": 50}) or Left(error)
|
// // result(cfg)() returns Right(map[string]int{"users": 100, "posts": 50}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func SequenceRecord[R any, K comparable, E, A any](ma map[K]ReaderIOEither[R, E, A]) ReaderIOEither[R, E, map[K]A] {
|
func SequenceRecord[R any, K comparable, E, A any](ma map[K]ReaderIOEither[R, E, A]) ReaderIOEither[R, E, map[K]A] {
|
||||||
return G.SequenceRecord[ReaderIOEither[R, E, A], ReaderIOEither[R, E, map[K]A]](ma)
|
return G.SequenceRecord[ReaderIOEither[R, E, A], ReaderIOEither[R, E, map[K]A]](ma)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user