mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-25 22:21:49 +02:00
fix: implement BindReaderK
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -22,10 +22,12 @@ import (
|
|||||||
O "github.com/IBM/fp-go/v2/option"
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func FromOption[A, HKTEA, E any](fromEither func(ET.Either[E, A]) HKTEA, onNone func() E) func(ma O.Option[A]) HKTEA {
|
func FromOption[A, HKTEA, E any](fromEither func(ET.Either[E, A]) HKTEA, onNone func() E) func(ma O.Option[A]) HKTEA {
|
||||||
return F.Flow2(ET.FromOption[A](onNone), fromEither)
|
return F.Flow2(ET.FromOption[A](onNone), fromEither)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func FromPredicate[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, pred func(A) bool, onFalse func(A) E) func(A) HKTEA {
|
func FromPredicate[E, A, HKTEA any](fromEither func(ET.Either[E, A]) HKTEA, pred func(A) bool, onFalse func(A) E) func(A) HKTEA {
|
||||||
return F.Flow2(ET.FromPredicate(pred, onFalse), fromEither)
|
return F.Flow2(ET.FromPredicate(pred, onFalse), fromEither)
|
||||||
}
|
}
|
||||||
@@ -45,6 +47,7 @@ func MonadFromOption[E, A, HKTEA any](
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func FromOptionK[A, E, B, HKTEB any](
|
func FromOptionK[A, E, B, HKTEB any](
|
||||||
fromEither func(ET.Either[E, B]) HKTEB,
|
fromEither func(ET.Either[E, B]) HKTEB,
|
||||||
onNone func() E) func(f func(A) O.Option[B]) func(A) HKTEB {
|
onNone func() E) func(f func(A) O.Option[B]) func(A) HKTEB {
|
||||||
@@ -52,6 +55,7 @@ func FromOptionK[A, E, B, HKTEB any](
|
|||||||
return F.Bind2nd(F.Flow2[func(A) O.Option[B], func(O.Option[B]) HKTEB, A, O.Option[B], HKTEB], FromOption(fromEither, onNone))
|
return F.Bind2nd(F.Flow2[func(A) O.Option[B], func(O.Option[B]) HKTEB, A, O.Option[B], HKTEB], FromOption(fromEither, onNone))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func MonadChainEitherK[A, E, B, HKTEA, HKTEB any](
|
func MonadChainEitherK[A, E, B, HKTEA, HKTEB any](
|
||||||
mchain func(HKTEA, func(A) HKTEB) HKTEB,
|
mchain func(HKTEA, func(A) HKTEB) HKTEB,
|
||||||
fromEither func(ET.Either[E, B]) HKTEB,
|
fromEither func(ET.Either[E, B]) HKTEB,
|
||||||
@@ -60,6 +64,7 @@ func MonadChainEitherK[A, E, B, HKTEA, HKTEB any](
|
|||||||
return mchain(ma, F.Flow2(f, fromEither))
|
return mchain(ma, F.Flow2(f, fromEither))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func ChainEitherK[A, E, B, HKTEA, HKTEB any](
|
func ChainEitherK[A, E, B, HKTEA, HKTEB any](
|
||||||
mchain func(func(A) HKTEB) func(HKTEA) HKTEB,
|
mchain func(func(A) HKTEB) func(HKTEA) HKTEB,
|
||||||
fromEither func(ET.Either[E, B]) HKTEB,
|
fromEither func(ET.Either[E, B]) HKTEB,
|
||||||
@@ -67,6 +72,7 @@ func ChainEitherK[A, E, B, HKTEA, HKTEB any](
|
|||||||
return mchain(F.Flow2(f, fromEither))
|
return mchain(F.Flow2(f, fromEither))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func ChainOptionK[A, E, B, HKTEA, HKTEB any](
|
func ChainOptionK[A, E, B, HKTEA, HKTEB any](
|
||||||
mchain func(HKTEA, func(A) HKTEB) HKTEB,
|
mchain func(HKTEA, func(A) HKTEB) HKTEB,
|
||||||
fromEither func(ET.Either[E, B]) HKTEB,
|
fromEither func(ET.Either[E, B]) HKTEB,
|
||||||
@@ -75,6 +81,7 @@ func ChainOptionK[A, E, B, HKTEA, HKTEB any](
|
|||||||
return F.Flow2(FromOptionK[A](fromEither, onNone), F.Bind1st(F.Bind2nd[HKTEA, func(A) HKTEB, HKTEB], mchain))
|
return F.Flow2(FromOptionK[A](fromEither, onNone), F.Bind1st(F.Bind2nd[HKTEA, func(A) HKTEB, HKTEB], mchain))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
|
func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
|
||||||
mchain func(HKTEA, func(A) HKTEA) HKTEA,
|
mchain func(HKTEA, func(A) HKTEA) HKTEA,
|
||||||
mmap func(HKTEB, func(B) A) HKTEA,
|
mmap func(HKTEB, func(B) A) HKTEA,
|
||||||
@@ -84,6 +91,7 @@ func MonadChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
|
|||||||
return C.MonadChainFirst(mchain, mmap, ma, F.Flow2(f, fromEither))
|
return C.MonadChainFirst(mchain, mmap, ma, F.Flow2(f, fromEither))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func ChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
|
func ChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
|
||||||
mchain func(func(A) HKTEA) func(HKTEA) HKTEA,
|
mchain func(func(A) HKTEA) func(HKTEA) HKTEA,
|
||||||
mmap func(func(B) A) func(HKTEB) HKTEA,
|
mmap func(func(B) A) func(HKTEB) HKTEA,
|
||||||
@@ -91,3 +99,23 @@ func ChainFirstEitherK[A, E, B, HKTEA, HKTEB any](
|
|||||||
f func(A) ET.Either[E, B]) func(HKTEA) HKTEA {
|
f func(A) ET.Either[E, B]) func(HKTEA) HKTEA {
|
||||||
return C.ChainFirst(mchain, mmap, F.Flow2(f, fromEither))
|
return C.ChainFirst(mchain, mmap, F.Flow2(f, fromEither))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindEitherK[
|
||||||
|
E, S1, S2, T,
|
||||||
|
HKTET,
|
||||||
|
HKTES1,
|
||||||
|
HKTES2 any](
|
||||||
|
mchain func(func(S1) HKTES2) func(HKTES1) HKTES2,
|
||||||
|
mmap func(func(T) S2) func(HKTET) HKTES2,
|
||||||
|
fromEither func(ET.Either[E, T]) HKTET,
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) ET.Either[E, T],
|
||||||
|
) func(HKTES1) HKTES2 {
|
||||||
|
return C.Bind(
|
||||||
|
mchain,
|
||||||
|
mmap,
|
||||||
|
setter,
|
||||||
|
F.Flow2(f, fromEither),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package fromreader
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
F "github.com/IBM/fp-go/v2/function"
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
C "github.com/IBM/fp-go/v2/internal/chain"
|
||||||
G "github.com/IBM/fp-go/v2/reader/generic"
|
G "github.com/IBM/fp-go/v2/reader/generic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,3 +70,24 @@ func ChainFirstReaderK[GB ~func(R) B, R, A, B, HKTRA, HKTRB any](
|
|||||||
) func(HKTRA) HKTRA {
|
) func(HKTRA) HKTRA {
|
||||||
return mchain(FromReaderK(fromReader, f))
|
return mchain(FromReaderK(fromReader, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindReaderK[
|
||||||
|
GT ~func(R) T,
|
||||||
|
R, S1, S2, T,
|
||||||
|
HKTET,
|
||||||
|
HKTES1,
|
||||||
|
HKTES2 any](
|
||||||
|
mchain func(func(S1) HKTES2) func(HKTES1) HKTES2,
|
||||||
|
mmap func(func(T) S2) func(HKTET) HKTES2,
|
||||||
|
fromReader func(GT) HKTET,
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) GT,
|
||||||
|
) func(HKTES1) HKTES2 {
|
||||||
|
return C.Bind(
|
||||||
|
mchain,
|
||||||
|
mmap,
|
||||||
|
setter,
|
||||||
|
FromReaderK(fromReader, f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package readereither
|
package readereither
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
ET "github.com/IBM/fp-go/v2/either"
|
||||||
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"
|
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"
|
||||||
@@ -35,6 +36,8 @@ import (
|
|||||||
// ConfigService ConfigService
|
// ConfigService ConfigService
|
||||||
// }
|
// }
|
||||||
// result := readereither.Do[Env, error](State{})
|
// result := readereither.Do[Env, error](State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Do[R, E, S any](
|
func Do[R, E, S any](
|
||||||
empty S,
|
empty S,
|
||||||
) ReaderEither[R, E, S] {
|
) ReaderEither[R, E, S] {
|
||||||
@@ -83,6 +86,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) ReaderEither[R, E, T],
|
f func(S1) ReaderEither[R, E, T],
|
||||||
@@ -90,7 +95,59 @@ func Bind[R, E, S1, S2, T any](
|
|||||||
return G.Bind[ReaderEither[R, E, S1], ReaderEither[R, E, S2]](setter, f)
|
return G.Bind[ReaderEither[R, E, S1], ReaderEither[R, E, S2]](setter, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindReaderK[R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) Reader[R, T],
|
||||||
|
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
|
||||||
|
return G.BindReaderK[ReaderEither[R, E, S1], ReaderEither[R, E, S2]](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindEitherK[R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) Either[E, T],
|
||||||
|
) func(ReaderEither[R, E, S1]) ReaderEither[R, E, S2] {
|
||||||
|
return G.BindEitherK[ReaderEither[R, E, S1], ReaderEither[R, E, S2]](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindToReader[
|
||||||
|
R, E, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(Reader[R, T]) ReaderEither[R, E, S1] {
|
||||||
|
return G.BindToReader[ReaderEither[R, E, S1], Reader[R, T]](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindToEither[
|
||||||
|
R, E, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(ET.Either[E, T]) ReaderEither[R, E, S1] {
|
||||||
|
return G.BindToEither[ReaderEither[R, E, S1]](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ApReaderS[
|
||||||
|
R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Reader[R, T],
|
||||||
|
) Operator[R, E, S1, S2] {
|
||||||
|
return G.ApReaderS[ReaderEither[R, E, S1], ReaderEither[R, E, S2]](setter, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ApEitherS[
|
||||||
|
R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ET.Either[E, T],
|
||||||
|
) Operator[R, E, S1, S2] {
|
||||||
|
return G.ApEitherS[ReaderEither[R, E, S1], ReaderEither[R, E, S2]](setter, fa)
|
||||||
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ package generic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
ET "github.com/IBM/fp-go/v2/either"
|
ET "github.com/IBM/fp-go/v2/either"
|
||||||
|
"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"
|
||||||
|
FE "github.com/IBM/fp-go/v2/internal/fromeither"
|
||||||
|
FR "github.com/IBM/fp-go/v2/internal/fromreader"
|
||||||
F "github.com/IBM/fp-go/v2/internal/functor"
|
F "github.com/IBM/fp-go/v2/internal/functor"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +39,8 @@ import (
|
|||||||
// UserService UserService
|
// UserService UserService
|
||||||
// }
|
// }
|
||||||
// result := generic.Do[ReaderEither[Env, error, State], Env, error, State](State{})
|
// result := generic.Do[ReaderEither[Env, error, State], Env, error, State](State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Do[GS ~func(R) ET.Either[E, S], R, E, S any](
|
func Do[GS ~func(R) ET.Either[E, S], R, E, S any](
|
||||||
empty S,
|
empty S,
|
||||||
) GS {
|
) GS {
|
||||||
@@ -84,7 +89,12 @@ func Do[GS ~func(R) ET.Either[E, S], R, E, S any](
|
|||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
func Bind[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func(R) ET.Either[E, T], R, E, S1, S2, T any](
|
//
|
||||||
|
//go:inline
|
||||||
|
func Bind[
|
||||||
|
GS1 ~func(R) ET.Either[E, S1],
|
||||||
|
GS2 ~func(R) ET.Either[E, S2],
|
||||||
|
GT ~func(R) ET.Either[E, T], R, E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) GT,
|
f func(S1) GT,
|
||||||
) func(GS1) GS2 {
|
) func(GS1) GS2 {
|
||||||
@@ -96,6 +106,41 @@ func Bind[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindReaderK[
|
||||||
|
GS1 ~func(R) ET.Either[E, S1],
|
||||||
|
GS2 ~func(R) ET.Either[E, S2],
|
||||||
|
GRT ~func(R) T,
|
||||||
|
R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) GRT,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return FR.BindReaderK(
|
||||||
|
Chain[GS1, GS2, E, R, S1, S2],
|
||||||
|
Map[func(R) ET.Either[E, T], GS2, E, R, T, S2],
|
||||||
|
FromReader[GRT, func(R) ET.Either[E, T]],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindEitherK[
|
||||||
|
GS1 ~func(R) ET.Either[E, S1],
|
||||||
|
GS2 ~func(R) ET.Either[E, S2],
|
||||||
|
R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) ET.Either[E, T],
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return FE.BindEitherK(
|
||||||
|
Chain[GS1, GS2, E, R, S1, S2],
|
||||||
|
Map[func(R) ET.Either[E, T], GS2, E, R, T, S2],
|
||||||
|
FromEither[func(R) ET.Either[E, T]],
|
||||||
|
setter,
|
||||||
|
f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 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]
|
||||||
func Let[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], R, E, S1, S2, T any](
|
func Let[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], R, E, S1, S2, T any](
|
||||||
key func(T) func(S1) S2,
|
key func(T) func(S1) S2,
|
||||||
@@ -130,6 +175,31 @@ func BindTo[GS1 ~func(R) ET.Either[E, S1], GT ~func(R) ET.Either[E, T], R, E, S1
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindToReader[
|
||||||
|
GS1 ~func(R) ET.Either[E, S1],
|
||||||
|
GT ~func(R) T,
|
||||||
|
R, E, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(GT) GS1 {
|
||||||
|
return function.Flow2(
|
||||||
|
FromReader[GT, func(R) ET.Either[E, T]],
|
||||||
|
BindTo[GS1, func(R) ET.Either[E, T]](setter),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindToEither[
|
||||||
|
GS1 ~func(R) ET.Either[E, S1],
|
||||||
|
R, E, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(ET.Either[E, T]) GS1 {
|
||||||
|
return function.Flow2(
|
||||||
|
FromEither[func(R) ET.Either[E, T]],
|
||||||
|
BindTo[GS1, func(R) ET.Either[E, T]](setter),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ApS attaches a value to a context [S1] to produce a context [S2] by considering
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering
|
||||||
// the context and the value concurrently (using Applicative rather than Monad).
|
// the context and the value concurrently (using Applicative rather than Monad).
|
||||||
// This allows independent computations to be combined without one depending on the result of the other.
|
// This allows independent computations to be combined without one depending on the result of the other.
|
||||||
@@ -182,3 +252,32 @@ func ApS[GS1 ~func(R) ET.Either[E, S1], GS2 ~func(R) ET.Either[E, S2], GT ~func(
|
|||||||
fa,
|
fa,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ApReaderS[
|
||||||
|
GS1 ~func(R) ET.Either[E, S1],
|
||||||
|
GS2 ~func(R) ET.Either[E, S2],
|
||||||
|
GT ~func(R) T,
|
||||||
|
R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa GT,
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return ApS[GS1, GS2](
|
||||||
|
setter,
|
||||||
|
FromReader[GT, func(R) ET.Either[E, T]](fa),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ApEitherS[
|
||||||
|
GS1 ~func(R) ET.Either[E, S1],
|
||||||
|
GS2 ~func(R) ET.Either[E, S2],
|
||||||
|
R, E, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ET.Either[E, T],
|
||||||
|
) func(GS1) GS2 {
|
||||||
|
return ApS[GS1, GS2](
|
||||||
|
setter,
|
||||||
|
FromEither[func(R) ET.Either[E, T]](fa),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -71,6 +71,23 @@ func Chain[GEA ~func(E) ET.Either[L, A], GEB ~func(E) ET.Either[L, B], L, E, A,
|
|||||||
return F.Bind2nd(MonadChain[GEA, GEB, L, E, A, B], f)
|
return F.Bind2nd(MonadChain[GEA, GEB, L, E, A, B], f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MonadChainReaderK[
|
||||||
|
GEA ~func(E) ET.Either[L, A],
|
||||||
|
GEB ~func(E) ET.Either[L, B],
|
||||||
|
GB ~func(E) B,
|
||||||
|
L, E, A, B any](ma GEA, f func(A) GB) GEB {
|
||||||
|
|
||||||
|
return MonadChain(ma, F.Flow2(f, FromReader[GB, GEB, L, E, B]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChainReaderK[
|
||||||
|
GEA ~func(E) ET.Either[L, A],
|
||||||
|
GEB ~func(E) ET.Either[L, B],
|
||||||
|
GB ~func(E) B,
|
||||||
|
L, E, A, B any](f func(A) GB) func(GEA) GEB {
|
||||||
|
return Chain[GEA, GEB, L, E, A, B](F.Flow2(f, FromReader[GB, GEB, L, E, B]))
|
||||||
|
}
|
||||||
|
|
||||||
func Of[GEA ~func(E) ET.Either[L, A], L, E, A any](a A) GEA {
|
func Of[GEA ~func(E) ET.Either[L, A], L, E, A any](a A) GEA {
|
||||||
return readert.MonadOf[GEA](ET.Of[L, A], a)
|
return readert.MonadOf[GEA](ET.Of[L, A], a)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ package readerresult
|
|||||||
import (
|
import (
|
||||||
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"
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
G "github.com/IBM/fp-go/v2/readereither/generic"
|
G "github.com/IBM/fp-go/v2/readereither/generic"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -321,3 +323,277 @@ func LetToL[R, S, T any](
|
|||||||
) Operator[R, S, S] {
|
) Operator[R, S, S] {
|
||||||
return LetTo[R](lens.Set, b)
|
return LetTo[R](lens.Set, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BindReaderK lifts a Reader Kleisli arrow into a ReaderResult context and binds it to the state.
|
||||||
|
// This allows you to integrate pure Reader computations (that don't have error handling)
|
||||||
|
// into a ReaderResult computation chain.
|
||||||
|
//
|
||||||
|
// The function f takes the current state S1 and returns a Reader[R, T] computation.
|
||||||
|
// The result T is then attached to the state using the setter to produce state S2.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Env struct {
|
||||||
|
// ConfigPath string
|
||||||
|
// }
|
||||||
|
// type State struct {
|
||||||
|
// Config string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // A pure Reader computation that reads from environment
|
||||||
|
// getConfigPath := func(s State) reader.Reader[Env, string] {
|
||||||
|
// return func(env Env) string {
|
||||||
|
// return env.ConfigPath
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerresult.Do[Env](State{}),
|
||||||
|
// readerresult.BindReaderK(
|
||||||
|
// func(path string) func(State) State {
|
||||||
|
// return func(s State) State { s.Config = path; return s }
|
||||||
|
// },
|
||||||
|
// getConfigPath,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindReaderK[R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f reader.Kleisli[R, S1, T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return G.BindReaderK[ReaderResult[R, S1], ReaderResult[R, S2]](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindEitherK[R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f result.Kleisli[S1, T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return G.BindEitherK[ReaderResult[R, S1], ReaderResult[R, S2]](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindResultK lifts a Result Kleisli arrow into a ReaderResult context and binds it to the state.
|
||||||
|
// This allows you to integrate Result computations (that may fail with an error but don't need
|
||||||
|
// environment access) into a ReaderResult computation chain.
|
||||||
|
//
|
||||||
|
// The function f takes the current state S1 and returns a Result[T] computation.
|
||||||
|
// If the Result is successful, the value T is attached to the state using the setter to produce state S2.
|
||||||
|
// If the Result is an error, the entire computation short-circuits with that error.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// Value int
|
||||||
|
// ParsedValue int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // A Result computation that may fail
|
||||||
|
// parseValue := func(s State) result.Result[int] {
|
||||||
|
// if s.Value < 0 {
|
||||||
|
// return result.Error[int](errors.New("negative value"))
|
||||||
|
// }
|
||||||
|
// return result.Of(s.Value * 2)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerresult.Do[any](State{Value: 5}),
|
||||||
|
// readerresult.BindResultK(
|
||||||
|
// func(parsed int) func(State) State {
|
||||||
|
// return func(s State) State { s.ParsedValue = parsed; return s }
|
||||||
|
// },
|
||||||
|
// parseValue,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindResultK[R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f result.Kleisli[S1, T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return G.BindEitherK[ReaderResult[R, S1], ReaderResult[R, S2]](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindToReader initializes a new state S1 from a Reader[R, T] computation.
|
||||||
|
// This is used to start a ReaderResult computation chain from a pure Reader value.
|
||||||
|
//
|
||||||
|
// The setter function takes the result T from the Reader and initializes the state S1.
|
||||||
|
// This is useful when you want to begin a do-notation chain with a Reader computation
|
||||||
|
// that doesn't involve error handling.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Env struct {
|
||||||
|
// ConfigPath string
|
||||||
|
// }
|
||||||
|
// type State struct {
|
||||||
|
// Config string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // A Reader that just reads from the environment
|
||||||
|
// getConfigPath := func(env Env) string {
|
||||||
|
// return env.ConfigPath
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe1(
|
||||||
|
// reader.Of[Env](getConfigPath),
|
||||||
|
// readerresult.BindToReader(func(path string) State {
|
||||||
|
// return State{Config: path}
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindToReader[
|
||||||
|
R, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(Reader[R, T]) ReaderResult[R, S1] {
|
||||||
|
return G.BindToReader[ReaderResult[R, S1], Reader[R, T]](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func BindToEither[
|
||||||
|
R, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(Result[T]) ReaderResult[R, S1] {
|
||||||
|
return G.BindToEither[ReaderResult[R, S1]](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindToResult initializes a new state S1 from a Result[T] value.
|
||||||
|
// This is used to start a ReaderResult computation chain from a Result that may contain an error.
|
||||||
|
//
|
||||||
|
// The setter function takes the successful result T and initializes the state S1.
|
||||||
|
// If the Result is an error, the entire computation will carry that error forward.
|
||||||
|
// This is useful when you want to begin a do-notation chain with a Result computation
|
||||||
|
// that doesn't need environment access.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// Value int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // A Result that might contain an error
|
||||||
|
// parseResult := result.TryCatch(func() int {
|
||||||
|
// // some parsing logic that might fail
|
||||||
|
// return 42
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// computation := F.Pipe1(
|
||||||
|
// parseResult,
|
||||||
|
// readerresult.BindToResult[any](func(value int) State {
|
||||||
|
// return State{Value: value}
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindToResult[
|
||||||
|
R, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) func(Result[T]) ReaderResult[R, S1] {
|
||||||
|
return G.BindToEither[ReaderResult[R, S1]](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApReaderS attaches a value from a pure Reader computation to a context [S1] to produce a context [S2]
|
||||||
|
// using Applicative semantics (independent, non-sequential composition).
|
||||||
|
//
|
||||||
|
// Unlike BindReaderK which uses monadic bind (sequential), ApReaderS uses applicative apply,
|
||||||
|
// meaning the Reader computation fa is independent of the current state and can conceptually
|
||||||
|
// execute in parallel.
|
||||||
|
//
|
||||||
|
// This is useful when you want to combine a Reader computation with your ReaderResult state
|
||||||
|
// without creating a dependency between them.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type Env struct {
|
||||||
|
// DefaultPort int
|
||||||
|
// DefaultHost string
|
||||||
|
// }
|
||||||
|
// type State struct {
|
||||||
|
// Port int
|
||||||
|
// Host string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// getDefaultPort := func(env Env) int { return env.DefaultPort }
|
||||||
|
// getDefaultHost := func(env Env) string { return env.DefaultHost }
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerresult.Do[Env](State{}),
|
||||||
|
// readerresult.ApReaderS(
|
||||||
|
// func(port int) func(State) State {
|
||||||
|
// return func(s State) State { s.Port = port; return s }
|
||||||
|
// },
|
||||||
|
// getDefaultPort,
|
||||||
|
// ),
|
||||||
|
// readerresult.ApReaderS(
|
||||||
|
// func(host string) func(State) State {
|
||||||
|
// return func(s State) State { s.Host = host; return s }
|
||||||
|
// },
|
||||||
|
// getDefaultHost,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApReaderS[
|
||||||
|
R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Reader[R, T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return G.ApReaderS[ReaderResult[R, S1], ReaderResult[R, S2]](setter, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func ApEitherS[
|
||||||
|
R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Result[T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return G.ApEitherS[ReaderResult[R, S1], ReaderResult[R, S2]](setter, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApResultS attaches a value from a Result to a context [S1] to produce a context [S2]
|
||||||
|
// using Applicative semantics (independent, non-sequential composition).
|
||||||
|
//
|
||||||
|
// Unlike BindResultK which uses monadic bind (sequential), ApResultS uses applicative apply,
|
||||||
|
// meaning the Result computation fa is independent of the current state and can conceptually
|
||||||
|
// execute in parallel.
|
||||||
|
//
|
||||||
|
// If the Result fa contains an error, the entire computation short-circuits with that error.
|
||||||
|
// This is useful when you want to combine a Result value with your ReaderResult state
|
||||||
|
// without creating a dependency between them.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// Value1 int
|
||||||
|
// Value2 int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Independent Result computations
|
||||||
|
// parseValue1 := result.TryCatch(func() int { return 42 })
|
||||||
|
// parseValue2 := result.TryCatch(func() int { return 100 })
|
||||||
|
//
|
||||||
|
// computation := F.Pipe2(
|
||||||
|
// readerresult.Do[any](State{}),
|
||||||
|
// readerresult.ApResultS(
|
||||||
|
// func(v int) func(State) State {
|
||||||
|
// return func(s State) State { s.Value1 = v; return s }
|
||||||
|
// },
|
||||||
|
// parseValue1,
|
||||||
|
// ),
|
||||||
|
// readerresult.ApResultS(
|
||||||
|
// func(v int) func(State) State {
|
||||||
|
// return func(s State) State { s.Value2 = v; return s }
|
||||||
|
// },
|
||||||
|
// parseValue2,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApResultS[
|
||||||
|
R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa Result[T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return G.ApEitherS[ReaderResult[R, S1], ReaderResult[R, S2]](setter, fa)
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,3 +56,291 @@ func TestApS(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, res(context.Background()), result.Of("John Doe"))
|
assert.Equal(t, res(context.Background()), result.Of("John Doe"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBindReaderK(t *testing.T) {
|
||||||
|
type Env struct {
|
||||||
|
ConfigPath string
|
||||||
|
}
|
||||||
|
type State struct {
|
||||||
|
Config string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A pure Reader computation
|
||||||
|
getConfigPath := func(s State) func(Env) string {
|
||||||
|
return func(env Env) string {
|
||||||
|
return env.ConfigPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := F.Pipe2(
|
||||||
|
Do[Env](State{}),
|
||||||
|
BindReaderK(
|
||||||
|
func(path string) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Config = path
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getConfigPath,
|
||||||
|
),
|
||||||
|
Map[Env](func(s State) string { return s.Config }),
|
||||||
|
)
|
||||||
|
|
||||||
|
env := Env{ConfigPath: "/etc/config"}
|
||||||
|
assert.Equal(t, result.Of("/etc/config"), res(env))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindResultK(t *testing.T) {
|
||||||
|
type State struct {
|
||||||
|
Value int
|
||||||
|
ParsedValue int
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Result computation that may fail
|
||||||
|
parseValue := func(s State) result.Result[int] {
|
||||||
|
if s.Value < 0 {
|
||||||
|
return result.Left[int](assert.AnError)
|
||||||
|
}
|
||||||
|
return result.Of(s.Value * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("success case", func(t *testing.T) {
|
||||||
|
res := F.Pipe2(
|
||||||
|
Do[context.Context](State{Value: 5}),
|
||||||
|
BindResultK[context.Context](
|
||||||
|
func(parsed int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.ParsedValue = parsed
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue,
|
||||||
|
),
|
||||||
|
Map[context.Context](func(s State) int { return s.ParsedValue }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of(10), res(context.Background()))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("error case", func(t *testing.T) {
|
||||||
|
res := F.Pipe2(
|
||||||
|
Do[context.Context](State{Value: -5}),
|
||||||
|
BindResultK[context.Context](
|
||||||
|
func(parsed int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.ParsedValue = parsed
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue,
|
||||||
|
),
|
||||||
|
Map[context.Context](func(s State) int { return s.ParsedValue }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.True(t, result.IsLeft(res(context.Background())))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindToReader(t *testing.T) {
|
||||||
|
type Env struct {
|
||||||
|
ConfigPath string
|
||||||
|
}
|
||||||
|
type State struct {
|
||||||
|
Config string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Reader that just reads from the environment
|
||||||
|
getConfigPath := func(env Env) string {
|
||||||
|
return env.ConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
res := F.Pipe2(
|
||||||
|
getConfigPath,
|
||||||
|
BindToReader[Env](func(path string) State {
|
||||||
|
return State{Config: path}
|
||||||
|
}),
|
||||||
|
Map[Env](func(s State) string { return s.Config }),
|
||||||
|
)
|
||||||
|
|
||||||
|
env := Env{ConfigPath: "/etc/config"}
|
||||||
|
assert.Equal(t, result.Of("/etc/config"), res(env))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindToResult(t *testing.T) {
|
||||||
|
type State struct {
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("success case", func(t *testing.T) {
|
||||||
|
parseResult := result.Of(42)
|
||||||
|
|
||||||
|
computation := F.Pipe2(
|
||||||
|
parseResult,
|
||||||
|
BindToResult[context.Context](func(value int) State {
|
||||||
|
return State{Value: value}
|
||||||
|
}),
|
||||||
|
Map[context.Context](func(s State) int { return s.Value }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of(42), computation(context.Background()))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("error case", func(t *testing.T) {
|
||||||
|
parseResult := result.Left[int](assert.AnError)
|
||||||
|
|
||||||
|
computation := F.Pipe2(
|
||||||
|
parseResult,
|
||||||
|
BindToResult[context.Context](func(value int) State {
|
||||||
|
return State{Value: value}
|
||||||
|
}),
|
||||||
|
Map[context.Context](func(s State) int { return s.Value }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.True(t, result.IsLeft(computation(context.Background())))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApReaderS(t *testing.T) {
|
||||||
|
type Env struct {
|
||||||
|
DefaultPort int
|
||||||
|
DefaultHost string
|
||||||
|
}
|
||||||
|
type State struct {
|
||||||
|
Port int
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultPort := func(env Env) int { return env.DefaultPort }
|
||||||
|
getDefaultHost := func(env Env) string { return env.DefaultHost }
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do[Env](State{}),
|
||||||
|
ApReaderS(
|
||||||
|
func(port int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Port = port
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getDefaultPort,
|
||||||
|
),
|
||||||
|
ApReaderS(
|
||||||
|
func(host string) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Host = host
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getDefaultHost,
|
||||||
|
),
|
||||||
|
Map[Env](func(s State) State { return s }),
|
||||||
|
)
|
||||||
|
|
||||||
|
env := Env{DefaultPort: 8080, DefaultHost: "localhost"}
|
||||||
|
r := res(env)
|
||||||
|
assert.True(t, result.IsRight(r))
|
||||||
|
state := result.GetOrElse(func(error) State { return State{} })(r)
|
||||||
|
assert.Equal(t, 8080, state.Port)
|
||||||
|
assert.Equal(t, "localhost", state.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApResultS(t *testing.T) {
|
||||||
|
type State struct {
|
||||||
|
Value1 int
|
||||||
|
Value2 int
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("success case", func(t *testing.T) {
|
||||||
|
parseValue1 := result.Of(42)
|
||||||
|
parseValue2 := result.Of(100)
|
||||||
|
|
||||||
|
computation := F.Pipe3(
|
||||||
|
Do[context.Context](State{}),
|
||||||
|
ApResultS[context.Context](
|
||||||
|
func(v int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Value1 = v
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue1,
|
||||||
|
),
|
||||||
|
ApResultS[context.Context](
|
||||||
|
func(v int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Value2 = v
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue2,
|
||||||
|
),
|
||||||
|
Map[context.Context](func(s State) State { return s }),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := computation(context.Background())
|
||||||
|
assert.True(t, result.IsRight(r))
|
||||||
|
state := result.GetOrElse(func(error) State { return State{} })(r)
|
||||||
|
assert.Equal(t, 42, state.Value1)
|
||||||
|
assert.Equal(t, 100, state.Value2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("error in first value", func(t *testing.T) {
|
||||||
|
parseValue1 := result.Left[int](assert.AnError)
|
||||||
|
parseValue2 := result.Of(100)
|
||||||
|
|
||||||
|
computation := F.Pipe3(
|
||||||
|
Do[context.Context](State{}),
|
||||||
|
ApResultS[context.Context](
|
||||||
|
func(v int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Value1 = v
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue1,
|
||||||
|
),
|
||||||
|
ApResultS[context.Context](
|
||||||
|
func(v int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Value2 = v
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue2,
|
||||||
|
),
|
||||||
|
Map[context.Context](func(s State) State { return s }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.True(t, result.IsLeft(computation(context.Background())))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("error in second value", func(t *testing.T) {
|
||||||
|
parseValue1 := result.Of(42)
|
||||||
|
parseValue2 := result.Left[int](assert.AnError)
|
||||||
|
|
||||||
|
computation := F.Pipe3(
|
||||||
|
Do[context.Context](State{}),
|
||||||
|
ApResultS[context.Context](
|
||||||
|
func(v int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Value1 = v
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue1,
|
||||||
|
),
|
||||||
|
ApResultS[context.Context](
|
||||||
|
func(v int) func(State) State {
|
||||||
|
return func(s State) State {
|
||||||
|
s.Value2 = v
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseValue2,
|
||||||
|
),
|
||||||
|
Map[context.Context](func(s State) State { return s }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.True(t, result.IsLeft(computation(context.Background())))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user