1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-25 22:21:49 +02:00

fix: ChainReaderK

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2025-11-22 10:39:56 +01:00
parent 4e9998b645
commit d8ab6b0ce5
2 changed files with 53 additions and 4 deletions

View File

@@ -365,13 +365,13 @@ func Asks[R, A any](r Reader[R, A]) ReaderResult[R, A] {
// //
// parseUser := func(data string) result.Result[User] { ... } // parseUser := func(data string) result.Result[User] { ... }
// result := readerresult.MonadChainEitherK(getUserDataRR, parseUser) // result := readerresult.MonadChainEitherK(getUserDataRR, parseUser)
func MonadChainEitherK[R, A, B any](ma ReaderResult[R, A], f result.Kleisli[A, B]) ReaderResult[R, B] { func MonadChainEitherK[R, A, B any](ma ReaderResult[R, A], f RES.Kleisli[A, B]) ReaderResult[R, B] {
return func(r R) (B, error) { return func(r R) (B, error) {
a, err := ma(r) a, err := ma(r)
if err != nil { if err != nil {
return result.Left[B](err) return result.Left[B](err)
} }
return f(a) return RES.Unwrap(f(a))
} }
} }
@@ -384,10 +384,40 @@ func MonadChainEitherK[R, A, B any](ma ReaderResult[R, A], f result.Kleisli[A, B
// result := F.Pipe1(getUserDataRR, readerresult.ChainEitherK[Config](parseUser)) // result := F.Pipe1(getUserDataRR, readerresult.ChainEitherK[Config](parseUser))
// //
//go:inline //go:inline
func ChainEitherK[R, A, B any](f result.Kleisli[A, B]) Operator[R, A, B] { func ChainEitherK[R, A, B any](f RES.Kleisli[A, B]) Operator[R, A, B] {
return function.Bind2nd(MonadChainEitherK[R, A, B], f) return function.Bind2nd(MonadChainEitherK[R, A, B], f)
} }
// MonadChainReaderK chains a ReaderResult with a function that returns a plain Result.
// This is useful for integrating functions that don't need environment access.
//
// Example:
//
// parseUser := func(data string) result.Result[User] { ... }
// result := readerresult.MonadChainReaderK(getUserDataRR, parseUser)
func MonadChainReaderK[R, A, B any](ma ReaderResult[R, A], f result.Kleisli[A, B]) ReaderResult[R, B] {
return func(r R) (B, error) {
a, err := ma(r)
if err != nil {
return result.Left[B](err)
}
return f(a)
}
}
// ChainReaderK is the curried version of MonadChainEitherK.
// It lifts a Result-returning function into a ReaderResult operator.
//
// Example:
//
// parseUser := func(data string) result.Result[User] { ... }
// result := F.Pipe1(getUserDataRR, readerresult.ChainReaderK[Config](parseUser))
//
//go:inline
func ChainReaderK[R, A, B any](f result.Kleisli[A, B]) Operator[R, A, B] {
return function.Bind2nd(MonadChainReaderK[R, A, B], f)
}
// ChainOptionK chains with a function that returns an Option, converting None to an error. // ChainOptionK chains with a function that returns an Option, converting None to an error.
// This is useful for integrating functions that return optional values. // This is useful for integrating functions that return optional values.
// //

View File

@@ -24,6 +24,7 @@ import (
"github.com/IBM/fp-go/v2/internal/utils" "github.com/IBM/fp-go/v2/internal/utils"
"github.com/IBM/fp-go/v2/reader" "github.com/IBM/fp-go/v2/reader"
"github.com/IBM/fp-go/v2/result" "github.com/IBM/fp-go/v2/result"
RES "github.com/IBM/fp-go/v2/result"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -271,7 +272,7 @@ func TestAsks(t *testing.T) {
assert.Equal(t, 7, v) assert.Equal(t, 7, v)
} }
func TestChainEitherK(t *testing.T) { func TestChainReaderK(t *testing.T) {
parseInt := func(s string) (int, error) { parseInt := func(s string) (int, error) {
if s == "42" { if s == "42" {
return 42, nil return 42, nil
@@ -279,6 +280,24 @@ func TestChainEitherK(t *testing.T) {
return 0, errors.New("parse error") return 0, errors.New("parse error")
} }
chain := ChainReaderK[MyContext](parseInt)
v, err := F.Pipe1(Of[MyContext]("42"), chain)(defaultContext)
assert.NoError(t, err)
assert.Equal(t, 42, v)
_, err = F.Pipe1(Of[MyContext]("invalid"), chain)(defaultContext)
assert.Error(t, err)
}
func TestChainEitherK(t *testing.T) {
parseInt := func(s string) RES.Result[int] {
if s == "42" {
return RES.Of(42)
}
return RES.Left[int](errors.New("parse error"))
}
chain := ChainEitherK[MyContext](parseInt) chain := ChainEitherK[MyContext](parseInt)
v, err := F.Pipe1(Of[MyContext]("42"), chain)(defaultContext) v, err := F.Pipe1(Of[MyContext]("42"), chain)(defaultContext)