mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-25 22:21:49 +02:00
fix: introcuce readeioresult
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -201,7 +201,7 @@ func Flatten[R, A any](mma Reader[R, Reader[R, A]]) Reader[R, A] {
|
|||||||
// getConfig := func(e Env) Config { return e.Config }
|
// getConfig := func(e Env) Config { return e.Config }
|
||||||
// getPort := func(c Config) int { return c.Port }
|
// getPort := func(c Config) int { return c.Port }
|
||||||
// getPortFromEnv := reader.Compose(getConfig)(getPort)
|
// getPortFromEnv := reader.Compose(getConfig)(getPort)
|
||||||
func Compose[R, B, C any](ab Reader[R, B]) func(Reader[B, C]) Reader[R, C] {
|
func Compose[C, R, B any](ab Reader[R, B]) func(Reader[B, C]) Reader[R, C] {
|
||||||
return func(bc Reader[B, C]) Reader[R, C] {
|
return func(bc Reader[B, C]) Reader[R, C] {
|
||||||
return function.Flow2(ab, bc)
|
return function.Flow2(ab, bc)
|
||||||
}
|
}
|
||||||
@@ -265,8 +265,8 @@ func Promap[E, A, D, B any](f func(D) E, g func(A) B) func(Reader[E, A]) Reader[
|
|||||||
// simplify := func(d DetailedConfig) SimpleConfig { return SimpleConfig{Host: d.Host} }
|
// simplify := func(d DetailedConfig) SimpleConfig { return SimpleConfig{Host: d.Host} }
|
||||||
// r := reader.Local(simplify)(getHost)
|
// r := reader.Local(simplify)(getHost)
|
||||||
// result := r(DetailedConfig{Host: "localhost", Port: 8080}) // "localhost"
|
// result := r(DetailedConfig{Host: "localhost", Port: 8080}) // "localhost"
|
||||||
func Local[R2, R1, A any](f func(R2) R1) func(Reader[R1, A]) Reader[R2, A] {
|
func Local[A, R2, R1 any](f func(R2) R1) func(Reader[R1, A]) Reader[R2, A] {
|
||||||
return Compose[R2, R1, A](f)
|
return Compose[A, R2, R1](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read applies a context to a Reader to obtain its value.
|
// Read applies a context to a Reader to obtain its value.
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func TestCompose(t *testing.T) {
|
|||||||
env := Env{Config: Config{Port: 8080}}
|
env := Env{Config: Config{Port: 8080}}
|
||||||
getConfig := func(e Env) Config { return e.Config }
|
getConfig := func(e Env) Config { return e.Config }
|
||||||
getPort := func(c Config) int { return c.Port }
|
getPort := func(c Config) int { return c.Port }
|
||||||
getPortFromEnv := Compose[Env, Config, int](getConfig)(getPort)
|
getPortFromEnv := Compose[int](getConfig)(getPort)
|
||||||
result := getPortFromEnv(env)
|
result := getPortFromEnv(env)
|
||||||
assert.Equal(t, 8080, result)
|
assert.Equal(t, 8080, result)
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ func TestLocal(t *testing.T) {
|
|||||||
detailed := DetailedConfig{Host: "localhost", Port: 8080}
|
detailed := DetailedConfig{Host: "localhost", Port: 8080}
|
||||||
getHost := func(c SimpleConfig) string { return c.Host }
|
getHost := func(c SimpleConfig) string { return c.Host }
|
||||||
simplify := func(d DetailedConfig) SimpleConfig { return SimpleConfig{Host: d.Host} }
|
simplify := func(d DetailedConfig) SimpleConfig { return SimpleConfig{Host: d.Host} }
|
||||||
r := Local[DetailedConfig, SimpleConfig, string](simplify)(getHost)
|
r := Local[string](simplify)(getHost)
|
||||||
result := r(detailed)
|
result := r(detailed)
|
||||||
assert.Equal(t, "localhost", result)
|
assert.Equal(t, "localhost", result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ func BiMap[E, E1, E2, A, B any](f func(E1) E2, g func(A) B) func(ReaderEither[E,
|
|||||||
// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s
|
// Local changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s
|
||||||
// `contramap`).
|
// `contramap`).
|
||||||
func Local[E, A, R2, R1 any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A] {
|
func Local[E, A, R2, R1 any](f func(R2) R1) func(ReaderEither[R1, E, A]) ReaderEither[R2, E, A] {
|
||||||
return reader.Local[R2, R1, Either[E, A]](f)
|
return reader.Local[Either[E, A], R2, R1](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read applies a context to a reader to obtain its value
|
// Read applies a context to a reader to obtain its value
|
||||||
|
|||||||
@@ -626,6 +626,6 @@ func MapLeft[R, A, E1, E2 any](f func(E1) E2) func(ReaderIOEither[R, E1, A]) Rea
|
|||||||
// This is similar to Contravariant's contramap operation.
|
// This is similar to Contravariant's contramap operation.
|
||||||
//
|
//
|
||||||
//go:inline
|
//go:inline
|
||||||
func Local[R1, R2, E, A any](f func(R2) R1) func(ReaderIOEither[R1, E, A]) ReaderIOEither[R2, E, A] {
|
func Local[E, A, R1, R2 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[IOEither[E, A], R2, R1](f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -603,7 +603,7 @@ func TestLocal(t *testing.T) {
|
|||||||
ctx2 := testContext{value: 20}
|
ctx2 := testContext{value: 20}
|
||||||
|
|
||||||
rdr := Asks[error](func(c testContext) int { return c.value })
|
rdr := Asks[error](func(c testContext) int { return c.value })
|
||||||
result := Local[testContext, testContext, error, int](func(c testContext) testContext {
|
result := Local[error, int](func(c testContext) testContext {
|
||||||
return testContext{value: c.value * 2}
|
return testContext{value: c.value * 2}
|
||||||
})(rdr)
|
})(rdr)
|
||||||
|
|
||||||
@@ -755,7 +755,7 @@ func TestTraverseArrayWithIndex(t *testing.T) {
|
|||||||
|
|
||||||
func TestTraverseRecord(t *testing.T) {
|
func TestTraverseRecord(t *testing.T) {
|
||||||
ctx := testContext{value: 10}
|
ctx := testContext{value: 10}
|
||||||
result := TraverseRecord[testContext, string, error](func(x int) ReaderIOEither[testContext, error, int] {
|
result := TraverseRecord[string, testContext, error](func(x int) ReaderIOEither[testContext, error, int] {
|
||||||
return Of[testContext, error](x * 2)
|
return Of[testContext, error](x * 2)
|
||||||
})(map[string]int{"a": 1, "b": 2})
|
})(map[string]int{"a": 1, "b": 2})
|
||||||
|
|
||||||
@@ -765,7 +765,7 @@ func TestTraverseRecord(t *testing.T) {
|
|||||||
|
|
||||||
func TestTraverseRecordWithIndex(t *testing.T) {
|
func TestTraverseRecordWithIndex(t *testing.T) {
|
||||||
ctx := testContext{value: 10}
|
ctx := testContext{value: 10}
|
||||||
result := TraverseRecordWithIndex[testContext, string, error](func(k string, x int) ReaderIOEither[testContext, error, string] {
|
result := TraverseRecordWithIndex[string, testContext, error](func(k string, x int) ReaderIOEither[testContext, error, string] {
|
||||||
return Of[testContext, error](fmt.Sprintf("%s:%d", k, x))
|
return Of[testContext, error](fmt.Sprintf("%s:%d", k, x))
|
||||||
})(map[string]int{"a": 1, "b": 2})
|
})(map[string]int{"a": 1, "b": 2})
|
||||||
|
|
||||||
@@ -775,7 +775,7 @@ func TestTraverseRecordWithIndex(t *testing.T) {
|
|||||||
|
|
||||||
func TestSequenceRecord(t *testing.T) {
|
func TestSequenceRecord(t *testing.T) {
|
||||||
ctx := testContext{value: 10}
|
ctx := testContext{value: 10}
|
||||||
result := SequenceRecord[testContext, string, error](map[string]ReaderIOEither[testContext, error, int]{
|
result := SequenceRecord[string, testContext, error](map[string]ReaderIOEither[testContext, error, int]{
|
||||||
"a": Of[testContext, error](1),
|
"a": Of[testContext, error](1),
|
||||||
"b": Of[testContext, error](2),
|
"b": Of[testContext, error](2),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ import (
|
|||||||
// // result(cfg)() returns Right([user1, user2, user3]) or Left(error)
|
// // result(cfg)() returns Right([user1, user2, user3]) or Left(error)
|
||||||
//
|
//
|
||||||
//go:inline
|
//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 Kleisli[R, E, A, B]) Kleisli[R, E, []A, []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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ func SequenceArray[R, E, A any](ma []ReaderIOEither[R, E, A]) ReaderIOEither[R,
|
|||||||
// result := enrichUsers(map[string]User{"alice": user1, "bob": user2})
|
// result := enrichUsers(map[string]User{"alice": user1, "bob": user2})
|
||||||
//
|
//
|
||||||
//go:inline
|
//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[K comparable, R, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ func TraverseRecord[R any, K comparable, E, A, B any](f func(A) ReaderIOEither[R
|
|||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
//go:inline
|
//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[K comparable, R, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +201,6 @@ func TraverseRecordWithIndex[R any, K comparable, E, A, B any](f func(K, A) Read
|
|||||||
// // 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
|
//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[K comparable, R, 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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ type (
|
|||||||
// }
|
// }
|
||||||
ReaderIOEither[R, E, A any] = Reader[R, IOEither[E, A]]
|
ReaderIOEither[R, E, A any] = Reader[R, IOEither[E, A]]
|
||||||
|
|
||||||
|
Kleisli[R, E, A, B any] = reader.Reader[A, ReaderIOEither[R, E, B]]
|
||||||
|
|
||||||
// Operator represents a transformation from one ReaderIOEither to another.
|
// Operator represents a transformation from one ReaderIOEither to another.
|
||||||
// It's a Reader that takes a ReaderIOEither[R, E, A] and produces a ReaderIOEither[R, E, B].
|
// It's a Reader that takes a ReaderIOEither[R, E, A] and produces a ReaderIOEither[R, E, B].
|
||||||
// This type is commonly used for composing operations in a point-free style.
|
// This type is commonly used for composing operations in a point-free style.
|
||||||
@@ -78,5 +80,5 @@ type (
|
|||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
// var doubleOp Operator[Config, error, int, int] = Map(func(x int) int { return x * 2 })
|
// var doubleOp Operator[Config, error, int, int] = Map(func(x int) int { return x * 2 })
|
||||||
Operator[R, E, A, B any] = reader.Reader[ReaderIOEither[R, E, A], ReaderIOEither[R, E, B]]
|
Operator[R, E, A, B any] = Kleisli[R, E, ReaderIOEither[R, E, A], B]
|
||||||
)
|
)
|
||||||
|
|||||||
75
v2/readerioresult/array_test.go
Normal file
75
v2/readerioresult/array_test.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
A "github.com/IBM/fp-go/v2/array"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
TST "github.com/IBM/fp-go/v2/internal/testing"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTraverseArray(t *testing.T) {
|
||||||
|
|
||||||
|
e := errors.New("e")
|
||||||
|
|
||||||
|
f := TraverseArray(func(a string) ReaderIOResult[context.Context, string] {
|
||||||
|
if len(a) > 0 {
|
||||||
|
return Right[context.Context](a + a)
|
||||||
|
}
|
||||||
|
return Left[context.Context, string](e)
|
||||||
|
})
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, result.Of(A.Empty[string]()), F.Pipe1(A.Empty[string](), f)(ctx)())
|
||||||
|
assert.Equal(t, result.Of([]string{"aa", "bb"}), F.Pipe1([]string{"a", "b"}, f)(ctx)())
|
||||||
|
assert.Equal(t, result.Left[[]string](e), F.Pipe1([]string{"a", ""}, f)(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceArray(t *testing.T) {
|
||||||
|
|
||||||
|
s := TST.SequenceArrayTest(
|
||||||
|
FromStrictEquals[context.Context, bool]()(context.Background()),
|
||||||
|
Pointed[context.Context, string](),
|
||||||
|
Pointed[context.Context, bool](),
|
||||||
|
Functor[context.Context, []string, bool](),
|
||||||
|
SequenceArray[context.Context, string],
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
t.Run(fmt.Sprintf("TestSequenceArray %d", i), s(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceArrayError(t *testing.T) {
|
||||||
|
|
||||||
|
s := TST.SequenceArrayErrorTest(
|
||||||
|
FromStrictEquals[context.Context, bool]()(context.Background()),
|
||||||
|
Left[context.Context],
|
||||||
|
Left[context.Context, bool],
|
||||||
|
Pointed[context.Context, string](),
|
||||||
|
Pointed[context.Context, bool](),
|
||||||
|
Functor[context.Context, []string, bool](),
|
||||||
|
SequenceArray[context.Context, string],
|
||||||
|
)
|
||||||
|
// run across four bits
|
||||||
|
s(4)(t)
|
||||||
|
}
|
||||||
322
v2/readerioresult/bind.go
Normal file
322
v2/readerioresult/bind.go
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
L "github.com/IBM/fp-go/v2/optics/lens"
|
||||||
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Do creates an empty context of type [S] to be used with the [Bind] operation.
|
||||||
|
// This is the starting point for do-notation style composition.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// User User
|
||||||
|
// Posts []Post
|
||||||
|
// }
|
||||||
|
// type Env struct {
|
||||||
|
// UserRepo UserRepository
|
||||||
|
// PostRepo PostRepository
|
||||||
|
// }
|
||||||
|
// result := readerioeither.Do[Env, error](State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Do[R, S any](
|
||||||
|
empty S,
|
||||||
|
) ReaderIOResult[R, S] {
|
||||||
|
return RIOE.Do[R, error](empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind attaches the result of a computation to a context [S1] to produce a context [S2].
|
||||||
|
// This enables sequential composition where each step can depend on the results of previous steps
|
||||||
|
// and access the shared environment.
|
||||||
|
//
|
||||||
|
// The setter function takes the result of the computation and returns a function that
|
||||||
|
// updates the context from S1 to S2.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// User User
|
||||||
|
// Posts []Post
|
||||||
|
// }
|
||||||
|
// type Env struct {
|
||||||
|
// UserRepo UserRepository
|
||||||
|
// PostRepo PostRepository
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do[Env, error](State{}),
|
||||||
|
// readerioeither.Bind(
|
||||||
|
// func(user User) func(State) State {
|
||||||
|
// return func(s State) State { s.User = user; return s }
|
||||||
|
// },
|
||||||
|
// func(s State) readerioeither.ReaderIOResult[Env, error, User] {
|
||||||
|
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||||
|
// return env.UserRepo.FindUser()
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// readerioeither.Bind(
|
||||||
|
// func(posts []Post) func(State) State {
|
||||||
|
// return func(s State) State { s.Posts = posts; return s }
|
||||||
|
// },
|
||||||
|
// func(s State) readerioeither.ReaderIOResult[Env, error, []Post] {
|
||||||
|
// // This can access s.User from the previous step
|
||||||
|
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, []Post] {
|
||||||
|
// return env.PostRepo.FindPostsByUser(s.User.ID)
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Bind[R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f Kleisli[R, S1, T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return RIOE.Bind(setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Let[R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
f func(S1) T,
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return RIOE.Let[R, error](setter, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LetTo attaches the a value to a context [S1] to produce a context [S2]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LetTo[R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
b T,
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return RIOE.LetTo[R, error](setter, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindTo initializes a new state [S1] from a value [T]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindTo[R, S1, T any](
|
||||||
|
setter func(T) S1,
|
||||||
|
) Operator[R, T, S1] {
|
||||||
|
return RIOE.BindTo[R, error](setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApS attaches a value to a context [S1] to produce a context [S2] by considering
|
||||||
|
// the context and the value concurrently (using Applicative rather than Monad).
|
||||||
|
// This allows independent computations to be combined without one depending on the result of the other.
|
||||||
|
//
|
||||||
|
// Unlike Bind, which sequences operations, ApS can be used when operations are independent
|
||||||
|
// and can conceptually run in parallel.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// type State struct {
|
||||||
|
// User User
|
||||||
|
// Posts []Post
|
||||||
|
// }
|
||||||
|
// type Env struct {
|
||||||
|
// UserRepo UserRepository
|
||||||
|
// PostRepo PostRepository
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // These operations are independent and can be combined with ApS
|
||||||
|
// getUser := readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||||
|
// return env.UserRepo.FindUser()
|
||||||
|
// })
|
||||||
|
// getPosts := readerioeither.Asks(func(env Env) ioeither.IOEither[error, []Post] {
|
||||||
|
// return env.PostRepo.FindPosts()
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// result := F.Pipe2(
|
||||||
|
// readerioeither.Do[Env, error](State{}),
|
||||||
|
// readerioeither.ApS(
|
||||||
|
// func(user User) func(State) State {
|
||||||
|
// return func(s State) State { s.User = user; return s }
|
||||||
|
// },
|
||||||
|
// getUser,
|
||||||
|
// ),
|
||||||
|
// readerioeither.ApS(
|
||||||
|
// func(posts []Post) func(State) State {
|
||||||
|
// return func(s State) State { s.Posts = posts; return s }
|
||||||
|
// },
|
||||||
|
// getPosts,
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ApS[R, S1, S2, T any](
|
||||||
|
setter func(T) func(S1) S2,
|
||||||
|
fa ReaderIOResult[R, T],
|
||||||
|
) Operator[R, S1, S2] {
|
||||||
|
return RIOE.ApS(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, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
fa ReaderIOResult[R, T],
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return RIOE.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 ReaderIOResult 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.ReaderIOResult[Env, error, User] {
|
||||||
|
// return readerioeither.Asks(func(env Env) ioeither.IOEither[error, User] {
|
||||||
|
// return env.UserRepo.FindUser()
|
||||||
|
// })
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BindL[R, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f Kleisli[R, T, T],
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return RIOE.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 ReaderIOResult).
|
||||||
|
//
|
||||||
|
// 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, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
f func(T) T,
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return RIOE.LetL[R, error](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 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, S, T any](
|
||||||
|
lens L.Lens[S, T],
|
||||||
|
b T,
|
||||||
|
) Operator[R, S, S] {
|
||||||
|
return RIOE.LetToL[R, error](lens, b)
|
||||||
|
}
|
||||||
58
v2/readerioresult/bind_test.go
Normal file
58
v2/readerioresult/bind_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLastName(s utils.Initial) ReaderIOResult[context.Context, string] {
|
||||||
|
return Of[context.Context]("Doe")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGivenName(s utils.WithLastName) ReaderIOResult[context.Context, string] {
|
||||||
|
return Of[context.Context]("John")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBind(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do[context.Context](utils.Empty),
|
||||||
|
Bind(utils.SetLastName, getLastName),
|
||||||
|
Bind(utils.SetGivenName, getGivenName),
|
||||||
|
Map[context.Context](utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background())(), result.Of("John Doe"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApS(t *testing.T) {
|
||||||
|
|
||||||
|
res := F.Pipe3(
|
||||||
|
Do[context.Context](utils.Empty),
|
||||||
|
ApS(utils.SetLastName, Of[context.Context]("Doe")),
|
||||||
|
ApS(utils.SetGivenName, Of[context.Context]("John")),
|
||||||
|
Map[context.Context](utils.GetFullName),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, res(context.Background())(), result.Of("John Doe"))
|
||||||
|
}
|
||||||
33
v2/readerioresult/bracket.go
Normal file
33
v2/readerioresult/bracket.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Bracket[
|
||||||
|
R, A, B, ANY any](
|
||||||
|
acquire ReaderIOResult[R, A],
|
||||||
|
use Kleisli[R, A, B],
|
||||||
|
release func(A, Result[B]) ReaderIOResult[R, ANY],
|
||||||
|
) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.Bracket(acquire, use, release)
|
||||||
|
}
|
||||||
127
v2/readerioresult/coverage.out
Normal file
127
v2/readerioresult/coverage.out
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
mode: set
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/bind.go:26.27,28.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/bind.go:34.26,36.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/bind.go:42.26,44.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/bind.go:50.26,52.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/bind.go:57.25,59.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/bind.go:65.26,67.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/bracket.go:31.27,33.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/eq.go:25.92,27.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/eq.go:30.88,32.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:14.92,16.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:20.90,22.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:26.92,28.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:32.102,34.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:38.100,40.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:44.102,46.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:50.114,52.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:56.112,58.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:62.114,64.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:68.126,70.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:74.124,76.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:80.126,82.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:86.138,88.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:92.136,94.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:98.138,100.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:104.150,106.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:110.148,112.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:116.150,118.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:122.162,124.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:128.160,130.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:134.162,136.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:140.174,142.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:146.172,148.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:152.174,154.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:158.186,160.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:164.184,166.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:170.186,172.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:176.198,178.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:182.196,184.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:188.198,190.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:194.211,196.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:200.209,202.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/gen.go:206.211,208.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/monad.go:26.73,28.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/monad.go:31.104,33.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/monad.go:36.131,38.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:40.92,46.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:50.90,52.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:55.76,60.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:63.75,68.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:73.96,75.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:79.60,81.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:85.90,87.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:91.54,93.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:98.120,104.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:108.125,114.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:118.123,125.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:129.87,135.2 1 0
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:139.128,147.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:151.92,158.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:162.116,169.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:173.80,179.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:183.124,190.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:194.88,200.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:204.108,211.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:215.72,221.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:225.113,233.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:237.77,244.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:248.99,254.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:258.119,265.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:268.122,275.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:278.122,285.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:289.119,291.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:295.84,300.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:304.89,309.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:312.54,314.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:317.53,319.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:323.59,325.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:329.51,331.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:335.102,337.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:341.77,343.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:346.72,348.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:351.71,353.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:357.71,359.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:362.64,364.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:367.63,369.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:373.63,375.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:379.79,381.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:385.89,387.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:391.46,393.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:397.64,399.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:403.89,405.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:409.103,411.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:415.135,417.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:421.105,423.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:427.129,429.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:433.120,440.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:444.120,449.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:453.117,455.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:459.77,461.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:465.86,467.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:471.104,472.34 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:472.34,474.3 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:479.123,487.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:491.84,498.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:503.68,505.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:509.98,511.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:515.94,517.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:520.106,522.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:526.103,528.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/reader.go:533.101,535.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/resource.go:21.181,22.73 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/resource.go:22.73,23.44 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/resource.go:23.44,27.41 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/resource.go:27.41,29.6 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/resource.go:30.40,32.5 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/sequence.go:25.91,30.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/sequence.go:32.124,38.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/sequence.go:40.157,47.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/sequence.go:49.190,57.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/sync.go:26.81,28.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/traverse.go:23.107,25.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/traverse.go:28.121,30.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/traverse.go:33.89,35.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/traverse.go:38.134,40.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/traverse.go:43.146,45.2 1 1
|
||||||
|
github.com/IBM/fp-go/v2/readerioeither/traverse.go:48.116,50.2 1 1
|
||||||
100
v2/readerioresult/doc.go
Normal file
100
v2/readerioresult/doc.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// 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 readerioresult provides a functional programming abstraction that combines
|
||||||
|
// three powerful concepts: Reader, IO, and Either monads.
|
||||||
|
//
|
||||||
|
// # ReaderIOResult
|
||||||
|
//
|
||||||
|
// ReaderIOResult[R, A] represents a computation that:
|
||||||
|
// - Depends on some context/environment of type R (Reader)
|
||||||
|
// - Performs side effects (IO)
|
||||||
|
// - Can fail with an error of type E or succeed with a value of type A (Either)
|
||||||
|
//
|
||||||
|
// This is particularly useful for:
|
||||||
|
// - Dependency injection patterns
|
||||||
|
// - Error handling in effectful computations
|
||||||
|
// - Composing operations that need access to shared configuration or context
|
||||||
|
//
|
||||||
|
// # Core Operations
|
||||||
|
//
|
||||||
|
// Construction:
|
||||||
|
// - Of/Right: Create a successful computation
|
||||||
|
// - Left/ThrowError: Create a failed computation
|
||||||
|
// - FromEither: Lift an Either into ReaderIOResult
|
||||||
|
// - FromIO: Lift an IO into ReaderIOResult
|
||||||
|
// - FromReader: Lift a Reader into ReaderIOResult
|
||||||
|
// - FromIOEither: Lift an IOEither into ReaderIOResult
|
||||||
|
// - TryCatch: Wrap error-returning functions
|
||||||
|
//
|
||||||
|
// Transformation:
|
||||||
|
// - Map: Transform the success value
|
||||||
|
// - MapLeft: Transform the error value
|
||||||
|
// - BiMap: Transform both success and error values
|
||||||
|
// - Chain/Bind: Sequence dependent computations
|
||||||
|
// - Flatten: Flatten nested ReaderIOResult
|
||||||
|
//
|
||||||
|
// Combination:
|
||||||
|
// - Ap: Apply a function in a context to a value in a context
|
||||||
|
// - SequenceArray: Convert array of ReaderIOResult to ReaderIOResult of array
|
||||||
|
// - TraverseArray: Map and sequence in one operation
|
||||||
|
//
|
||||||
|
// Error Handling:
|
||||||
|
// - Fold: Handle both success and error cases
|
||||||
|
// - GetOrElse: Provide a default value on error
|
||||||
|
// - OrElse: Try an alternative computation on error
|
||||||
|
// - Alt: Choose the first successful computation
|
||||||
|
//
|
||||||
|
// Context Access:
|
||||||
|
// - Ask: Get the current context
|
||||||
|
// - Asks: Get a value derived from the context
|
||||||
|
// - Local: Run a computation with a modified context
|
||||||
|
//
|
||||||
|
// Resource Management:
|
||||||
|
// - Bracket: Ensure resource cleanup
|
||||||
|
// - WithResource: Manage resource lifecycle
|
||||||
|
//
|
||||||
|
// # Example Usage
|
||||||
|
//
|
||||||
|
// type Config struct {
|
||||||
|
// BaseURL string
|
||||||
|
// Timeout time.Duration
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // A computation that depends on Config, performs IO, and can fail
|
||||||
|
// func fetchUser(id int) readerioeither.ReaderIOResult[Config, error, User] {
|
||||||
|
// return func(cfg Config) ioeither.IOEither[error, User] {
|
||||||
|
// return func() either.Either[error, User] {
|
||||||
|
// // Use cfg.BaseURL and cfg.Timeout to fetch user
|
||||||
|
// // Return either.Right(user) or either.Left(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Compose operations
|
||||||
|
// result := function.Pipe2(
|
||||||
|
// fetchUser(123),
|
||||||
|
// readerioeither.Map[Config, error](func(u User) string { return u.Name }),
|
||||||
|
// readerioeither.Chain[Config, error](func(name string) readerioeither.ReaderIOResult[Config, error, string] {
|
||||||
|
// return readerioeither.Of[Config, error]("Hello, " + name)
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// // Execute with config
|
||||||
|
// config := Config{BaseURL: "https://api.example.com", Timeout: 30 * time.Second}
|
||||||
|
// outcome := result(config)() // Returns either.Either[error, string]
|
||||||
|
package readerioresult
|
||||||
|
|
||||||
|
//go:generate go run .. readerioeither --count 10 --filename gen.go
|
||||||
35
v2/readerioresult/eq.go
Normal file
35
v2/readerioresult/eq.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/eq"
|
||||||
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Eq implements the equals predicate for values contained in the IOEither monad
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Eq[R, A any](eq eq.Eq[Result[A]]) func(R) eq.Eq[ReaderIOResult[R, A]] {
|
||||||
|
return RIOE.Eq[R](eq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStrictEquals constructs an [EQ.Eq] from the canonical comparison function
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromStrictEquals[R any, A comparable]() func(R) eq.Eq[ReaderIOResult[R, A]] {
|
||||||
|
return RIOE.FromStrictEquals[R, error, A]()
|
||||||
|
}
|
||||||
273
v2/readerioresult/gen.go
Normal file
273
v2/readerioresult/gen.go
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
|
// This file was generated by robots at
|
||||||
|
// 2025-03-09 23:53:13.769132 +0100 CET m=+0.013697001
|
||||||
|
|
||||||
|
package readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
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 [ReaderIOResult[R]]
|
||||||
|
// 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() ReaderIOResult[C, R] {
|
||||||
|
return G.From0[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize0 converts a function with 1 parameters returning a tuple into a function with 0 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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() ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize0[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize0 converts a function with 1 parameters returning a [ReaderIOResult[C, R]] into a function with 0 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize0[F ~func() ReaderIOResult[C, R], C, R any](f F) func(C) (R, error) {
|
||||||
|
return G.Uneitherize0[ReaderIOResult[C, R], func(C) (R, error)](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From1[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize1 converts a function with 2 parameters returning a tuple into a function with 1 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize1[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize1 converts a function with 2 parameters returning a [ReaderIOResult[C, R]] into a function with 1 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize1[F ~func(T0) ReaderIOResult[C, R], T0, C, R any](f F) func(C, T0) (R, error) {
|
||||||
|
return G.Uneitherize1[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From2[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize2 converts a function with 3 parameters returning a tuple into a function with 2 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize2[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize2 converts a function with 3 parameters returning a [ReaderIOResult[C, R]] into a function with 2 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize2[F ~func(T0, T1) ReaderIOResult[C, R], T0, T1, C, R any](f F) func(C, T0, T1) (R, error) {
|
||||||
|
return G.Uneitherize2[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From3[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize3 converts a function with 4 parameters returning a tuple into a function with 3 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize3[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize3 converts a function with 4 parameters returning a [ReaderIOResult[C, R]] into a function with 3 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize3[F ~func(T0, T1, T2) ReaderIOResult[C, R], T0, T1, T2, C, R any](f F) func(C, T0, T1, T2) (R, error) {
|
||||||
|
return G.Uneitherize3[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From4[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize4 converts a function with 5 parameters returning a tuple into a function with 4 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize4[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize4 converts a function with 5 parameters returning a [ReaderIOResult[C, R]] into a function with 4 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize4[F ~func(T0, T1, T2, T3) ReaderIOResult[C, R], T0, T1, T2, T3, C, R any](f F) func(C, T0, T1, T2, T3) (R, error) {
|
||||||
|
return G.Uneitherize4[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From5[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize5 converts a function with 6 parameters returning a tuple into a function with 5 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize5[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize5 converts a function with 6 parameters returning a [ReaderIOResult[C, R]] into a function with 5 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize5[F ~func(T0, T1, T2, T3, T4) ReaderIOResult[C, R], T0, T1, T2, T3, T4, C, R any](f F) func(C, T0, T1, T2, T3, T4) (R, error) {
|
||||||
|
return G.Uneitherize5[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From6[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize6 converts a function with 7 parameters returning a tuple into a function with 6 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize6[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize6 converts a function with 7 parameters returning a [ReaderIOResult[C, R]] into a function with 6 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize6[F ~func(T0, T1, T2, T3, T4, T5) ReaderIOResult[C, 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[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From7[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize7 converts a function with 8 parameters returning a tuple into a function with 7 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize7[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize7 converts a function with 8 parameters returning a [ReaderIOResult[C, R]] into a function with 7 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize7[F ~func(T0, T1, T2, T3, T4, T5, T6) ReaderIOResult[C, 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[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From8[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize8 converts a function with 9 parameters returning a tuple into a function with 8 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize8[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize8 converts a function with 9 parameters returning a [ReaderIOResult[C, R]] into a function with 8 parameters returning a tuple.
|
||||||
|
// The first parameter is considered to be the context [C].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Uneitherize8[F ~func(T0, T1, T2, T3, T4, T5, T6, T7) ReaderIOResult[C, 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[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From9[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize9 converts a function with 10 parameters returning a tuple into a function with 9 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize9[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize9 converts a function with 10 parameters returning a [ReaderIOResult[C, R]] into a function with 9 parameters returning a tuple.
|
||||||
|
// 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) ReaderIOResult[C, 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[ReaderIOResult[C, 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 [ReaderIOResult[R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.From10[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eitherize10 converts a function with 11 parameters returning a tuple into a function with 10 parameters returning a [ReaderIOResult[C, R]]
|
||||||
|
// 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) ReaderIOResult[C, R] {
|
||||||
|
return G.Eitherize10[ReaderIOResult[C, R]](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uneitherize10 converts a function with 11 parameters returning a [ReaderIOResult[C, R]] into a function with 10 parameters returning a tuple.
|
||||||
|
// 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) ReaderIOResult[C, 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[ReaderIOResult[C, R], func(C, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9) (R, error)](f)
|
||||||
|
}
|
||||||
164
v2/readerioresult/gen_test.go
Normal file
164
v2/readerioresult/gen_test.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEitherize0(t *testing.T) {
|
||||||
|
f := func(ctx context.Context) (int, error) {
|
||||||
|
return ctx.Value("key").(int), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Eitherize0(f)()
|
||||||
|
ctx := context.WithValue(context.Background(), "key", 42)
|
||||||
|
assert.Equal(t, E.Right[error](42), result(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEitherize1(t *testing.T) {
|
||||||
|
f := func(ctx context.Context, x int) (int, error) {
|
||||||
|
return x * 2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Eitherize1(f)(5)
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, E.Right[error](10), result(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEitherize2(t *testing.T) {
|
||||||
|
f := func(ctx context.Context, x, y int) (int, error) {
|
||||||
|
return x + y, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Eitherize2(f)(5, 3)
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, E.Right[error](8), result(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEitherize3(t *testing.T) {
|
||||||
|
f := func(ctx context.Context, x, y, z int) (int, error) {
|
||||||
|
return x + y + z, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Eitherize3(f)(5, 3, 2)
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, E.Right[error](10), result(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUneitherize0(t *testing.T) {
|
||||||
|
f := func() ReaderIOResult[context.Context, int] {
|
||||||
|
return Of[context.Context](42)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Uneitherize0(f)
|
||||||
|
ctx := context.Background()
|
||||||
|
val, err := result(ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 42, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUneitherize1(t *testing.T) {
|
||||||
|
f := func(x int) ReaderIOResult[context.Context, int] {
|
||||||
|
return Of[context.Context](x * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Uneitherize1(f)
|
||||||
|
ctx := context.Background()
|
||||||
|
val, err := result(ctx, 5)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 10, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUneitherize2(t *testing.T) {
|
||||||
|
f := func(x, y int) ReaderIOResult[context.Context, int] {
|
||||||
|
return Of[context.Context](x + y)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Uneitherize2(f)
|
||||||
|
ctx := context.Background()
|
||||||
|
val, err := result(ctx, 5, 3)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 8, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrom0(t *testing.T) {
|
||||||
|
f := func(ctx context.Context) func() (int, error) {
|
||||||
|
return func() (int, error) {
|
||||||
|
return 42, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := From0(f)()
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, E.Right[error](42), result(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrom1(t *testing.T) {
|
||||||
|
f := func(ctx context.Context, x int) func() (int, error) {
|
||||||
|
return func() (int, error) {
|
||||||
|
return x * 2, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := From1(f)(5)
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, E.Right[error](10), result(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrom2(t *testing.T) {
|
||||||
|
f := func(ctx context.Context, x, y int) func() (int, error) {
|
||||||
|
return func() (int, error) {
|
||||||
|
return x + y, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := From2(f)(5, 3)
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, E.Right[error](8), result(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEitherizeWithError(t *testing.T) {
|
||||||
|
f := func(ctx context.Context, x int) (int, error) {
|
||||||
|
if x < 0 {
|
||||||
|
return 0, errors.New("negative value")
|
||||||
|
}
|
||||||
|
return x * 2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Eitherize1(f)(-5)
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.True(t, E.IsLeft(result(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUneitherizeWithError(t *testing.T) {
|
||||||
|
f := func(x int) ReaderIOResult[context.Context, int] {
|
||||||
|
if x < 0 {
|
||||||
|
return Left[context.Context, int](errors.New("negative value"))
|
||||||
|
}
|
||||||
|
return Of[context.Context](x * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := Uneitherize1(f)
|
||||||
|
ctx := context.Background()
|
||||||
|
_, err := result(ctx, -5)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
44
v2/readerioresult/monad.go
Normal file
44
v2/readerioresult/monad.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) 2024 - 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/internal/functor"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/monad"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/pointed"
|
||||||
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pointed returns the pointed operations for [ReaderIOResult]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Pointed[R, A any]() pointed.Pointed[A, ReaderIOResult[R, A]] {
|
||||||
|
return RIOE.Pointed[R, error, A]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functor returns the functor operations for [ReaderIOResult]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Functor[R, A, B any]() functor.Functor[A, B, ReaderIOResult[R, A], ReaderIOResult[R, B]] {
|
||||||
|
return RIOE.Functor[R, error, A, B]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monad returns the monadic operations for [ReaderIOResult]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Monad[R, A, B any]() monad.Monad[A, B, ReaderIOResult[R, A], ReaderIOResult[R, B], ReaderIOResult[R, func(A) B]] {
|
||||||
|
return RIOE.Monad[R, error, A, B]()
|
||||||
|
}
|
||||||
586
v2/readerioresult/reader.go
Normal file
586
v2/readerioresult/reader.go
Normal file
@@ -0,0 +1,586 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
RE "github.com/IBM/fp-go/v2/readereither"
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftReaderIO lifts a ReaderIO into a ReaderIOResult, placing the result in the Left (error) side.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LeftReaderIO[A, R any](me ReaderIO[R, error]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.LeftReaderIO[A](me)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMap applies a function to the value inside a ReaderIOResult context.
|
||||||
|
// If the computation is successful (Right), the function is applied to the value.
|
||||||
|
// If it's an error (Left), the error is propagated unchanged.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadMap[R, A, B any](fa ReaderIOResult[R, A], f func(A) B) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadMap(fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map returns a function that applies a transformation to the success value of a ReaderIOResult.
|
||||||
|
// This is the curried version of MonadMap, useful for function composition.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Map[R, A, B any](f func(A) B) Operator[R, A, B] {
|
||||||
|
return RIOE.Map[R, error](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMapTo replaces the success value with a constant value.
|
||||||
|
// Useful when you want to discard the result but keep the effect.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadMapTo[R, A, B any](fa ReaderIOResult[R, A], b B) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadMapTo(fa, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapTo returns a function that replaces the success value with a constant.
|
||||||
|
// This is the curried version of MonadMapTo.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MapTo[R, A, B any](b B) Operator[R, A, B] {
|
||||||
|
return RIOE.MapTo[R, error, A](b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChain sequences two computations where the second depends on the result of the first.
|
||||||
|
// This is the fundamental operation for composing dependent effectful computations.
|
||||||
|
// If the first computation fails, the second is not executed.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChain[R, A, B any](fa ReaderIOResult[R, A], f Kleisli[R, A, B]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadChain(fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainFirst sequences two computations but keeps the result of the first.
|
||||||
|
// Useful for performing side effects while preserving the original value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirst[R, A, B any](fa ReaderIOResult[R, A], f Kleisli[R, A, B]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.MonadChainFirst(fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainEitherK chains a computation that returns an Either into a ReaderIOResult.
|
||||||
|
// The Either is automatically lifted into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainEitherK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadChainEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainEitherK chains a computation that returns an Either into a ReaderIOResult.
|
||||||
|
// The Either is automatically lifted into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainResultK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadChainEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainEitherK returns a function that chains an Either-returning function into ReaderIOResult.
|
||||||
|
// This is the curried version of MonadChainEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainEitherK[R, A, B any](f func(A) Result[B]) Operator[R, A, B] {
|
||||||
|
return RIOE.ChainEitherK[R](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainResultK returns a function that chains an Either-returning function into ReaderIOResult.
|
||||||
|
// This is the curried version of MonadChainEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainResultK[R, A, B any](f func(A) Result[B]) Operator[R, A, B] {
|
||||||
|
return RIOE.ChainEitherK[R](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainFirstEitherK chains an Either-returning computation but keeps the original value.
|
||||||
|
// Useful for validation or side effects that return Either.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstEitherK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.MonadChainFirstEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainFirstEitherK returns a function that chains an Either computation while preserving the original value.
|
||||||
|
// This is the curried version of MonadChainFirstEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstEitherK[R, A, B any](f func(A) Result[B]) Operator[R, A, A] {
|
||||||
|
return RIOE.ChainFirstEitherK[R](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainFirstEitherK chains an Either-returning computation but keeps the original value.
|
||||||
|
// Useful for validation or side effects that return Either.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstResultK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Result[B]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.MonadChainFirstEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainFirstEitherK returns a function that chains an Either computation while preserving the original value.
|
||||||
|
// This is the curried version of MonadChainFirstEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstResultK[R, A, B any](f func(A) Result[B]) Operator[R, A, A] {
|
||||||
|
return RIOE.ChainFirstEitherK[R](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainReaderK chains a Reader-returning computation into a ReaderIOResult.
|
||||||
|
// The Reader is automatically lifted into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainReaderK[R, A, B any](ma ReaderIOResult[R, A], f func(A) Reader[R, B]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadChainReaderK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainReaderK returns a function that chains a Reader-returning function into ReaderIOResult.
|
||||||
|
// This is the curried version of MonadChainReaderK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainReaderK[R, A, B any](f func(A) Reader[R, B]) Operator[R, A, B] {
|
||||||
|
return RIOE.ChainReaderK[error](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainIOEitherK chains an IOEither-returning computation into a ReaderIOResult.
|
||||||
|
// The IOEither is automatically lifted into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainIOEitherK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IOResult[B]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadChainIOEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainIOEitherK chains an IOEither-returning computation into a ReaderIOResult.
|
||||||
|
// The IOEither is automatically lifted into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainIOResultK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IOResult[B]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadChainIOEitherK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainIOEitherK returns a function that chains an IOEither-returning function into ReaderIOResult.
|
||||||
|
// This is the curried version of MonadChainIOEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainIOEitherK[R, A, B any](f func(A) IOResult[B]) Operator[R, A, B] {
|
||||||
|
return RIOE.ChainIOEitherK[R](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainIOEitherK returns a function that chains an IOEither-returning function into ReaderIOResult.
|
||||||
|
// This is the curried version of MonadChainIOEitherK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainIOResultK[R, A, B any](f func(A) IOResult[B]) Operator[R, A, B] {
|
||||||
|
return RIOE.ChainIOEitherK[R](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainIOK chains an IO-returning computation into a ReaderIOResult.
|
||||||
|
// The IO is automatically lifted into the ReaderIOResult context (always succeeds).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainIOK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IO[B]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadChainIOK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainIOK returns a function that chains an IO-returning function into ReaderIOResult.
|
||||||
|
// This is the curried version of MonadChainIOK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainIOK[R, A, B any](f func(A) IO[B]) Operator[R, A, B] {
|
||||||
|
return RIOE.ChainIOK[R, error](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadChainFirstIOK chains an IO computation but keeps the original value.
|
||||||
|
// Useful for performing IO side effects while preserving the original value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadChainFirstIOK[R, A, B any](ma ReaderIOResult[R, A], f func(A) IO[B]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.MonadChainFirstIOK(ma, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainFirstIOK returns a function that chains an IO computation while preserving the original value.
|
||||||
|
// This is the curried version of MonadChainFirstIOK.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirstIOK[R, A, B any](f func(A) IO[B]) Operator[R, A, A] {
|
||||||
|
return RIOE.ChainFirstIOK[R, error](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainOptionK returns a function that chains an Option-returning function into ReaderIOResult.
|
||||||
|
// If the Option is None, the provided error function is called to produce the error value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainOptionK[R, A, B any](onNone func() error) func(func(A) Option[B]) Operator[R, A, B] {
|
||||||
|
return RIOE.ChainOptionK[R, A, B](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadAp[R, A, B any](fab ReaderIOResult[R, func(A) B], fa ReaderIOResult[R, A]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadAp(fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadApSeq applies a function in a context to a value in a context, executing them sequentially.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadApSeq[R, A, B any](fab ReaderIOResult[R, func(A) B], fa ReaderIOResult[R, A]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadApSeq(fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadApPar applies a function in a context to a value in a context, executing them in parallel.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadApPar[R, A, B any](fab ReaderIOResult[R, func(A) B], fa ReaderIOResult[R, A]) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadApPar(fab, fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ap returns a function that applies a function in a context to a value in a context.
|
||||||
|
// This is the curried version of MonadAp.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Ap[B, R, A any](fa ReaderIOResult[R, A]) Operator[R, func(A) B, B] {
|
||||||
|
return RIOE.Ap[B](fa)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain returns a function that sequences computations where the second depends on the first.
|
||||||
|
// This is the curried version of MonadChain.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Chain[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, B] {
|
||||||
|
return RIOE.Chain(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainFirst returns a function that sequences computations but keeps the first result.
|
||||||
|
// This is the curried version of MonadChainFirst.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ChainFirst[R, A, B any](f Kleisli[R, A, B]) Operator[R, A, A] {
|
||||||
|
return RIOE.ChainFirst(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right creates a successful ReaderIOResult with the given value.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Right[R, A any](a A) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.Right[R, error](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left creates a failed ReaderIOResult with the given error.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Left[R, A any](e error) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.Left[R, A](e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThrowError creates a failed ReaderIOResult with the given error.
|
||||||
|
// This is an alias for Left, following the naming convention from other functional libraries.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func ThrowError[R, A any](e error) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.ThrowError[R, A](e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a successful ReaderIOResult with the given value.
|
||||||
|
// This is the pointed functor operation, lifting a pure value into the ReaderIOResult context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Of[R, A any](a A) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.Of[R, error](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten removes one level of nesting from a nested ReaderIOResult.
|
||||||
|
// Converts ReaderIOResult[R, ReaderIOResult[R, A]] to ReaderIOResult[R, A].
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Flatten[R, A any](mma ReaderIOResult[R, ReaderIOResult[R, A]]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.Flatten(mma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromEither lifts an Either into a ReaderIOResult context.
|
||||||
|
// The Either value is independent of any context or IO effects.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromEither[R, A any](t Result[A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.FromEither[R](t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromResult lifts an Either into a ReaderIOResult context.
|
||||||
|
// The Either value is independent of any context or IO effects.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromResult[R, A any](t Result[A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.FromEither[R](t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightReader lifts a Reader into a ReaderIOResult, placing the result in the Right side.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func RightReader[R, A any](ma Reader[R, A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.RightReader[error](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftReader lifts a Reader into a ReaderIOResult, placing the result in the Left (error) side.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LeftReader[A, R any](ma Reader[R, error]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.LeftReader[A](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromReader lifts a Reader into a ReaderIOResult context.
|
||||||
|
// The Reader result is placed in the Right side (success).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromReader[R, A any](ma Reader[R, A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.FromReader[error](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RightIO lifts an IO into a ReaderIOResult, placing the result in the Right side.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func RightIO[R, A any](ma IO[A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.RightIO[R, error](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftIO lifts an IO into a ReaderIOResult, placing the result in the Left (error) side.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func LeftIO[R, A any](ma IO[error]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.LeftIO[R, A](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIO lifts an IO into a ReaderIOResult context.
|
||||||
|
// The IO result is placed in the Right side (success).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromIO[R, A any](ma IO[A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.FromIO[R, error](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIOEither lifts an IOEither into a ReaderIOResult context.
|
||||||
|
// The computation becomes independent of any reader context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromIOEither[R, A any](ma IOResult[A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.FromIOEither[R](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIOEither lifts an IOEither into a ReaderIOResult context.
|
||||||
|
// The computation becomes independent of any reader context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromIOResult[R, A any](ma IOResult[A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.FromIOEither[R](ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromReaderEither lifts a ReaderEither into a ReaderIOResult context.
|
||||||
|
// The Either result is lifted into an IO effect.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromReaderEither[R, A any](ma RE.ReaderEither[R, error, A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.FromReaderEither(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask returns a ReaderIOResult that retrieves the current context.
|
||||||
|
// Useful for accessing configuration or dependencies.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Ask[R any]() ReaderIOResult[R, R] {
|
||||||
|
return RIOE.Ask[R, error]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asks returns a ReaderIOResult that retrieves a value derived from the context.
|
||||||
|
// This is useful for extracting specific fields from a configuration object.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Asks[R, A any](r Reader[R, A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.Asks[error](r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromOption converts an Option to a ReaderIOResult.
|
||||||
|
// If the Option is None, the provided function is called to produce the error.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromOption[R, A any](onNone func() error) Kleisli[R, Option[A], A] {
|
||||||
|
return RIOE.FromOption[R, A](onNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPredicate creates a ReaderIOResult from a predicate.
|
||||||
|
// If the predicate returns false, the onFalse function is called to produce the error.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func FromPredicate[R, A any](pred func(A) bool, onFalse func(A) error) Kleisli[R, A, A] {
|
||||||
|
return RIOE.FromPredicate[R](pred, onFalse)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fold handles both success and error cases, producing a ReaderIO.
|
||||||
|
// This is useful for converting a ReaderIOResult into a ReaderIO by handling all cases.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Fold[R, A, B any](onLeft func(error) ReaderIO[R, B], onRight func(A) ReaderIO[R, B]) func(ReaderIOResult[R, A]) ReaderIO[R, B] {
|
||||||
|
return RIOE.Fold(onLeft, onRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrElse provides a default value in case of error.
|
||||||
|
// The default is computed lazily via a ReaderIO.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func GetOrElse[R, A any](onLeft func(error) ReaderIO[R, A]) func(ReaderIOResult[R, A]) ReaderIO[R, A] {
|
||||||
|
return RIOE.GetOrElse(onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrElse tries an alternative computation if the first one fails.
|
||||||
|
// The alternative can produce a different error type.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func OrElse[R, A, E any](onLeft func(error) RIOE.ReaderIOEither[R, E, A]) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, A] {
|
||||||
|
return RIOE.OrElse(onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrLeft transforms the error using a ReaderIO if the computation fails.
|
||||||
|
// The success value is preserved unchanged.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func OrLeft[A, R, E any](onLeft func(error) ReaderIO[R, E]) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, A] {
|
||||||
|
return RIOE.OrLeft[A](onLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadBiMap applies two functions: one to the error, one to the success value.
|
||||||
|
// This allows transforming both channels simultaneously.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadBiMap[R, E, A, B any](fa ReaderIOResult[R, A], f func(error) E, g func(A) B) RIOE.ReaderIOEither[R, E, B] {
|
||||||
|
return RIOE.MonadBiMap(fa, f, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BiMap returns a function that maps over both the error and success channels.
|
||||||
|
// This is the curried version of MonadBiMap.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func BiMap[R, E, A, B any](f func(error) E, g func(A) B) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, B] {
|
||||||
|
return RIOE.BiMap[R](f, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap exchanges the error and success types.
|
||||||
|
// Left becomes Right and Right becomes Left.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Swap[R, A any](val ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, A, error] {
|
||||||
|
return RIOE.Swap(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defer creates a ReaderIOResult lazily via a generator function.
|
||||||
|
// The generator is called each time the ReaderIOResult is executed.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Defer[R, A any](gen Lazy[ReaderIOResult[R, A]]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.Defer(gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryCatch wraps a function that returns (value, error) into a ReaderIOResult.
|
||||||
|
// The onThrow function converts the error into the desired error type.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func TryCatch[R, A any](f func(R) func() (A, error), onThrow Endomorphism[error]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.TryCatch(f, onThrow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadAlt tries the first computation, and if it fails, tries the second.
|
||||||
|
// This implements the Alternative pattern for error recovery.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadAlt[R, A any](first ReaderIOResult[R, A], second Lazy[ReaderIOResult[R, A]]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.MonadAlt(first, second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt returns a function that tries an alternative computation if the first fails.
|
||||||
|
// This is the curried version of MonadAlt.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Alt[R, A any](second Lazy[ReaderIOResult[R, A]]) Operator[R, A, A] {
|
||||||
|
return RIOE.Alt(second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memoize computes the value of the ReaderIOResult lazily but exactly once.
|
||||||
|
// The context used is from the first call. Do not use if the value depends on the context.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Memoize[R, A any](rdr ReaderIOResult[R, A]) ReaderIOResult[R, A] {
|
||||||
|
return RIOE.Memoize(rdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadFlap[R, B, A any](fab ReaderIOResult[R, func(A) B], a A) ReaderIOResult[R, B] {
|
||||||
|
return RIOE.MonadFlap(fab, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flap returns a function that applies a fixed value to a function in a context.
|
||||||
|
// This is the curried version of MonadFlap.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Flap[R, B, A any](a A) Operator[R, func(A) B, B] {
|
||||||
|
return RIOE.Flap[R, error, B](a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonadMapLeft applies a function to the error value, leaving success unchanged.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MonadMapLeft[R, E, A any](fa ReaderIOResult[R, A], f func(error) E) RIOE.ReaderIOEither[R, E, A] {
|
||||||
|
return RIOE.MonadMapLeft(fa, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapLeft returns a function that transforms the error channel.
|
||||||
|
// This is the curried version of MonadMapLeft.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func MapLeft[R, A, E any](f func(error) E) func(ReaderIOResult[R, A]) RIOE.ReaderIOEither[R, E, A] {
|
||||||
|
return RIOE.MapLeft[R, A](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local runs a computation with a modified context.
|
||||||
|
// The function f transforms the context before passing it to the computation.
|
||||||
|
// This is similar to Contravariant's contramap operation.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func Local[A, R1, R2 any](f func(R2) R1) func(ReaderIOResult[R1, A]) ReaderIOResult[R2, A] {
|
||||||
|
return RIOE.Local[error, A](f)
|
||||||
|
}
|
||||||
59
v2/readerioresult/reader_test.go
Normal file
59
v2/readerioresult/reader_test.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/internal/utils"
|
||||||
|
R "github.com/IBM/fp-go/v2/reader"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
|
||||||
|
g := F.Pipe1(
|
||||||
|
Of[context.Context](1),
|
||||||
|
Map[context.Context](utils.Double),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of(2), g(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAp(t *testing.T) {
|
||||||
|
g := F.Pipe1(
|
||||||
|
Right[context.Context](utils.Double),
|
||||||
|
Ap[int](Right[context.Context](1)),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of(2), g(context.Background())())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainReaderK(t *testing.T) {
|
||||||
|
|
||||||
|
g := F.Pipe1(
|
||||||
|
Of[context.Context](1),
|
||||||
|
ChainReaderK(func(v int) R.Reader[context.Context, string] {
|
||||||
|
return R.Of[context.Context](fmt.Sprintf("%d", v))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of("1"), g(context.Background())())
|
||||||
|
}
|
||||||
791
v2/readerioresult/readerioeither_test.go
Normal file
791
v2/readerioresult/readerioeither_test.go
Normal file
@@ -0,0 +1,791 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
E "github.com/IBM/fp-go/v2/either"
|
||||||
|
F "github.com/IBM/fp-go/v2/function"
|
||||||
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
|
O "github.com/IBM/fp-go/v2/option"
|
||||||
|
R "github.com/IBM/fp-go/v2/reader"
|
||||||
|
RE "github.com/IBM/fp-go/v2/readereither"
|
||||||
|
RIO "github.com/IBM/fp-go/v2/readerio"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testContext struct {
|
||||||
|
value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadMap(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadMap(Of[testContext](5), func(x int) int { return x * 2 })
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadMapTo(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadMapTo(Of[testContext](5), 42)
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapTo(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(Of[testContext](5), MapTo[testContext, int](42))
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainFirst(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadChainFirst(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(x int) ReaderIOResult[testContext, string] {
|
||||||
|
return Of[testContext](fmt.Sprintf("%d", x))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainFirst(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
ChainFirst(func(x int) ReaderIOResult[testContext, string] {
|
||||||
|
return Of[testContext](fmt.Sprintf("%d", x))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainEitherK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadChainEitherK(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(x int) E.Either[error, int] {
|
||||||
|
return result.Of(x * 2)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainFirstEitherK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadChainFirstEitherK(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(x int) E.Either[error, string] {
|
||||||
|
return result.Of(fmt.Sprintf("%d", x))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainFirstEitherK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
ChainFirstEitherK[testContext](func(x int) E.Either[error, string] {
|
||||||
|
return result.Of(fmt.Sprintf("%d", x))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainReaderK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadChainReaderK(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(x int) R.Reader[testContext, int] {
|
||||||
|
return func(c testContext) int { return x + c.value }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(15), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainIOEitherK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadChainIOEitherK(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(x int) IOResult[int] {
|
||||||
|
return ioresult.Of(x * 2)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainIOEitherK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
ChainIOEitherK[testContext](func(x int) IOResult[int] {
|
||||||
|
return ioresult.Of(x * 2)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainIOK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadChainIOK(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(x int) IO[int] {
|
||||||
|
return func() int { return x * 2 }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainIOK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
ChainIOK[testContext](func(x int) IO[int] {
|
||||||
|
return func() int { return x * 2 }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadChainFirstIOK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := MonadChainFirstIOK(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(x int) IO[string] {
|
||||||
|
return func() string { return fmt.Sprintf("%d", x) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainFirstIOK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
ChainFirstIOK[testContext](func(x int) IO[string] {
|
||||||
|
return func() string { return fmt.Sprintf("%d", x) }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainOptionK(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test with Some
|
||||||
|
resultSome := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
ChainOptionK[testContext, int, int](func() error {
|
||||||
|
return errors.New("none")
|
||||||
|
})(func(x int) Option[int] {
|
||||||
|
return O.Some(x * 2)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), resultSome(ctx)())
|
||||||
|
|
||||||
|
// Test with None
|
||||||
|
resultNone := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
ChainOptionK[testContext, int, int](func() error {
|
||||||
|
return errors.New("none")
|
||||||
|
})(func(x int) Option[int] {
|
||||||
|
return O.None[int]()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.True(t, E.IsLeft(resultNone(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadApSeq(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
fab := Of[testContext](func(x int) int { return x * 2 })
|
||||||
|
fa := Of[testContext](5)
|
||||||
|
res := MonadApSeq(fab, fa)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadApPar(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
fab := Of[testContext](func(x int) int { return x * 2 })
|
||||||
|
fa := Of[testContext](5)
|
||||||
|
res := MonadApPar(fab, fa)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChain(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
Chain(func(x int) ReaderIOResult[testContext, int] {
|
||||||
|
return Of[testContext](x * 2)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestThrowError(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
result := ThrowError[testContext, int](errors.New("test error"))
|
||||||
|
assert.True(t, E.IsLeft(result(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlatten(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
nested := Of[testContext](Of[testContext](5))
|
||||||
|
res := Flatten(nested)
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromEither(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := FromEither[testContext](result.Of(5))
|
||||||
|
assert.Equal(t, result.Of(5), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRightReader(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
rdr := func(c testContext) int { return c.value }
|
||||||
|
res := RightReader(rdr)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLeftReader(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
reader := func(c testContext) error { return errors.New("test") }
|
||||||
|
res := LeftReader[int](reader)
|
||||||
|
assert.True(t, E.IsLeft(res(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRightIO(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
ioVal := func() int { return 42 }
|
||||||
|
res := RightIO[testContext](ioVal)
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLeftIO(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
ioVal := func() error { return errors.New("test") }
|
||||||
|
res := LeftIO[testContext, int](ioVal)
|
||||||
|
assert.True(t, E.IsLeft(res(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromIO(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
ioVal := func() int { return 42 }
|
||||||
|
res := FromIO[testContext](ioVal)
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromIOEither(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
ioe := ioresult.Of(42)
|
||||||
|
res := FromIOEither[testContext](ioe)
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromReaderEither(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
re := RE.Of[testContext, error](42)
|
||||||
|
res := FromReaderEither(re)
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsk(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := Ask[testContext]()
|
||||||
|
assert.Equal(t, result.Of(ctx), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsks(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := Asks(func(c testContext) int { return c.value })
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromOption(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test with Some
|
||||||
|
resultSome := FromOption[testContext, int](func() error {
|
||||||
|
return errors.New("none")
|
||||||
|
})(O.Some(42))
|
||||||
|
assert.Equal(t, result.Of(42), resultSome(ctx)())
|
||||||
|
|
||||||
|
// Test with None
|
||||||
|
resultNone := FromOption[testContext, int](func() error {
|
||||||
|
return errors.New("none")
|
||||||
|
})(O.None[int]())
|
||||||
|
assert.True(t, E.IsLeft(resultNone(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromPredicate(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test predicate true
|
||||||
|
resultTrue := FromPredicate[testContext](
|
||||||
|
func(x int) bool { return x > 0 },
|
||||||
|
func(x int) error { return errors.New("negative") },
|
||||||
|
)(5)
|
||||||
|
assert.Equal(t, result.Of(5), resultTrue(ctx)())
|
||||||
|
|
||||||
|
// Test predicate false
|
||||||
|
resultFalse := FromPredicate[testContext](
|
||||||
|
func(x int) bool { return x > 0 },
|
||||||
|
func(x int) error { return errors.New("negative") },
|
||||||
|
)(-5)
|
||||||
|
assert.True(t, E.IsLeft(resultFalse(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFold(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test Right case
|
||||||
|
resultRight := Fold(
|
||||||
|
func(e error) RIO.ReaderIO[testContext, string] {
|
||||||
|
return RIO.Of[testContext]("error: " + e.Error())
|
||||||
|
},
|
||||||
|
func(x int) RIO.ReaderIO[testContext, string] {
|
||||||
|
return RIO.Of[testContext](fmt.Sprintf("value: %d", x))
|
||||||
|
},
|
||||||
|
)(Of[testContext](42))
|
||||||
|
assert.Equal(t, "value: 42", resultRight(ctx)())
|
||||||
|
|
||||||
|
// Test Left case
|
||||||
|
resultLeft := Fold(
|
||||||
|
func(e error) RIO.ReaderIO[testContext, string] {
|
||||||
|
return RIO.Of[testContext]("error: " + e.Error())
|
||||||
|
},
|
||||||
|
func(x int) RIO.ReaderIO[testContext, string] {
|
||||||
|
return RIO.Of[testContext](fmt.Sprintf("value: %d", x))
|
||||||
|
},
|
||||||
|
)(Left[testContext, int](errors.New("test")))
|
||||||
|
assert.Equal(t, "error: test", resultLeft(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOrElse(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test Right case
|
||||||
|
resultRight := GetOrElse(func(e error) RIO.ReaderIO[testContext, int] {
|
||||||
|
return RIO.Of[testContext](0)
|
||||||
|
})(Of[testContext](42))
|
||||||
|
assert.Equal(t, 42, resultRight(ctx)())
|
||||||
|
|
||||||
|
// Test Left case
|
||||||
|
resultLeft := GetOrElse(func(e error) RIO.ReaderIO[testContext, int] {
|
||||||
|
return RIO.Of[testContext](0)
|
||||||
|
})(Left[testContext, int](errors.New("test")))
|
||||||
|
assert.Equal(t, 0, resultLeft(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadBiMap(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test Right case
|
||||||
|
resultRight := MonadBiMap(
|
||||||
|
Of[testContext](5),
|
||||||
|
func(e error) string { return e.Error() },
|
||||||
|
func(x int) string { return fmt.Sprintf("%d", x) },
|
||||||
|
)
|
||||||
|
assert.Equal(t, E.Of[string]("5"), resultRight(ctx)())
|
||||||
|
|
||||||
|
// Test Left case
|
||||||
|
resultLeft := MonadBiMap(
|
||||||
|
Left[testContext, int](errors.New("test")),
|
||||||
|
func(e error) string { return e.Error() },
|
||||||
|
func(x int) string { return fmt.Sprintf("%d", x) },
|
||||||
|
)
|
||||||
|
assert.Equal(t, E.Left[string]("test"), resultLeft(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBiMap(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](5),
|
||||||
|
BiMap[testContext](
|
||||||
|
func(e error) string { return e.Error() },
|
||||||
|
func(x int) string { return fmt.Sprintf("%d", x) },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert.Equal(t, E.Of[string]("5"), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwap(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test Right becomes Left
|
||||||
|
resultRight := Swap(Of[testContext](5))
|
||||||
|
res := resultRight(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
|
||||||
|
// Test Left becomes Right
|
||||||
|
resultLeft := Swap(Left[testContext, int](errors.New("test")))
|
||||||
|
assert.True(t, E.IsRight(resultLeft(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefer(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
callCount := 0
|
||||||
|
res := Defer(func() ReaderIOResult[testContext, int] {
|
||||||
|
callCount++
|
||||||
|
return Of[testContext](42)
|
||||||
|
})
|
||||||
|
|
||||||
|
// First call
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
assert.Equal(t, 1, callCount)
|
||||||
|
|
||||||
|
// Second call
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
assert.Equal(t, 2, callCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadAlt(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
|
||||||
|
// Test first succeeds
|
||||||
|
resultFirst := MonadAlt(
|
||||||
|
Of[testContext](42),
|
||||||
|
func() ReaderIOResult[testContext, int] {
|
||||||
|
return Of[testContext](99)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(42), resultFirst(ctx)())
|
||||||
|
|
||||||
|
// Test first fails, second succeeds
|
||||||
|
resultSecond := MonadAlt(
|
||||||
|
Left[testContext, int](errors.New("first")),
|
||||||
|
func() ReaderIOResult[testContext, int] {
|
||||||
|
return Of[testContext](99)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(99), resultSecond(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAlt(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Left[testContext, int](errors.New("first")),
|
||||||
|
Alt(func() ReaderIOResult[testContext, int] {
|
||||||
|
return Of[testContext](99)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(99), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMemoize(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
callCount := 0
|
||||||
|
res := Memoize(func(c testContext) IOResult[int] {
|
||||||
|
return func() E.Either[error, int] {
|
||||||
|
callCount++
|
||||||
|
return result.Of(c.value * 2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// First call
|
||||||
|
assert.Equal(t, result.Of(20), res(ctx)())
|
||||||
|
assert.Equal(t, 1, callCount)
|
||||||
|
|
||||||
|
// Second call should use memoized value
|
||||||
|
assert.Equal(t, result.Of(20), res(ctx)())
|
||||||
|
assert.Equal(t, 1, callCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadFlap(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
fab := Of[testContext](func(x int) int { return x * 2 })
|
||||||
|
res := MonadFlap(fab, 5)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlap(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](func(x int) int { return x * 2 }),
|
||||||
|
Flap[testContext, int](5),
|
||||||
|
)
|
||||||
|
assert.Equal(t, result.Of(10), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonadMapLeft(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
result := MonadMapLeft(
|
||||||
|
Left[testContext, int](errors.New("test")),
|
||||||
|
func(e error) string { return e.Error() + "!" },
|
||||||
|
)
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
// Verify the error was transformed
|
||||||
|
E.Fold(
|
||||||
|
func(s string) int {
|
||||||
|
assert.Equal(t, "test!", s)
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
func(i int) int { return i },
|
||||||
|
)(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapLeft(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
result := F.Pipe1(
|
||||||
|
Left[testContext, int](errors.New("test")),
|
||||||
|
MapLeft[testContext, int](func(e error) string { return e.Error() + "!" }),
|
||||||
|
)
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsLeft(res))
|
||||||
|
// Verify the error was transformed
|
||||||
|
E.Fold(
|
||||||
|
func(s string) int {
|
||||||
|
assert.Equal(t, "test!", s)
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
func(i int) int { return i },
|
||||||
|
)(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLocal(t *testing.T) {
|
||||||
|
ctx2 := testContext{value: 20}
|
||||||
|
|
||||||
|
rdr := Asks(func(c testContext) int { return c.value })
|
||||||
|
res := Local[int](func(c testContext) testContext {
|
||||||
|
return testContext{value: c.value * 2}
|
||||||
|
})(rdr)
|
||||||
|
|
||||||
|
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] {
|
||||||
|
return func() int { return c.value * 2 }
|
||||||
|
}
|
||||||
|
res := RightReaderIO(rio)
|
||||||
|
assert.Equal(t, result.Of(20), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLeftReaderIO(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
rio := func(c testContext) IO[error] {
|
||||||
|
return func() error { return errors.New("test") }
|
||||||
|
}
|
||||||
|
res := LeftReaderIO[int](rio)
|
||||||
|
assert.True(t, E.IsLeft(res(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLet(t *testing.T) {
|
||||||
|
type State struct {
|
||||||
|
a int
|
||||||
|
b string
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
res := F.Pipe2(
|
||||||
|
Do[context.Context](State{}),
|
||||||
|
Let[context.Context](func(b string) func(State) State {
|
||||||
|
return func(s State) State { return State{a: s.a, b: b} }
|
||||||
|
}, func(s State) string { return "test" }),
|
||||||
|
Map[context.Context](func(s State) string { return s.b }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of("test"), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLetTo(t *testing.T) {
|
||||||
|
type State struct {
|
||||||
|
a int
|
||||||
|
b string
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
res := F.Pipe2(
|
||||||
|
Do[context.Context](State{}),
|
||||||
|
LetTo[context.Context](func(b string) func(State) State {
|
||||||
|
return func(s State) State { return State{a: s.a, b: b} }
|
||||||
|
}, "constant"),
|
||||||
|
Map[context.Context](func(s State) string { return s.b }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of("constant"), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindTo(t *testing.T) {
|
||||||
|
type State struct {
|
||||||
|
value int
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
res := F.Pipe2(
|
||||||
|
Of[context.Context](42),
|
||||||
|
BindTo[context.Context](func(v int) State { return State{value: v} }),
|
||||||
|
Map[context.Context](func(s State) int { return s.value }),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBracket(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
released := false
|
||||||
|
|
||||||
|
res := Bracket(
|
||||||
|
Of[testContext](42),
|
||||||
|
func(x int) ReaderIOResult[testContext, string] {
|
||||||
|
return Of[testContext](fmt.Sprintf("%d", x))
|
||||||
|
},
|
||||||
|
func(x int, result E.Either[error, string]) ReaderIOResult[testContext, int] {
|
||||||
|
released = true
|
||||||
|
return Of[testContext](0)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of("42"), res(ctx)())
|
||||||
|
assert.True(t, released)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithResource(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
released := false
|
||||||
|
|
||||||
|
res := WithResource[string](
|
||||||
|
Of[testContext](42),
|
||||||
|
func(x int) ReaderIOResult[testContext, int] {
|
||||||
|
released = true
|
||||||
|
return Of[testContext](0)
|
||||||
|
},
|
||||||
|
)(func(x int) ReaderIOResult[testContext, string] {
|
||||||
|
return Of[testContext](fmt.Sprintf("%d", x))
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of("42"), res(ctx)())
|
||||||
|
assert.True(t, released)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonad(t *testing.T) {
|
||||||
|
m := Monad[testContext, int, string]()
|
||||||
|
assert.NotNil(t, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraverseArrayWithIndex(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := TraverseArrayWithIndex(func(i int, x int) ReaderIOResult[testContext, int] {
|
||||||
|
return Of[testContext](x + i)
|
||||||
|
})([]int{1, 2, 3})
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of([]int{1, 3, 5}), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraverseRecord(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := TraverseRecord[string](func(x int) ReaderIOResult[testContext, int] {
|
||||||
|
return Of[testContext](x * 2)
|
||||||
|
})(map[string]int{"a": 1, "b": 2})
|
||||||
|
|
||||||
|
expected := map[string]int{"a": 2, "b": 4}
|
||||||
|
assert.Equal(t, result.Of(expected), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraverseRecordWithIndex(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := TraverseRecordWithIndex(func(k string, x int) ReaderIOResult[testContext, string] {
|
||||||
|
return Of[testContext](fmt.Sprintf("%s:%d", k, x))
|
||||||
|
})(map[string]int{"a": 1, "b": 2})
|
||||||
|
|
||||||
|
assert.True(t, E.IsRight(res(ctx)()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceRecord(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
res := SequenceRecord(map[string]ReaderIOResult[testContext, int]{
|
||||||
|
"a": Of[testContext](1),
|
||||||
|
"b": Of[testContext](2),
|
||||||
|
})
|
||||||
|
|
||||||
|
expected := map[string]int{"a": 1, "b": 2}
|
||||||
|
assert.Equal(t, result.Of(expected), res(ctx)())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceT1(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
result := SequenceT1(Of[testContext](42))
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceT3(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
result := SequenceT3(
|
||||||
|
Of[testContext](1),
|
||||||
|
Of[testContext]("a"),
|
||||||
|
Of[testContext](true),
|
||||||
|
)
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceT4(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
result := SequenceT4(
|
||||||
|
Of[testContext](1),
|
||||||
|
Of[testContext]("a"),
|
||||||
|
Of[testContext](true),
|
||||||
|
Of[testContext](3.14),
|
||||||
|
)
|
||||||
|
res := result(ctx)()
|
||||||
|
assert.True(t, E.IsRight(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithLock(t *testing.T) {
|
||||||
|
ctx := testContext{value: 10}
|
||||||
|
unlocked := false
|
||||||
|
|
||||||
|
res := F.Pipe1(
|
||||||
|
Of[testContext](42),
|
||||||
|
WithLock[testContext, int](func() context.CancelFunc {
|
||||||
|
return func() { unlocked = true }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Of(42), res(ctx)())
|
||||||
|
assert.True(t, unlocked)
|
||||||
|
}
|
||||||
58
v2/readerioresult/resource.go
Normal file
58
v2/readerioresult/resource.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithResource constructs a function that creates a resource, operates on it, and then releases the resource.
|
||||||
|
// This ensures proper resource cleanup even in the presence of errors, following the Resource Acquisition Is Initialization (RAII) pattern.
|
||||||
|
//
|
||||||
|
// The resource lifecycle is:
|
||||||
|
// 1. onCreate: Acquires the resource
|
||||||
|
// 2. use: Operates on the resource (provided as argument to the returned function)
|
||||||
|
// 3. onRelease: Releases the resource (called regardless of success or failure)
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - A: The type of the result produced by using the resource
|
||||||
|
// - L: The context type
|
||||||
|
// - E: The error type
|
||||||
|
// - R: The resource type
|
||||||
|
// - ANY: The type returned by the release function (typically ignored)
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - onCreate: A computation that acquires the resource
|
||||||
|
// - onRelease: A function that releases the resource, called with the resource and executed regardless of errors
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A function that takes a resource-using function and returns a ReaderIOResult that manages the resource lifecycle
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// withFile := WithResource(
|
||||||
|
// openFile("data.txt"),
|
||||||
|
// func(f *File) ReaderIOResult[Config, error, int] {
|
||||||
|
// return closeFile(f)
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// result := withFile(func(f *File) ReaderIOResult[Config, error, string] {
|
||||||
|
// return readContent(f)
|
||||||
|
// })
|
||||||
|
func WithResource[A, L, R, ANY any](onCreate ReaderIOResult[L, R], onRelease Kleisli[L, R, ANY]) Kleisli[L, Kleisli[L, R, A], A] {
|
||||||
|
return RIOE.WithResource[A](onCreate, onRelease)
|
||||||
|
}
|
||||||
93
v2/readerioresult/sequence.go
Normal file
93
v2/readerioresult/sequence.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SequenceT1 converts a single ReaderIOResult into a ReaderIOResult of a 1-tuple.
|
||||||
|
// This is useful for uniformly handling computations with different arities.
|
||||||
|
//
|
||||||
|
// If the input computation fails, the result will be a Left with the error.
|
||||||
|
// If it succeeds, the result will be a Right with a tuple containing the value.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := SequenceT1(Of[Config, error](42))
|
||||||
|
// // result(cfg)() returns Right(Tuple1{42})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func SequenceT1[R, A any](a ReaderIOResult[R, A]) ReaderIOResult[R, T.Tuple1[A]] {
|
||||||
|
return RIOE.SequenceT1(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceT2 combines two ReaderIOResult computations into a single ReaderIOResult of a 2-tuple.
|
||||||
|
// Both computations are executed, and if both succeed, their results are combined into a tuple.
|
||||||
|
// If either fails, the result is a Left with the first error encountered.
|
||||||
|
//
|
||||||
|
// This is useful for running multiple independent computations and collecting their results.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := SequenceT2(
|
||||||
|
// fetchUser(123),
|
||||||
|
// fetchProfile(123),
|
||||||
|
// )
|
||||||
|
// // result(cfg)() returns Right(Tuple2{user, profile}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func SequenceT2[R, A, B any](a ReaderIOResult[R, A], b ReaderIOResult[R, B]) ReaderIOResult[R, T.Tuple2[A, B]] {
|
||||||
|
return RIOE.SequenceT2(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceT3 combines three ReaderIOResult computations into a single ReaderIOResult of a 3-tuple.
|
||||||
|
// All three computations are executed, and if all succeed, their results are combined into a tuple.
|
||||||
|
// If any fails, the result is a Left with the first error encountered.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := SequenceT3(
|
||||||
|
// fetchUser(123),
|
||||||
|
// fetchProfile(123),
|
||||||
|
// fetchSettings(123),
|
||||||
|
// )
|
||||||
|
// // result(cfg)() returns Right(Tuple3{user, profile, settings}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func SequenceT3[R, A, B, C any](a ReaderIOResult[R, A], b ReaderIOResult[R, B], c ReaderIOResult[R, C]) ReaderIOResult[R, T.Tuple3[A, B, C]] {
|
||||||
|
return RIOE.SequenceT3(a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceT4 combines four ReaderIOResult computations into a single ReaderIOResult of a 4-tuple.
|
||||||
|
// All four computations are executed, and if all succeed, their results are combined into a tuple.
|
||||||
|
// If any fails, the result is a Left with the first error encountered.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// result := SequenceT4(
|
||||||
|
// fetchUser(123),
|
||||||
|
// fetchProfile(123),
|
||||||
|
// fetchSettings(123),
|
||||||
|
// fetchPreferences(123),
|
||||||
|
// )
|
||||||
|
// // result(cfg)() returns Right(Tuple4{user, profile, settings, prefs}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func SequenceT4[R, A, B, C, D any](a ReaderIOResult[R, A], b ReaderIOResult[R, B], c ReaderIOResult[R, C], d ReaderIOResult[R, D]) ReaderIOResult[R, T.Tuple4[A, B, C, D]] {
|
||||||
|
return RIOE.SequenceT4(a, b, c, d)
|
||||||
|
}
|
||||||
38
v2/readerioresult/sequence_test.go
Normal file
38
v2/readerioresult/sequence_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/either"
|
||||||
|
T "github.com/IBM/fp-go/v2/tuple"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSequence2(t *testing.T) {
|
||||||
|
// two readers of heterogeneous types
|
||||||
|
first := Of[context.Context]("a")
|
||||||
|
second := Of[context.Context](1)
|
||||||
|
|
||||||
|
// compose
|
||||||
|
s2 := SequenceT2[context.Context, string, int]
|
||||||
|
res := s2(first, second)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
assert.Equal(t, either.Right[error](T.MakeTuple2("a", 1)), res(ctx)())
|
||||||
|
}
|
||||||
57
v2/readerioresult/sync.go
Normal file
57
v2/readerioresult/sync.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IBM/fp-go/v2/readerio"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithLock executes a ReaderIOResult operation within the scope of a lock.
|
||||||
|
// The lock is acquired before the operation executes and released after it completes,
|
||||||
|
// regardless of whether the operation succeeds or fails.
|
||||||
|
//
|
||||||
|
// This is useful for ensuring thread-safe access to shared resources or for
|
||||||
|
// implementing critical sections in concurrent code.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - E: The error type
|
||||||
|
// - A: The value type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - lock: A function that acquires a lock and returns a CancelFunc to release it
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// An Operator that wraps the computation with lock acquisition and release
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var mu sync.Mutex
|
||||||
|
// safeFetch := F.Pipe1(
|
||||||
|
// fetchData(),
|
||||||
|
// WithLock[Config, error, Data](func() context.CancelFunc {
|
||||||
|
// mu.Lock()
|
||||||
|
// return func() { mu.Unlock() }
|
||||||
|
// }),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func WithLock[R, A any](lock func() context.CancelFunc) Operator[R, A, A] {
|
||||||
|
return readerio.WithLock[R, Result[A]](lock)
|
||||||
|
}
|
||||||
206
v2/readerioresult/traverse.go
Normal file
206
v2/readerioresult/traverse.go
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
RIOE "github.com/IBM/fp-go/v2/readerioeither"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TraverseArray transforms each element of an array using a function that returns a ReaderIOResult,
|
||||||
|
// then collects the results into a single ReaderIOResult containing an array.
|
||||||
|
//
|
||||||
|
// If any transformation fails, the entire operation fails with the first error encountered.
|
||||||
|
// All transformations are executed sequentially.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - E: The error type
|
||||||
|
// - A: The input element type
|
||||||
|
// - B: The output element type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: A function that transforms each element into a ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A function that takes an array and returns a ReaderIOResult of an array
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// fetchUsers := TraverseArray(func(id int) ReaderIOResult[Config, error, User] {
|
||||||
|
// return fetchUser(id)
|
||||||
|
// })
|
||||||
|
// result := fetchUsers([]int{1, 2, 3})
|
||||||
|
// // result(cfg)() returns Right([user1, user2, user3]) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func TraverseArray[R, A, B any](f Kleisli[R, A, B]) Kleisli[R, []A, []B] {
|
||||||
|
return RIOE.TraverseArray(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseArrayWithIndex is like TraverseArray but the transformation function also receives the index.
|
||||||
|
//
|
||||||
|
// This is useful when the transformation depends on the element's position in the array.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - E: The error type
|
||||||
|
// - A: The input element type
|
||||||
|
// - B: The output element type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: A function that transforms each element and its index into a ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A function that takes an array and returns a ReaderIOResult of an array
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// processWithIndex := TraverseArrayWithIndex(func(i int, val string) ReaderIOResult[Config, error, string] {
|
||||||
|
// return Of[Config, error](fmt.Sprintf("%d: %s", i, val))
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func TraverseArrayWithIndex[R, A, B any](f func(int, A) ReaderIOResult[R, B]) Kleisli[R, []A, []B] {
|
||||||
|
return RIOE.TraverseArrayWithIndex(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceArray converts an array of ReaderIOResult into a ReaderIOResult of an array.
|
||||||
|
//
|
||||||
|
// This is useful when you have multiple independent computations and want to execute them all
|
||||||
|
// and collect their results. If any computation fails, the entire operation fails with the first error.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - E: The error type
|
||||||
|
// - A: The element type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: An array of ReaderIOResult computations
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A ReaderIOResult that produces an array of results
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// computations := []ReaderIOResult[Config, error, int]{
|
||||||
|
// fetchCount("users"),
|
||||||
|
// fetchCount("posts"),
|
||||||
|
// fetchCount("comments"),
|
||||||
|
// }
|
||||||
|
// result := SequenceArray(computations)
|
||||||
|
// // result(cfg)() returns Right([userCount, postCount, commentCount]) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func SequenceArray[R, A any](ma []ReaderIOResult[R, A]) ReaderIOResult[R, []A] {
|
||||||
|
return RIOE.SequenceArray(ma)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecord transforms each value in a map using a function that returns a ReaderIOResult,
|
||||||
|
// then collects the results into a single ReaderIOResult containing a map.
|
||||||
|
//
|
||||||
|
// If any transformation fails, the entire operation fails with the first error encountered.
|
||||||
|
// The keys are preserved in the output map.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - K: The key type (must be comparable)
|
||||||
|
// - E: The error type
|
||||||
|
// - A: The input value type
|
||||||
|
// - B: The output value type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: A function that transforms each value into a ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A function that takes a map and returns a ReaderIOResult of a map
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// enrichUsers := TraverseRecord(func(user User) ReaderIOResult[Config, error, EnrichedUser] {
|
||||||
|
// return enrichUser(user)
|
||||||
|
// })
|
||||||
|
// result := enrichUsers(map[string]User{"alice": user1, "bob": user2})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func TraverseRecord[K comparable, R, A, B any](f Kleisli[R, A, B]) Kleisli[R, map[K]A, map[K]B] {
|
||||||
|
return RIOE.TraverseRecord[K](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraverseRecordWithIndex is like TraverseRecord but the transformation function also receives the key.
|
||||||
|
//
|
||||||
|
// This is useful when the transformation depends on the key associated with each value.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - K: The key type (must be comparable)
|
||||||
|
// - E: The error type
|
||||||
|
// - A: The input value type
|
||||||
|
// - B: The output value type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - f: A function that transforms each key-value pair into a ReaderIOResult
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A function that takes a map and returns a ReaderIOResult of a map
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// processWithKey := TraverseRecordWithIndex(func(key string, val int) ReaderIOResult[Config, error, string] {
|
||||||
|
// return Of[Config, error](fmt.Sprintf("%s: %d", key, val))
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func TraverseRecordWithIndex[K comparable, R, A, B any](f func(K, A) ReaderIOResult[R, B]) Kleisli[R, map[K]A, map[K]B] {
|
||||||
|
return RIOE.TraverseRecordWithIndex[K](f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SequenceRecord converts a map of ReaderIOResult into a ReaderIOResult of a map.
|
||||||
|
//
|
||||||
|
// This is useful when you have multiple independent computations keyed by some identifier
|
||||||
|
// and want to execute them all and collect their results. If any computation fails,
|
||||||
|
// the entire operation fails with the first error.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - K: The key type (must be comparable)
|
||||||
|
// - E: The error type
|
||||||
|
// - A: The value type
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - ma: A map of ReaderIOResult computations
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// A ReaderIOResult that produces a map of results
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// computations := map[string]ReaderIOResult[Config, error, int]{
|
||||||
|
// "users": fetchCount("users"),
|
||||||
|
// "posts": fetchCount("posts"),
|
||||||
|
// }
|
||||||
|
// result := SequenceRecord(computations)
|
||||||
|
// // result(cfg)() returns Right(map[string]int{"users": 100, "posts": 50}) or Left(error)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
|
func SequenceRecord[K comparable, R, A any](ma map[K]ReaderIOResult[R, A]) ReaderIOResult[R, map[K]A] {
|
||||||
|
return RIOE.SequenceRecord(ma)
|
||||||
|
}
|
||||||
100
v2/readerioresult/types.go
Normal file
100
v2/readerioresult/types.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// 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 readerioresult
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IBM/fp-go/v2/endomorphism"
|
||||||
|
"github.com/IBM/fp-go/v2/io"
|
||||||
|
"github.com/IBM/fp-go/v2/ioeither"
|
||||||
|
"github.com/IBM/fp-go/v2/ioresult"
|
||||||
|
"github.com/IBM/fp-go/v2/lazy"
|
||||||
|
"github.com/IBM/fp-go/v2/option"
|
||||||
|
"github.com/IBM/fp-go/v2/reader"
|
||||||
|
"github.com/IBM/fp-go/v2/readerio"
|
||||||
|
"github.com/IBM/fp-go/v2/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Either represents a value of one of two possible types (a disjoint union).
|
||||||
|
// An instance of Either is either Left (representing an error) or Right (representing a success).
|
||||||
|
Either[E, A any] = Result[A]
|
||||||
|
|
||||||
|
Result[A any] = result.Result[A]
|
||||||
|
|
||||||
|
// Reader represents a computation that depends on some context/environment of type R
|
||||||
|
// and produces a value of type A. It's useful for dependency injection patterns.
|
||||||
|
Reader[R, A any] = reader.Reader[R, A]
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
|
||||||
|
// IOEither represents a computation that performs side effects and can either
|
||||||
|
// fail with an error of type E or succeed with a value of type A.
|
||||||
|
IOEither[E, A any] = ioeither.IOEither[E, A]
|
||||||
|
|
||||||
|
IOResult[A any] = ioresult.IOResult[A]
|
||||||
|
|
||||||
|
IO[A any] = io.IO[A]
|
||||||
|
|
||||||
|
Lazy[A any] = lazy.Lazy[A]
|
||||||
|
|
||||||
|
Option[A any] = option.Option[A]
|
||||||
|
|
||||||
|
Endomorphism[A any] = endomorphism.Endomorphism[A]
|
||||||
|
|
||||||
|
// ReaderIOResult represents a computation that:
|
||||||
|
// - Depends on some context/environment of type R (Reader)
|
||||||
|
// - Performs side effects (IO)
|
||||||
|
// - Can fail with an error of type E or succeed with a value of type A (Either)
|
||||||
|
//
|
||||||
|
// It combines three powerful functional programming concepts:
|
||||||
|
// 1. Reader monad for dependency injection
|
||||||
|
// 2. IO monad for side effects
|
||||||
|
// 3. Either monad for error handling
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The type of the context/environment (e.g., configuration, dependencies)
|
||||||
|
// - E: The type of errors that can occur
|
||||||
|
// - A: The type of the success value
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// type Config struct { BaseURL string }
|
||||||
|
// func fetchUser(id int) ReaderIOResult[Config, error, User] {
|
||||||
|
// return func(cfg Config) IOEither[error, User] {
|
||||||
|
// return func() Either[error, User] {
|
||||||
|
// // Use cfg.BaseURL to fetch user
|
||||||
|
// // Return either.Right(user) or either.Left(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
ReaderIOResult[R, A any] = Reader[R, IOResult[A]]
|
||||||
|
|
||||||
|
Kleisli[R, A, B any] = reader.Reader[A, ReaderIOResult[R, B]]
|
||||||
|
|
||||||
|
// Operator represents a transformation from one ReaderIOResult to another.
|
||||||
|
// It's a Reader that takes a ReaderIOResult[R, A] and produces a ReaderIOResult[R, B].
|
||||||
|
// This type is commonly used for composing operations in a point-free style.
|
||||||
|
//
|
||||||
|
// Type parameters:
|
||||||
|
// - R: The context type
|
||||||
|
// - A: The input value type
|
||||||
|
// - B: The output value type
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// var doubleOp Operator[Config, error, int, int] = Map(func(x int) int { return x * 2 })
|
||||||
|
Operator[R, A, B any] = Kleisli[R, ReaderIOResult[R, A], B]
|
||||||
|
)
|
||||||
@@ -116,7 +116,7 @@ func FromEither[S, R, E, A any](ma Either[E, A]) StateReaderIOEither[S, R, E, A]
|
|||||||
|
|
||||||
func Local[S, E, A, B, R1, R2 any](f func(R2) R1) func(StateReaderIOEither[S, R1, E, A]) StateReaderIOEither[S, R2, E, A] {
|
func Local[S, E, A, B, R1, R2 any](f func(R2) R1) func(StateReaderIOEither[S, R1, E, A]) StateReaderIOEither[S, R2, E, A] {
|
||||||
return func(ma StateReaderIOEither[S, R1, E, A]) StateReaderIOEither[S, R2, E, A] {
|
return func(ma StateReaderIOEither[S, R1, E, A]) StateReaderIOEither[S, R2, E, A] {
|
||||||
return function.Flow2(ma, readerioeither.Local[R1, R2, E, Pair[S, A]](f))
|
return function.Flow2(ma, readerioeither.Local[E, Pair[S, A]](f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user