From 1af6501cd80b8c5a38c9c1024ea53283aa596e08 Mon Sep 17 00:00:00 2001 From: "Dr. Carsten Leue" Date: Tue, 11 Nov 2025 12:42:14 +0100 Subject: [PATCH] fix: add bind variations Signed-off-by: Dr. Carsten Leue --- v2/context/readerioresult/bind.go | 350 +++++++++++++++ v2/context/readerioresult/reader.go | 15 + v2/readerio/types.go | 3 +- v2/readerioeither/bind.go | 270 +++++++++++- v2/readerioeither/reader.go | 18 +- v2/readerioeither/readerioeither_test.go | 25 +- v2/readerioeither/types.go | 3 + v2/readerioresult/bind.go | 521 +++++++++++++++++++++++ v2/readerioresult/bind_test.go | 2 + v2/readerioresult/reader.go | 12 +- v2/readerioresult/readerioeither_test.go | 23 - 11 files changed, 1165 insertions(+), 77 deletions(-) diff --git a/v2/context/readerioresult/bind.go b/v2/context/readerioresult/bind.go index 10ab086..cb51cdb 100644 --- a/v2/context/readerioresult/bind.go +++ b/v2/context/readerioresult/bind.go @@ -18,9 +18,16 @@ package readerioresult import ( "context" + F "github.com/IBM/fp-go/v2/function" "github.com/IBM/fp-go/v2/internal/apply" + "github.com/IBM/fp-go/v2/io" + "github.com/IBM/fp-go/v2/ioeither" + "github.com/IBM/fp-go/v2/ioresult" L "github.com/IBM/fp-go/v2/optics/lens" + "github.com/IBM/fp-go/v2/reader" + "github.com/IBM/fp-go/v2/readerio" RIOR "github.com/IBM/fp-go/v2/readerioresult" + "github.com/IBM/fp-go/v2/result" ) // Do creates an empty context of type [S] to be used with the [Bind] operation. @@ -320,3 +327,346 @@ func LetToL[S, T any]( ) Operator[S, S] { return RIOR.LetToL[context.Context](lens, b) } + +// BindIOEitherK is a variant of Bind that works with IOEither computations. +// It lifts an IOEither Kleisli arrow into the ReaderIOResult context (with context.Context as environment). +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An IOEither Kleisli arrow (S1 -> IOEither[error, T]) +func BindIOEitherK[S1, S2, T any]( + setter func(T) func(S1) S2, + f ioresult.Kleisli[S1, T], +) Operator[S1, S2] { + return Bind(setter, F.Flow2(f, FromIOEither[T])) +} + +// BindIOResultK is a variant of Bind that works with IOResult computations. +// This is an alias for BindIOEitherK for consistency with the Result naming convention. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An IOResult Kleisli arrow (S1 -> IOResult[T]) +func BindIOResultK[S1, S2, T any]( + setter func(T) func(S1) S2, + f ioresult.Kleisli[S1, T], +) Operator[S1, S2] { + return Bind(setter, F.Flow2(f, FromIOResult[T])) +} + +// BindIOK is a variant of Bind that works with IO computations. +// It lifts an IO Kleisli arrow into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An IO Kleisli arrow (S1 -> IO[T]) +func BindIOK[S1, S2, T any]( + setter func(T) func(S1) S2, + f io.Kleisli[S1, T], +) Operator[S1, S2] { + return Bind(setter, F.Flow2(f, FromIO[T])) +} + +// BindReaderK is a variant of Bind that works with Reader computations. +// It lifts a Reader Kleisli arrow (with context.Context) into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: A Reader Kleisli arrow (S1 -> Reader[context.Context, T]) +func BindReaderK[S1, S2, T any]( + setter func(T) func(S1) S2, + f reader.Kleisli[context.Context, S1, T], +) Operator[S1, S2] { + return Bind(setter, F.Flow2(f, FromReader[T])) +} + +// BindReaderIOK is a variant of Bind that works with ReaderIO computations. +// It lifts a ReaderIO Kleisli arrow (with context.Context) into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: A ReaderIO Kleisli arrow (S1 -> ReaderIO[context.Context, T]) +func BindReaderIOK[S1, S2, T any]( + setter func(T) func(S1) S2, + f readerio.Kleisli[context.Context, S1, T], +) Operator[S1, S2] { + return Bind(setter, F.Flow2(f, FromReaderIO[T])) +} + +// BindEitherK is a variant of Bind that works with Either (Result) computations. +// It lifts an Either Kleisli arrow into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An Either Kleisli arrow (S1 -> Either[error, T]) +func BindEitherK[S1, S2, T any]( + setter func(T) func(S1) S2, + f result.Kleisli[S1, T], +) Operator[S1, S2] { + return Bind(setter, F.Flow2(f, FromEither[T])) +} + +// BindResultK is a variant of Bind that works with Result computations. +// This is an alias for BindEitherK for consistency with the Result naming convention. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: A Result Kleisli arrow (S1 -> Result[T]) +func BindResultK[S1, S2, T any]( + setter func(T) func(S1) S2, + f result.Kleisli[S1, T], +) Operator[S1, S2] { + return Bind(setter, F.Flow2(f, FromResult[T])) +} + +// BindIOEitherKL is a lens-based variant of BindIOEitherK. +// It combines a lens with an IOEither Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IOEither Kleisli arrow (T -> IOEither[error, T]) +func BindIOEitherKL[S, T any]( + lens L.Lens[S, T], + f ioresult.Kleisli[T, T], +) Operator[S, S] { + return BindL(lens, F.Flow2(f, FromIOEither[T])) +} + +// BindIOResultKL is a lens-based variant of BindIOResultK. +// This is an alias for BindIOEitherKL for consistency with the Result naming convention. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IOResult Kleisli arrow (T -> IOResult[T]) +func BindIOResultKL[S, T any]( + lens L.Lens[S, T], + f ioresult.Kleisli[T, T], +) Operator[S, S] { + return BindL(lens, F.Flow2(f, FromIOEither[T])) +} + +// BindIOKL is a lens-based variant of BindIOK. +// It combines a lens with an IO Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IO Kleisli arrow (T -> IO[T]) +func BindIOKL[S, T any]( + lens L.Lens[S, T], + f io.Kleisli[T, T], +) Operator[S, S] { + return BindL(lens, F.Flow2(f, FromIO[T])) +} + +// BindReaderKL is a lens-based variant of BindReaderK. +// It combines a lens with a Reader Kleisli arrow (with context.Context), focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: A Reader Kleisli arrow (T -> Reader[context.Context, T]) +func BindReaderKL[S, T any]( + lens L.Lens[S, T], + f reader.Kleisli[context.Context, T, T], +) Operator[S, S] { + return BindL(lens, F.Flow2(f, FromReader[T])) +} + +// BindReaderIOKL is a lens-based variant of BindReaderIOK. +// It combines a lens with a ReaderIO Kleisli arrow (with context.Context), focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: A ReaderIO Kleisli arrow (T -> ReaderIO[context.Context, T]) +func BindReaderIOKL[S, T any]( + lens L.Lens[S, T], + f readerio.Kleisli[context.Context, T, T], +) Operator[S, S] { + return BindL(lens, F.Flow2(f, FromReaderIO[T])) +} + +// ApIOEitherS is an applicative variant that works with IOEither values. +// Unlike BindIOEitherK, this uses applicative composition (ApS) instead of monadic +// composition (Bind), allowing independent computations to be combined. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IOEither value +func ApIOEitherS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa IOResult[T], +) Operator[S1, S2] { + return F.Bind2nd(F.Flow2[ReaderIOResult[S1], ioresult.Operator[S1, S2]], ioeither.ApS(setter, fa)) +} + +// ApIOResultS is an applicative variant that works with IOResult values. +// This is an alias for ApIOEitherS for consistency with the Result naming convention. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IOResult value +func ApIOResultS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa IOResult[T], +) Operator[S1, S2] { + return F.Bind2nd(F.Flow2[ReaderIOResult[S1], ioresult.Operator[S1, S2]], ioeither.ApS(setter, fa)) +} + +// ApIOS is an applicative variant that works with IO values. +// It lifts an IO value into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IO value +func ApIOS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa IO[T], +) Operator[S1, S2] { + return ApS(setter, FromIO(fa)) +} + +// ApReaderS is an applicative variant that works with Reader values. +// It lifts a Reader value (with context.Context) into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A Reader value +func ApReaderS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa Reader[context.Context, T], +) Operator[S1, S2] { + return ApS(setter, FromReader[T](fa)) +} + +// ApReaderIOS is an applicative variant that works with ReaderIO values. +// It lifts a ReaderIO value (with context.Context) into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A ReaderIO value +func ApReaderIOS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderIO[T], +) Operator[S1, S2] { + return ApS(setter, FromReaderIO[T](fa)) +} + +// ApEitherS is an applicative variant that works with Either (Result) values. +// It lifts an Either value into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An Either value +func ApEitherS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa Result[T], +) Operator[S1, S2] { + return ApS(setter, FromEither[T](fa)) +} + +// ApResultS is an applicative variant that works with Result values. +// This is an alias for ApEitherS for consistency with the Result naming convention. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A Result value +func ApResultS[S1, S2, T any]( + setter func(T) func(S1) S2, + fa Result[T], +) Operator[S1, S2] { + return ApS(setter, FromResult[T](fa)) +} + +// ApIOEitherSL is a lens-based variant of ApIOEitherS. +// It combines a lens with an IOEither value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IOEither value +func ApIOEitherSL[S, T any]( + lens L.Lens[S, T], + fa IOResult[T], +) Operator[S, S] { + return F.Bind2nd(F.Flow2[ReaderIOResult[S], ioresult.Operator[S, S]], ioresult.ApSL(lens, fa)) +} + +// ApIOResultSL is a lens-based variant of ApIOResultS. +// This is an alias for ApIOEitherSL for consistency with the Result naming convention. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IOResult value +func ApIOResultSL[S, T any]( + lens L.Lens[S, T], + fa IOResult[T], +) Operator[S, S] { + return F.Bind2nd(F.Flow2[ReaderIOResult[S], ioresult.Operator[S, S]], ioresult.ApSL(lens, fa)) +} + +// ApIOSL is a lens-based variant of ApIOS. +// It combines a lens with an IO value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IO value +func ApIOSL[S, T any]( + lens L.Lens[S, T], + fa IO[T], +) Operator[S, S] { + return ApSL(lens, FromIO(fa)) +} + +// ApReaderSL is a lens-based variant of ApReaderS. +// It combines a lens with a Reader value (with context.Context) using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A Reader value +func ApReaderSL[S, T any]( + lens L.Lens[S, T], + fa Reader[context.Context, T], +) Operator[S, S] { + return ApSL(lens, FromReader[T](fa)) +} + +// ApReaderIOSL is a lens-based variant of ApReaderIOS. +// It combines a lens with a ReaderIO value (with context.Context) using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A ReaderIO value +func ApReaderIOSL[S, T any]( + lens L.Lens[S, T], + fa ReaderIO[T], +) Operator[S, S] { + return ApSL(lens, FromReaderIO[T](fa)) +} + +// ApEitherSL is a lens-based variant of ApEitherS. +// It combines a lens with an Either value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An Either value +func ApEitherSL[S, T any]( + lens L.Lens[S, T], + fa Result[T], +) Operator[S, S] { + return ApSL(lens, FromEither[T](fa)) +} + +// ApResultSL is a lens-based variant of ApResultS. +// This is an alias for ApEitherSL for consistency with the Result naming convention. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A Result value +func ApResultSL[S, T any]( + lens L.Lens[S, T], + fa Result[T], +) Operator[S, S] { + return ApSL(lens, FromResult[T](fa)) +} diff --git a/v2/context/readerioresult/reader.go b/v2/context/readerioresult/reader.go index 92ee600..b2dfa85 100644 --- a/v2/context/readerioresult/reader.go +++ b/v2/context/readerioresult/reader.go @@ -438,6 +438,11 @@ func FromIOEither[A any](t IOResult[A]) ReaderIOResult[A] { return RIOR.FromIOEither[context.Context](t) } +//go:inline +func FromIOResult[A any](t IOResult[A]) ReaderIOResult[A] { + return RIOR.FromIOResult[context.Context](t) +} + // FromIO converts an [IO] into a [ReaderIOResult]. // The IO computation always succeeds, so it's wrapped in Right. // @@ -451,6 +456,16 @@ func FromIO[A any](t IO[A]) ReaderIOResult[A] { return RIOR.FromIO[context.Context](t) } +//go:inline +func FromReader[A any](t Reader[context.Context, A]) ReaderIOResult[A] { + return RIOR.FromReader[context.Context](t) +} + +//go:inline +func FromReaderIO[A any](t ReaderIO[A]) ReaderIOResult[A] { + return RIOR.FromReaderIO[context.Context](t) +} + // FromLazy converts a [Lazy] computation into a [ReaderIOResult]. // The Lazy computation always succeeds, so it's wrapped in Right. // This is an alias for [FromIO] since Lazy and IO have the same structure. diff --git a/v2/readerio/types.go b/v2/readerio/types.go index 0808c1b..4383fbc 100644 --- a/v2/readerio/types.go +++ b/v2/readerio/types.go @@ -25,5 +25,6 @@ type ( Reader[R, A any] = reader.Reader[R, A] ReaderIO[R, A any] = Reader[R, IO[A]] - Operator[R, A, B any] = Reader[ReaderIO[R, A], ReaderIO[R, B]] + Kleisli[R, A, B any] = Reader[A, ReaderIO[R, B]] + Operator[R, A, B any] = Kleisli[R, ReaderIO[R, A], B] ) diff --git a/v2/readerioeither/bind.go b/v2/readerioeither/bind.go index 34609c9..9197a3e 100644 --- a/v2/readerioeither/bind.go +++ b/v2/readerioeither/bind.go @@ -16,9 +16,13 @@ package readerioeither import ( + "github.com/IBM/fp-go/v2/either" F "github.com/IBM/fp-go/v2/function" - IOE "github.com/IBM/fp-go/v2/ioeither" + "github.com/IBM/fp-go/v2/io" + "github.com/IBM/fp-go/v2/ioeither" L "github.com/IBM/fp-go/v2/optics/lens" + "github.com/IBM/fp-go/v2/reader" + "github.com/IBM/fp-go/v2/readerio" G "github.com/IBM/fp-go/v2/readerioeither/generic" ) @@ -102,7 +106,7 @@ func Let[R, E, S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) T, ) Operator[R, E, S1, S2] { - return G.Let[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], R, E, S1, S2, T](setter, f) + return G.Let[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ioeither.IOEither[E, S1], ioeither.IOEither[E, S2], R, E, S1, S2, T](setter, f) } // LetTo attaches the a value to a context [S1] to produce a context [S2] @@ -112,7 +116,7 @@ func LetTo[R, E, S1, S2, T any]( setter func(T) func(S1) S2, b T, ) Operator[R, E, S1, S2] { - return G.LetTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], IOE.IOEither[E, S1], IOE.IOEither[E, S2], R, E, S1, S2, T](setter, b) + return G.LetTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, S2], ioeither.IOEither[E, S1], ioeither.IOEither[E, S2], R, E, S1, S2, T](setter, b) } // BindTo initializes a new state [S1] from a value [T] @@ -121,7 +125,7 @@ func LetTo[R, E, S1, S2, T any]( func BindTo[R, E, S1, T any]( setter func(T) S1, ) Operator[R, E, T, S1] { - return G.BindTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, T], IOE.IOEither[E, S1], IOE.IOEither[E, T], R, E, S1, T](setter) + return G.BindTo[ReaderIOEither[R, E, S1], ReaderIOEither[R, E, T], ioeither.IOEither[E, S1], ioeither.IOEither[E, T], R, E, S1, T](setter) } // ApS attaches a value to a context [S1] to produce a context [S2] by considering @@ -171,7 +175,7 @@ func ApS[R, E, S1, S2, T any]( setter func(T) func(S1) S2, fa ReaderIOEither[R, E, T], ) 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], ioeither.IOEither[E, func(T) S2], ioeither.IOEither[E, S1], ioeither.IOEither[E, S2], ioeither.IOEither[E, T], R, E, S1, S2, T](setter, fa) } // ApSL attaches a value to a context using a lens-based setter. @@ -322,3 +326,259 @@ func LetToL[R, E, S, T any]( ) Operator[R, E, S, S] { return LetTo[R, E, S, S, T](lens.Set, b) } + +// BindIOEitherK is a variant of Bind that works with IOEither computations. +// It lifts an IOEither Kleisli arrow into the ReaderIOEither context, allowing you to +// compose IOEither operations within a do-notation chain. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An IOEither Kleisli arrow (S1 -> IOEither[E, T]) +// +// Returns: +// - An Operator that can be used in a do-notation chain +func BindIOEitherK[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f ioeither.Kleisli[E, S1, T], +) Operator[R, E, S1, S2] { + return Bind(setter, F.Flow2(f, FromIOEither[R, E, T])) +} + +// BindIOK is a variant of Bind that works with IO computations. +// It lifts an IO Kleisli arrow into the ReaderIOEither context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An IO Kleisli arrow (S1 -> IO[T]) +func BindIOK[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f io.Kleisli[S1, T], +) Operator[R, E, S1, S2] { + return Bind(setter, F.Flow2(f, FromIO[R, E, T])) +} + +// BindReaderK is a variant of Bind that works with Reader computations. +// It lifts a Reader Kleisli arrow into the ReaderIOEither context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: A Reader Kleisli arrow (S1 -> Reader[R, T]) +func BindReaderK[E, R, S1, S2, T any]( + setter func(T) func(S1) S2, + f reader.Kleisli[R, S1, T], +) Operator[R, E, S1, S2] { + return Bind(setter, F.Flow2(f, FromReader[E, R, T])) +} + +// BindReaderIOK is a variant of Bind that works with ReaderIO computations. +// It lifts a ReaderIO Kleisli arrow into the ReaderIOEither context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: A ReaderIO Kleisli arrow (S1 -> ReaderIO[R, T]) +func BindReaderIOK[E, R, S1, S2, T any]( + setter func(T) func(S1) S2, + f readerio.Kleisli[R, S1, T], +) Operator[R, E, S1, S2] { + return Bind(setter, F.Flow2(f, FromReaderIO[E, R, T])) +} + +// BindEitherK is a variant of Bind that works with Either computations. +// It lifts an Either Kleisli arrow into the ReaderIOEither context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An Either Kleisli arrow (S1 -> Either[E, T]) +func BindEitherK[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + f either.Kleisli[E, S1, T], +) Operator[R, E, S1, S2] { + return Bind(setter, F.Flow2(f, FromEither[R, E, T])) +} + +// BindIOEitherKL is a lens-based variant of BindIOEitherK. +// It combines a lens with an IOEither Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IOEither Kleisli arrow (T -> IOEither[E, T]) +func BindIOEitherKL[R, E, S, T any]( + lens L.Lens[S, T], + f ioeither.Kleisli[E, T, T], +) Operator[R, E, S, S] { + return BindL(lens, F.Flow2(f, FromIOEither[R, E, T])) +} + +// BindIOKL is a lens-based variant of BindIOK. +// It combines a lens with an IO Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IO Kleisli arrow (T -> IO[T]) +func BindIOKL[R, E, S, T any]( + lens L.Lens[S, T], + f io.Kleisli[T, T], +) Operator[R, E, S, S] { + return BindL(lens, F.Flow2(f, FromIO[R, E, T])) +} + +// BindReaderKL is a lens-based variant of BindReaderK. +// It combines a lens with a Reader Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: A Reader Kleisli arrow (T -> Reader[R, T]) +func BindReaderKL[E, R, S, T any]( + lens L.Lens[S, T], + f reader.Kleisli[R, T, T], +) Operator[R, E, S, S] { + return BindL(lens, F.Flow2(f, FromReader[E, R, T])) +} + +// BindReaderIOKL is a lens-based variant of BindReaderIOK. +// It combines a lens with a ReaderIO Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: A ReaderIO Kleisli arrow (T -> ReaderIO[R, T]) +func BindReaderIOKL[E, R, S, T any]( + lens L.Lens[S, T], + f readerio.Kleisli[R, T, T], +) Operator[R, E, S, S] { + return BindL(lens, F.Flow2(f, FromReaderIO[E, R, T])) +} + +// ApIOEitherS is an applicative variant that works with IOEither values. +// Unlike BindIOEitherK, this uses applicative composition (ApS) instead of monadic +// composition (Bind), allowing independent computations to be combined. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IOEither value +func ApIOEitherS[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa IOEither[E, T], +) Operator[R, E, S1, S2] { + return F.Bind2nd(F.Flow2[ReaderIOEither[R, E, S1], ioeither.Operator[E, S1, S2]], ioeither.ApS(setter, fa)) +} + +// ApIOS is an applicative variant that works with IO values. +// It lifts an IO value into the ReaderIOEither context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IO value +func ApIOS[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa IO[T], +) Operator[R, E, S1, S2] { + return ApS(setter, FromIO[R, E](fa)) +} + +// ApReaderS is an applicative variant that works with Reader values. +// It lifts a Reader value into the ReaderIOEither context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A Reader value +func ApReaderS[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa Reader[R, T], +) Operator[R, E, S1, S2] { + return ApS(setter, FromReader[E, R, T](fa)) +} + +// ApReaderIOS is an applicative variant that works with ReaderIO values. +// It lifts a ReaderIO value into the ReaderIOEither context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A ReaderIO value +func ApReaderIOS[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderIO[R, T], +) Operator[R, E, S1, S2] { + return ApS(setter, FromReaderIO[E, R, T](fa)) +} + +// ApEitherS is an applicative variant that works with Either values. +// It lifts an Either value into the ReaderIOEither context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An Either value +func ApEitherS[R, E, S1, S2, T any]( + setter func(T) func(S1) S2, + fa Either[E, T], +) Operator[R, E, S1, S2] { + return ApS(setter, FromEither[R, E, T](fa)) +} + +// ApIOEitherSL is a lens-based variant of ApIOEitherS. +// It combines a lens with an IOEither value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IOEither value +func ApIOEitherSL[R, E, S, T any]( + lens L.Lens[S, T], + fa IOEither[E, T], +) Operator[R, E, S, S] { + return F.Bind2nd(F.Flow2[ReaderIOEither[R, E, S], ioeither.Operator[E, S, S]], ioeither.ApSL(lens, fa)) +} + +// ApIOSL is a lens-based variant of ApIOS. +// It combines a lens with an IO value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IO value +func ApIOSL[R, E, S, T any]( + lens L.Lens[S, T], + fa IO[T], +) Operator[R, E, S, S] { + return ApSL(lens, FromIO[R, E](fa)) +} + +// ApReaderSL is a lens-based variant of ApReaderS. +// It combines a lens with a Reader value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A Reader value +func ApReaderSL[R, E, S, T any]( + lens L.Lens[S, T], + fa Reader[R, T], +) Operator[R, E, S, S] { + return ApSL(lens, FromReader[E, R, T](fa)) +} + +// ApReaderIOSL is a lens-based variant of ApReaderIOS. +// It combines a lens with a ReaderIO value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A ReaderIO value +func ApReaderIOSL[R, E, S, T any]( + lens L.Lens[S, T], + fa ReaderIO[R, T], +) Operator[R, E, S, S] { + return ApSL(lens, FromReaderIO[E, R, T](fa)) +} + +// ApEitherSL is a lens-based variant of ApEitherS. +// It combines a lens with an Either value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An Either value +func ApEitherSL[R, E, S, T any]( + lens L.Lens[S, T], + fa Either[E, T], +) Operator[R, E, S, S] { + return ApSL(lens, FromEither[R, E, T](fa)) +} diff --git a/v2/readerioeither/reader.go b/v2/readerioeither/reader.go index ab6a042..136a5dc 100644 --- a/v2/readerioeither/reader.go +++ b/v2/readerioeither/reader.go @@ -34,26 +34,14 @@ import ( "github.com/IBM/fp-go/v2/readerio" ) -// MonadFromReaderIO creates a ReaderIOEither from a value and a function that produces a ReaderIO. -// The ReaderIO result is lifted into the Right side of the Either. -func MonadFromReaderIO[R, E, A any](a A, f func(A) ReaderIO[R, A]) ReaderIOEither[R, E, A] { - return function.Pipe2( - a, - f, - RightReaderIO[R, E, A], - ) -} - -// FromReaderIO creates a function that lifts a ReaderIO-producing function into ReaderIOEither. -// The ReaderIO result is placed in the Right side of the Either. -func FromReaderIO[R, E, A any](f func(A) ReaderIO[R, A]) func(A) ReaderIOEither[R, E, A] { - return function.Bind2nd(MonadFromReaderIO[R, E, A], f) +func FromReaderIO[E, R, A any](ma ReaderIO[R, A]) ReaderIOEither[R, E, A] { + return RightReaderIO[E](ma) } // 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[E, R, A any](ma ReaderIO[R, A]) ReaderIOEither[R, E, A] { return eithert.RightF( readerio.MonadMap[R, A, either.Either[E, A]], ma, diff --git a/v2/readerioeither/readerioeither_test.go b/v2/readerioeither/readerioeither_test.go index f6295f0..750b819 100644 --- a/v2/readerioeither/readerioeither_test.go +++ b/v2/readerioeither/readerioeither_test.go @@ -611,35 +611,12 @@ func TestLocal(t *testing.T) { assert.Equal(t, E.Right[error](40), result(ctx2)()) } -func TestMonadFromReaderIO(t *testing.T) { - ctx := testContext{value: 10} - result := MonadFromReaderIO[testContext, error]( - 5, - func(x int) RIO.ReaderIO[testContext, int] { - return func(c testContext) io.IO[int] { - return func() int { return x + c.value } - } - }, - ) - assert.Equal(t, E.Right[error](15), result(ctx)()) -} - -func TestFromReaderIO(t *testing.T) { - ctx := testContext{value: 10} - result := FromReaderIO[testContext, error](func(x int) RIO.ReaderIO[testContext, int] { - return func(c testContext) io.IO[int] { - return func() int { return x + c.value } - } - })(5) - assert.Equal(t, E.Right[error](15), result(ctx)()) -} - func TestRightReaderIO(t *testing.T) { ctx := testContext{value: 10} rio := func(c testContext) io.IO[int] { return func() int { return c.value * 2 } } - result := RightReaderIO[testContext, error](rio) + result := RightReaderIO[error, testContext](rio) assert.Equal(t, E.Right[error](20), result(ctx)()) } diff --git a/v2/readerioeither/types.go b/v2/readerioeither/types.go index f915e3e..ae03d45 100644 --- a/v2/readerioeither/types.go +++ b/v2/readerioeither/types.go @@ -17,6 +17,7 @@ package readerioeither import ( "github.com/IBM/fp-go/v2/either" + "github.com/IBM/fp-go/v2/io" "github.com/IBM/fp-go/v2/ioeither" "github.com/IBM/fp-go/v2/reader" "github.com/IBM/fp-go/v2/readerio" @@ -31,6 +32,8 @@ type ( // and produces a value of type A. It's useful for dependency injection patterns. Reader[R, A any] = reader.Reader[R, A] + IO[T any] = io.IO[T] + // ReaderIO represents a computation that depends on some context R and performs // side effects to produce a value of type A. ReaderIO[R, A any] = readerio.ReaderIO[R, A] diff --git a/v2/readerioresult/bind.go b/v2/readerioresult/bind.go index 00527a3..c12589d 100644 --- a/v2/readerioresult/bind.go +++ b/v2/readerioresult/bind.go @@ -16,8 +16,15 @@ package readerioresult import ( + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/io" + "github.com/IBM/fp-go/v2/ioeither" + "github.com/IBM/fp-go/v2/ioresult" L "github.com/IBM/fp-go/v2/optics/lens" + "github.com/IBM/fp-go/v2/reader" + "github.com/IBM/fp-go/v2/readerio" RIOE "github.com/IBM/fp-go/v2/readerioeither" + "github.com/IBM/fp-go/v2/result" ) // Do creates an empty context of type [S] to be used with the [Bind] operation. @@ -320,3 +327,517 @@ func LetToL[R, S, T any]( ) Operator[R, S, S] { return RIOE.LetToL[R, error](lens, b) } + +// BindIOEitherK is a variant of Bind that works with IOEither computations. +// It lifts an IOEither Kleisli arrow into the ReaderIOResult context, allowing you to +// compose IOEither operations within a do-notation chain. +// +// This is useful when you have an existing IOEither computation that doesn't need +// access to the Reader environment, and you want to integrate it into a ReaderIOResult pipeline. +// +// Parameters: +// - setter: A function that takes the result T and returns a function to update the state from S1 to S2 +// - f: An IOEither Kleisli arrow that takes S1 and returns IOEither[error, T] +// +// Returns: +// - An Operator that can be used in a do-notation chain +// +// Example: +// +// type State struct { +// UserID int +// Data []byte +// } +// +// // An IOEither operation that reads a file +// readFile := func(s State) ioeither.IOEither[error, []byte] { +// return ioeither.TryCatch(func() ([]byte, error) { +// return os.ReadFile(fmt.Sprintf("user_%d.json", s.UserID)) +// }) +// } +// +// result := F.Pipe2( +// readerioresult.Do[Env, error](State{UserID: 123}), +// readerioresult.BindIOEitherK( +// func(data []byte) func(State) State { +// return func(s State) State { s.Data = data; return s } +// }, +// readFile, +// ), +// ) +func BindIOEitherK[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f ioresult.Kleisli[S1, T], +) Operator[R, S1, S2] { + return Bind(setter, F.Flow2(f, FromIOEither[R, T])) +} + +func BindIOResultK[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f ioresult.Kleisli[S1, T], +) Operator[R, S1, S2] { + return Bind(setter, F.Flow2(f, FromIOResult[R, T])) +} + +// BindIOK is a variant of Bind that works with IO computations. +// It lifts an IO Kleisli arrow into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An IO Kleisli arrow (S1 -> IO[T]) +// +// Example: +// +// getCurrentTime := func(s State) io.IO[time.Time] { +// return func() time.Time { return time.Now() } +// } +func BindIOK[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f io.Kleisli[S1, T], +) Operator[R, S1, S2] { + return Bind(setter, F.Flow2(f, FromIO[R, T])) +} + +// BindReaderK is a variant of Bind that works with Reader computations. +// It lifts a Reader Kleisli arrow into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: A Reader Kleisli arrow (S1 -> Reader[R, T]) +// +// Example: +// +// getConfig := func(s State) reader.Reader[Env, string] { +// return func(env Env) string { return env.ConfigValue } +// } +func BindReaderK[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f reader.Kleisli[R, S1, T], +) Operator[R, S1, S2] { + return Bind(setter, F.Flow2(f, FromReader[R, T])) +} + +// BindReaderIOK is a variant of Bind that works with ReaderIO computations. +// It lifts a ReaderIO Kleisli arrow into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: A ReaderIO Kleisli arrow (S1 -> ReaderIO[R, T]) +// +// Example: +// +// logState := func(s State) readerio.ReaderIO[Env, string] { +// return func(env Env) io.IO[string] { +// return func() string { +// env.Logger.Println(s) +// return "logged" +// } +// } +// } +func BindReaderIOK[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f readerio.Kleisli[R, S1, T], +) Operator[R, S1, S2] { + return Bind(setter, F.Flow2(f, FromReaderIO[R, T])) +} + +// BindEitherK is a variant of Bind that works with Either (Result) computations. +// It lifts an Either Kleisli arrow into the ReaderIOResult context. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - f: An Either Kleisli arrow (S1 -> Either[error, T]) +// +// Example: +// +// parseValue := func(s State) result.Result[int] { +// return result.TryCatch(func() (int, error) { +// return strconv.Atoi(s.StringValue) +// }) +// } +func BindEitherK[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f result.Kleisli[S1, T], +) Operator[R, S1, S2] { + return Bind(setter, F.Flow2(f, FromEither[R, T])) +} + +func BindResultK[R, S1, S2, T any]( + setter func(T) func(S1) S2, + f result.Kleisli[S1, T], +) Operator[R, S1, S2] { + return Bind(setter, F.Flow2(f, FromResult[R, T])) +} + +// BindIOEitherKL is a lens-based variant of BindIOEitherK. +// It combines a lens with an IOEither Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IOEither Kleisli arrow (T -> IOEither[error, T]) +// +// Example: +// +// userLens := lens.MakeLens( +// func(s State) User { return s.User }, +// func(s State, u User) State { s.User = u; return s }, +// ) +// updateUser := func(u User) ioeither.IOEither[error, User] { +// return ioeither.TryCatch(func() (User, error) { +// return saveUser(u) +// }) +// } +// result := F.Pipe2( +// readerioresult.Do[Env](State{}), +// readerioresult.BindIOEitherKL(userLens, updateUser), +// ) +func BindIOEitherKL[R, S, T any]( + lens L.Lens[S, T], + f ioresult.Kleisli[T, T], +) Operator[R, S, S] { + return BindL(lens, F.Flow2(f, FromIOEither[R, T])) +} + +// BindIOResultKL is a lens-based variant of BindIOResultK. +// It combines a lens with an IOResult Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IOResult Kleisli arrow (T -> IOResult[T]) +func BindIOResultKL[R, S, T any]( + lens L.Lens[S, T], + f ioresult.Kleisli[T, T], +) Operator[R, S, S] { + return BindL(lens, F.Flow2(f, FromIOEither[R, T])) +} + +// BindIOKL is a lens-based variant of BindIOK. +// It combines a lens with an IO Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: An IO Kleisli arrow (T -> IO[T]) +// +// Example: +// +// timestampLens := lens.MakeLens( +// func(s State) time.Time { return s.Timestamp }, +// func(s State, t time.Time) State { s.Timestamp = t; return s }, +// ) +// updateTimestamp := func(t time.Time) io.IO[time.Time] { +// return func() time.Time { return time.Now() } +// } +func BindIOKL[R, S, T any]( + lens L.Lens[S, T], + f io.Kleisli[T, T], +) Operator[R, S, S] { + return BindL(lens, F.Flow2(f, FromIO[R, T])) +} + +// BindReaderKL is a lens-based variant of BindReaderK. +// It combines a lens with a Reader Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: A Reader Kleisli arrow (T -> Reader[R, T]) +// +// Example: +// +// configLens := lens.MakeLens( +// func(s State) string { return s.Config }, +// func(s State, c string) State { s.Config = c; return s }, +// ) +// getConfigFromEnv := func(c string) reader.Reader[Env, string] { +// return func(env Env) string { return env.ConfigValue } +// } +func BindReaderKL[R, S, T any]( + lens L.Lens[S, T], + f reader.Kleisli[R, T, T], +) Operator[R, S, S] { + return BindL(lens, F.Flow2(f, FromReader[R, T])) +} + +// BindReaderIOKL is a lens-based variant of BindReaderIOK. +// It combines a lens with a ReaderIO Kleisli arrow, focusing on a specific field +// within the state structure. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - f: A ReaderIO Kleisli arrow (T -> ReaderIO[R, T]) +// +// Example: +// +// logLens := lens.MakeLens( +// func(s State) string { return s.LogMessage }, +// func(s State, l string) State { s.LogMessage = l; return s }, +// ) +// logMessage := func(msg string) readerio.ReaderIO[Env, string] { +// return func(env Env) io.IO[string] { +// return func() string { +// env.Logger.Println(msg) +// return "logged: " + msg +// } +// } +// } +func BindReaderIOKL[R, S, T any]( + lens L.Lens[S, T], + f readerio.Kleisli[R, T, T], +) Operator[R, S, S] { + return BindL(lens, F.Flow2(f, FromReaderIO[R, T])) +} + +// ApIOEitherS is an applicative variant that works with IOEither values. +// Unlike BindIOEitherK, this uses applicative composition (ApS) instead of monadic +// composition (Bind), allowing independent computations to be combined. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IOEither value (not a Kleisli arrow) +// +// Example: +// +// readConfig := ioeither.TryCatch(func() (Config, error) { +// return loadConfig() +// }) +func ApIOEitherS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa IOResult[T], +) Operator[R, S1, S2] { + return F.Bind2nd(F.Flow2[ReaderIOResult[R, S1], ioresult.Operator[S1, S2]], ioeither.ApS(setter, fa)) +} + +// ApIOResultS is an applicative variant that works with IOResult values. +// This is an alias for ApIOEitherS for consistency with the Result naming convention. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IOResult value +func ApIOResultS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa IOResult[T], +) Operator[R, S1, S2] { + return F.Bind2nd(F.Flow2[ReaderIOResult[R, S1], ioresult.Operator[S1, S2]], ioeither.ApS(setter, fa)) +} + +// ApIOS is an applicative variant that works with IO values. +// It lifts an IO value into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An IO value +// +// Example: +// +// getCurrentTime := func() time.Time { return time.Now() } +func ApIOS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa IO[T], +) Operator[R, S1, S2] { + return ApS(setter, FromIO[R](fa)) +} + +// ApReaderS is an applicative variant that works with Reader values. +// It lifts a Reader value into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A Reader value +// +// Example: +// +// getEnvConfig := func(env Env) string { return env.ConfigValue } +func ApReaderS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa Reader[R, T], +) Operator[R, S1, S2] { + return ApS(setter, FromReader[R, T](fa)) +} + +// ApReaderIOS is an applicative variant that works with ReaderIO values. +// It lifts a ReaderIO value into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A ReaderIO value +func ApReaderIOS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa ReaderIO[R, T], +) Operator[R, S1, S2] { + return ApS(setter, FromReaderIO[R, T](fa)) +} + +// ApEitherS is an applicative variant that works with Either (Result) values. +// It lifts an Either value into the ReaderIOResult context using applicative composition. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: An Either value +// +// Example: +// +// parseResult := result.TryCatch(func() (int, error) { +// return strconv.Atoi("123") +// }) +func ApEitherS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa Result[T], +) Operator[R, S1, S2] { + return ApS(setter, FromEither[R, T](fa)) +} + +// ApResultS is an applicative variant that works with Result values. +// This is an alias for ApEitherS for consistency with the Result naming convention. +// +// Parameters: +// - setter: Updates state from S1 to S2 using result T +// - fa: A Result value +func ApResultS[R, S1, S2, T any]( + setter func(T) func(S1) S2, + fa Result[T], +) Operator[R, S1, S2] { + return ApS(setter, FromResult[R, T](fa)) +} + +// ApIOEitherSL is a lens-based variant of ApIOEitherS. +// It combines a lens with an IOEither value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IOEither value +// +// Example: +// +// userLens := lens.MakeLens( +// func(s State) User { return s.User }, +// func(s State, u User) State { s.User = u; return s }, +// ) +// loadUser := ioeither.TryCatch(func() (User, error) { +// return fetchUser() +// }) +func ApIOEitherSL[R, S, T any]( + lens L.Lens[S, T], + fa IOResult[T], +) Operator[R, S, S] { + return F.Bind2nd(F.Flow2[ReaderIOResult[R, S], ioresult.Operator[S, S]], ioresult.ApSL(lens, fa)) +} + +// ApIOResultSL is a lens-based variant of ApIOResultS. +// This is an alias for ApIOEitherSL for consistency with the Result naming convention. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IOResult value +func ApIOResultSL[R, S, T any]( + lens L.Lens[S, T], + fa IOResult[T], +) Operator[R, S, S] { + return F.Bind2nd(F.Flow2[ReaderIOResult[R, S], ioresult.Operator[S, S]], ioresult.ApSL(lens, fa)) +} + +// ApIOSL is a lens-based variant of ApIOS. +// It combines a lens with an IO value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An IO value +// +// Example: +// +// timestampLens := lens.MakeLens( +// func(s State) time.Time { return s.Timestamp }, +// func(s State, t time.Time) State { s.Timestamp = t; return s }, +// ) +// getCurrentTime := func() time.Time { return time.Now() } +func ApIOSL[R, S, T any]( + lens L.Lens[S, T], + fa IO[T], +) Operator[R, S, S] { + return ApSL(lens, FromIO[R](fa)) +} + +// ApReaderSL is a lens-based variant of ApReaderS. +// It combines a lens with a Reader value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A Reader value +// +// Example: +// +// configLens := lens.MakeLens( +// func(s State) string { return s.Config }, +// func(s State, c string) State { s.Config = c; return s }, +// ) +// getConfig := func(env Env) string { return env.ConfigValue } +func ApReaderSL[R, S, T any]( + lens L.Lens[S, T], + fa Reader[R, T], +) Operator[R, S, S] { + return ApSL(lens, FromReader[R, T](fa)) +} + +// ApReaderIOSL is a lens-based variant of ApReaderIOS. +// It combines a lens with a ReaderIO value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A ReaderIO value +// +// Example: +// +// logLens := lens.MakeLens( +// func(s State) string { return s.LogMessage }, +// func(s State, l string) State { s.LogMessage = l; return s }, +// ) +// logWithEnv := func(env Env) io.IO[string] { +// return func() string { +// env.Logger.Println("Processing") +// return "logged" +// } +// } +func ApReaderIOSL[R, S, T any]( + lens L.Lens[S, T], + fa ReaderIO[R, T], +) Operator[R, S, S] { + return ApSL(lens, FromReaderIO[R, T](fa)) +} + +// ApEitherSL is a lens-based variant of ApEitherS. +// It combines a lens with an Either value using applicative composition. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: An Either value +// +// Example: +// +// valueLens := lens.MakeLens( +// func(s State) int { return s.Value }, +// func(s State, v int) State { s.Value = v; return s }, +// ) +// parseValue := result.TryCatch(func() (int, error) { +// return strconv.Atoi("123") +// }) +func ApEitherSL[R, S, T any]( + lens L.Lens[S, T], + fa Result[T], +) Operator[R, S, S] { + return ApSL(lens, FromEither[R, T](fa)) +} + +// ApResultSL is a lens-based variant of ApResultS. +// This is an alias for ApEitherSL for consistency with the Result naming convention. +// +// Parameters: +// - lens: A lens focusing on field T within state S +// - fa: A Result value +func ApResultSL[R, S, T any]( + lens L.Lens[S, T], + fa Result[T], +) Operator[R, S, S] { + return ApSL(lens, FromResult[R, T](fa)) +} diff --git a/v2/readerioresult/bind_test.go b/v2/readerioresult/bind_test.go index 7428237..a174a41 100644 --- a/v2/readerioresult/bind_test.go +++ b/v2/readerioresult/bind_test.go @@ -56,3 +56,5 @@ func TestApS(t *testing.T) { assert.Equal(t, res(context.Background())(), result.Of("John Doe")) } + +// Made with Bob diff --git a/v2/readerioresult/reader.go b/v2/readerioresult/reader.go index 1b2ef48..9f58bdc 100644 --- a/v2/readerioresult/reader.go +++ b/v2/readerioresult/reader.go @@ -20,23 +20,17 @@ import ( RIOE "github.com/IBM/fp-go/v2/readerioeither" ) -// MonadFromReaderIO creates a ReaderIOResult from a value and a function that produces a ReaderIO. -// The ReaderIO result is lifted into the Right side of the Either. -func MonadFromReaderIO[R, A any](a A, f func(A) ReaderIO[R, A]) ReaderIOResult[R, A] { - return RIOE.MonadFromReaderIO[R, error](a, f) -} - // FromReaderIO creates a function that lifts a ReaderIO-producing function into ReaderIOResult. // The ReaderIO result is placed in the Right side of the Either. -func FromReaderIO[R, A any](f func(A) ReaderIO[R, A]) Kleisli[R, A, A] { - return RIOE.FromReaderIO[R, error](f) +func FromReaderIO[R, A any](ma ReaderIO[R, A]) ReaderIOResult[R, A] { + return RIOE.FromReaderIO[error, R](ma) } // RightReaderIO lifts a ReaderIO into a ReaderIOResult, placing the result in the Right side. // //go:inline func RightReaderIO[R, A any](ma ReaderIO[R, A]) ReaderIOResult[R, A] { - return RIOE.RightReaderIO[R, error](ma) + return RIOE.RightReaderIO[error, R](ma) } // LeftReaderIO lifts a ReaderIO into a ReaderIOResult, placing the result in the Left (error) side. diff --git a/v2/readerioresult/readerioeither_test.go b/v2/readerioresult/readerioeither_test.go index b26c7a4..8c33254 100644 --- a/v2/readerioresult/readerioeither_test.go +++ b/v2/readerioresult/readerioeither_test.go @@ -573,29 +573,6 @@ func TestLocal(t *testing.T) { assert.Equal(t, result.Of(40), res(ctx2)()) } -func TestMonadFromReaderIO(t *testing.T) { - ctx := testContext{value: 10} - res := MonadFromReaderIO( - 5, - func(x int) RIO.ReaderIO[testContext, int] { - return func(c testContext) IO[int] { - return func() int { return x + c.value } - } - }, - ) - assert.Equal(t, result.Of(15), res(ctx)()) -} - -func TestFromReaderIO(t *testing.T) { - ctx := testContext{value: 10} - res := FromReaderIO(func(x int) RIO.ReaderIO[testContext, int] { - return func(c testContext) IO[int] { - return func() int { return x + c.value } - } - })(5) - assert.Equal(t, result.Of(15), res(ctx)()) -} - func TestRightReaderIO(t *testing.T) { ctx := testContext{value: 10} rio := func(c testContext) IO[int] {