Skip to content

Commit aa5e908

Browse files
committed
fix: introduce Kleisli type
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
1 parent b3bd5e9 commit aa5e908

90 files changed

Lines changed: 9616 additions & 5168 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

v2/context/readereither/array.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ package readereither
1818
import "github.com/IBM/fp-go/v2/readereither"
1919

2020
// TraverseArray transforms an array
21-
func TraverseArray[A, B any](f func(A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
21+
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
2222
return readereither.TraverseArray(f)
2323
}
2424

2525
// TraverseArrayWithIndex transforms an array
26-
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) func([]A) ReaderEither[[]B] {
26+
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderEither[B]) Kleisli[[]A, []B] {
2727
return readereither.TraverseArrayWithIndex(f)
2828
}
2929

v2/context/readereither/bind.go

Lines changed: 162 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package readereither
1818
import (
1919
"context"
2020

21+
L "github.com/IBM/fp-go/v2/optics/lens"
2122
G "github.com/IBM/fp-go/v2/readereither/generic"
2223
)
2324

@@ -80,31 +81,31 @@ func Do[S any](
8081
// )
8182
func Bind[S1, S2, T any](
8283
setter func(T) func(S1) S2,
83-
f func(S1) ReaderEither[T],
84-
) func(ReaderEither[S1]) ReaderEither[S2] {
84+
f Kleisli[S1, T],
85+
) Kleisli[ReaderEither[S1], S2] {
8586
return G.Bind[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, f)
8687
}
8788

8889
// Let attaches the result of a computation to a context [S1] to produce a context [S2]
8990
func Let[S1, S2, T any](
9091
setter func(T) func(S1) S2,
9192
f func(S1) T,
92-
) func(ReaderEither[S1]) ReaderEither[S2] {
93+
) Kleisli[ReaderEither[S1], S2] {
9394
return G.Let[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, f)
9495
}
9596

9697
// LetTo attaches the a value to a context [S1] to produce a context [S2]
9798
func LetTo[S1, S2, T any](
9899
setter func(T) func(S1) S2,
99100
b T,
100-
) func(ReaderEither[S1]) ReaderEither[S2] {
101+
) Kleisli[ReaderEither[S1], S2] {
101102
return G.LetTo[ReaderEither[S1], ReaderEither[S2], context.Context, error, S1, S2, T](setter, b)
102103
}
103104

104105
// BindTo initializes a new state [S1] from a value [T]
105106
func BindTo[S1, T any](
106107
setter func(T) S1,
107-
) func(ReaderEither[T]) ReaderEither[S1] {
108+
) Kleisli[ReaderEither[T], S1] {
108109
return G.BindTo[ReaderEither[S1], ReaderEither[T], context.Context, error, S1, T](setter)
109110
}
110111

@@ -148,6 +149,161 @@ func BindTo[S1, T any](
148149
func ApS[S1, S2, T any](
149150
setter func(T) func(S1) S2,
150151
fa ReaderEither[T],
151-
) func(ReaderEither[S1]) ReaderEither[S2] {
152+
) Kleisli[ReaderEither[S1], S2] {
152153
return G.ApS[ReaderEither[S1], ReaderEither[S2], ReaderEither[T], context.Context, error, S1, S2, T](setter, fa)
153154
}
155+
156+
// ApSL is a variant of ApS that uses a lens to focus on a specific field in the state.
157+
// Instead of providing a setter function, you provide a lens that knows how to get and set
158+
// the field. This is more convenient when working with nested structures.
159+
//
160+
// Parameters:
161+
// - lens: A lens that focuses on a field of type T within state S
162+
// - fa: A ReaderEither computation that produces a value of type T
163+
//
164+
// Returns:
165+
// - A function that transforms ReaderEither[S] to ReaderEither[S] by setting the focused field
166+
//
167+
// Example:
168+
//
169+
// type Person struct {
170+
// Name string
171+
// Age int
172+
// }
173+
//
174+
// ageLens := lens.MakeLens(
175+
// func(p Person) int { return p.Age },
176+
// func(p Person, a int) Person { p.Age = a; return p },
177+
// )
178+
//
179+
// getAge := func(ctx context.Context) either.Either[error, int] {
180+
// return either.Right[error](30)
181+
// }
182+
//
183+
// result := F.Pipe1(
184+
// readereither.Do(Person{Name: "Alice", Age: 25}),
185+
// readereither.ApSL(ageLens, getAge),
186+
// )
187+
func ApSL[S, T any](
188+
lens L.Lens[S, T],
189+
fa ReaderEither[T],
190+
) Kleisli[ReaderEither[S], S] {
191+
return ApS(lens.Set, fa)
192+
}
193+
194+
// BindL is a variant of Bind that uses a lens to focus on a specific field in the state.
195+
// It combines the lens-based field access with monadic composition, allowing you to:
196+
// 1. Extract a field value using the lens
197+
// 2. Use that value in a computation that may fail
198+
// 3. Update the field with the result
199+
//
200+
// Parameters:
201+
// - lens: A lens that focuses on a field of type T within state S
202+
// - f: A function that takes the current field value and returns a ReaderEither computation
203+
//
204+
// Returns:
205+
// - A function that transforms ReaderEither[S] to ReaderEither[S]
206+
//
207+
// Example:
208+
//
209+
// type Counter struct {
210+
// Value int
211+
// }
212+
//
213+
// valueLens := lens.MakeLens(
214+
// func(c Counter) int { return c.Value },
215+
// func(c Counter, v int) Counter { c.Value = v; return c },
216+
// )
217+
//
218+
// increment := func(v int) readereither.ReaderEither[int] {
219+
// return func(ctx context.Context) either.Either[error, int] {
220+
// if v >= 100 {
221+
// return either.Left[int](errors.New("value too large"))
222+
// }
223+
// return either.Right[error](v + 1)
224+
// }
225+
// }
226+
//
227+
// result := F.Pipe1(
228+
// readereither.Of[error](Counter{Value: 42}),
229+
// readereither.BindL(valueLens, increment),
230+
// )
231+
func BindL[S, T any](
232+
lens L.Lens[S, T],
233+
f Kleisli[T, T],
234+
) Kleisli[ReaderEither[S], S] {
235+
return Bind[S, S, T](lens.Set, func(s S) ReaderEither[T] {
236+
return f(lens.Get(s))
237+
})
238+
}
239+
240+
// LetL is a variant of Let that uses a lens to focus on a specific field in the state.
241+
// It applies a pure transformation to the focused field without any effects.
242+
//
243+
// Parameters:
244+
// - lens: A lens that focuses on a field of type T within state S
245+
// - f: A pure function that transforms the field value
246+
//
247+
// Returns:
248+
// - A function that transforms ReaderEither[S] to ReaderEither[S]
249+
//
250+
// Example:
251+
//
252+
// type Counter struct {
253+
// Value int
254+
// }
255+
//
256+
// valueLens := lens.MakeLens(
257+
// func(c Counter) int { return c.Value },
258+
// func(c Counter, v int) Counter { c.Value = v; return c },
259+
// )
260+
//
261+
// double := func(v int) int { return v * 2 }
262+
//
263+
// result := F.Pipe1(
264+
// readereither.Of[error](Counter{Value: 21}),
265+
// readereither.LetL(valueLens, double),
266+
// )
267+
// // result when executed will be Right(Counter{Value: 42})
268+
func LetL[S, T any](
269+
lens L.Lens[S, T],
270+
f func(T) T,
271+
) Kleisli[ReaderEither[S], S] {
272+
return Let[S, S, T](lens.Set, func(s S) T {
273+
return f(lens.Get(s))
274+
})
275+
}
276+
277+
// LetToL is a variant of LetTo that uses a lens to focus on a specific field in the state.
278+
// It sets the focused field to a constant value.
279+
//
280+
// Parameters:
281+
// - lens: A lens that focuses on a field of type T within state S
282+
// - b: The constant value to set
283+
//
284+
// Returns:
285+
// - A function that transforms ReaderEither[S] to ReaderEither[S]
286+
//
287+
// Example:
288+
//
289+
// type Config struct {
290+
// Debug bool
291+
// Timeout int
292+
// }
293+
//
294+
// debugLens := lens.MakeLens(
295+
// func(c Config) bool { return c.Debug },
296+
// func(c Config, d bool) Config { c.Debug = d; return c },
297+
// )
298+
//
299+
// result := F.Pipe1(
300+
// readereither.Of[error](Config{Debug: true, Timeout: 30}),
301+
// readereither.LetToL(debugLens, false),
302+
// )
303+
// // result when executed will be Right(Config{Debug: false, Timeout: 30})
304+
func LetToL[S, T any](
305+
lens L.Lens[S, T],
306+
b T,
307+
) Kleisli[ReaderEither[S], S] {
308+
return LetTo[S, S, T](lens.Set, b)
309+
}

v2/context/readereither/curry.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,26 @@ func Curry0[A any](f func(context.Context) (A, error)) ReaderEither[A] {
2828
return readereither.Curry0(f)
2929
}
3030

31-
func Curry1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
31+
func Curry1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A] {
3232
return readereither.Curry1(f)
3333
}
3434

35-
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) func(T2) ReaderEither[A] {
35+
func Curry2[T1, T2, A any](f func(context.Context, T1, T2) (A, error)) func(T1) Kleisli[T2, A] {
3636
return readereither.Curry2(f)
3737
}
3838

39-
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) func(T3) ReaderEither[A] {
39+
func Curry3[T1, T2, T3, A any](f func(context.Context, T1, T2, T3) (A, error)) func(T1) func(T2) Kleisli[T3, A] {
4040
return readereither.Curry3(f)
4141
}
4242

43-
func Uncurry1[T1, A any](f func(T1) ReaderEither[A]) func(context.Context, T1) (A, error) {
43+
func Uncurry1[T1, A any](f Kleisli[T1, A]) func(context.Context, T1) (A, error) {
4444
return readereither.Uncurry1(f)
4545
}
4646

47-
func Uncurry2[T1, T2, A any](f func(T1) func(T2) ReaderEither[A]) func(context.Context, T1, T2) (A, error) {
47+
func Uncurry2[T1, T2, A any](f func(T1) Kleisli[T2, A]) func(context.Context, T1, T2) (A, error) {
4848
return readereither.Uncurry2(f)
4949
}
5050

51-
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) func(T3) ReaderEither[A]) func(context.Context, T1, T2, T3) (A, error) {
51+
func Uncurry3[T1, T2, T3, A any](f func(T1) func(T2) Kleisli[T3, A]) func(context.Context, T1, T2, T3) (A, error) {
5252
return readereither.Uncurry3(f)
5353
}

v2/context/readereither/from.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func From0[A any](f func(context.Context) (A, error)) func() ReaderEither[A] {
2828
return readereither.From0(f)
2929
}
3030

31-
func From1[T1, A any](f func(context.Context, T1) (A, error)) func(T1) ReaderEither[A] {
31+
func From1[T1, A any](f func(context.Context, T1) (A, error)) Kleisli[T1, A] {
3232
return readereither.From1(f)
3333
}
3434

v2/context/readereither/reader.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ func Map[A, B any](f func(A) B) Operator[A, B] {
4141
return readereither.Map[context.Context, error](f)
4242
}
4343

44-
func MonadChain[A, B any](ma ReaderEither[A], f func(A) ReaderEither[B]) ReaderEither[B] {
44+
func MonadChain[A, B any](ma ReaderEither[A], f Kleisli[A, B]) ReaderEither[B] {
4545
return readereither.MonadChain(ma, f)
4646
}
4747

48-
func Chain[A, B any](f func(A) ReaderEither[B]) Operator[A, B] {
48+
func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] {
4949
return readereither.Chain(f)
5050
}
5151

@@ -61,11 +61,11 @@ func Ap[A, B any](fa ReaderEither[A]) func(ReaderEither[func(A) B]) ReaderEither
6161
return readereither.Ap[B](fa)
6262
}
6363

64-
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) func(A) ReaderEither[A] {
64+
func FromPredicate[A any](pred func(A) bool, onFalse func(A) error) Kleisli[A, A] {
6565
return readereither.FromPredicate[context.Context](pred, onFalse)
6666
}
6767

68-
func OrElse[A any](onLeft func(error) ReaderEither[A]) func(ReaderEither[A]) ReaderEither[A] {
68+
func OrElse[A any](onLeft Kleisli[error, A]) Kleisli[ReaderEither[A], A] {
6969
return readereither.OrElse(onLeft)
7070
}
7171

v2/context/readereither/type.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@ type (
3131
// ReaderEither is a specialization of the Reader monad for the typical golang scenario
3232
ReaderEither[A any] = readereither.ReaderEither[context.Context, error, A]
3333

34-
Operator[A, B any] = reader.Reader[ReaderEither[A], ReaderEither[B]]
34+
Kleisli[A, B any] = reader.Reader[A, ReaderEither[B]]
35+
Operator[A, B any] = Kleisli[ReaderEither[A], B]
3536
)

0 commit comments

Comments
 (0)