diff --git a/v2/array/array.go b/v2/array/array.go index 3735754..b11c015 100644 --- a/v2/array/array.go +++ b/v2/array/array.go @@ -17,11 +17,10 @@ package array import ( G "github.com/IBM/fp-go/v2/array/generic" - EM "github.com/IBM/fp-go/v2/endomorphism" F "github.com/IBM/fp-go/v2/function" "github.com/IBM/fp-go/v2/internal/array" M "github.com/IBM/fp-go/v2/monoid" - O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/option" "github.com/IBM/fp-go/v2/tuple" ) @@ -50,16 +49,16 @@ func Replicate[A any](n int, a A) []A { // This is the monadic version of Map that takes the array as the first parameter. // //go:inline -func MonadMap[A, B any](as []A, f func(a A) B) []B { +func MonadMap[A, B any](as []A, f func(A) B) []B { return G.MonadMap[[]A, []B](as, f) } // MonadMapRef applies a function to a pointer to each element of an array, returning a new array with the results. // This is useful when you need to access elements by reference without copying. -func MonadMapRef[A, B any](as []A, f func(a *A) B) []B { +func MonadMapRef[A, B any](as []A, f func(*A) B) []B { count := len(as) bs := make([]B, count) - for i := count - 1; i >= 0; i-- { + for i := range count { bs[i] = f(&as[i]) } return bs @@ -68,7 +67,7 @@ func MonadMapRef[A, B any](as []A, f func(a *A) B) []B { // MapWithIndex applies a function to each element and its index in an array, returning a new array with the results. // //go:inline -func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B { +func MapWithIndex[A, B any](f func(int, A) B) Operator[A, B] { return G.MapWithIndex[[]A, []B](f) } @@ -81,35 +80,35 @@ func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B { // result := double([]int{1, 2, 3}) // [2, 4, 6] // //go:inline -func Map[A, B any](f func(a A) B) func([]A) []B { +func Map[A, B any](f func(A) B) Operator[A, B] { return G.Map[[]A, []B](f) } // MapRef applies a function to a pointer to each element of an array, returning a new array with the results. // This is the curried version that returns a function. -func MapRef[A, B any](f func(a *A) B) func([]A) []B { +func MapRef[A, B any](f func(*A) B) Operator[A, B] { return F.Bind2nd(MonadMapRef[A, B], f) } -func filterRef[A any](fa []A, pred func(a *A) bool) []A { - var result []A +func filterRef[A any](fa []A, pred func(*A) bool) []A { count := len(fa) - for i := 0; i < count; i++ { - a := fa[i] - if pred(&a) { - result = append(result, a) + var result []A = make([]A, 0, count) + for i := range count { + a := &fa[i] + if pred(a) { + result = append(result, *a) } } return result } -func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B { - var result []B +func filterMapRef[A, B any](fa []A, pred func(*A) bool, f func(*A) B) []B { count := len(fa) - for i := 0; i < count; i++ { - a := fa[i] - if pred(&a) { - result = append(result, f(&a)) + var result []B = make([]B, 0, count) + for i := range count { + a := &fa[i] + if pred(a) { + result = append(result, f(a)) } } return result @@ -118,19 +117,19 @@ func filterMapRef[A, B any](fa []A, pred func(a *A) bool, f func(a *A) B) []B { // Filter returns a new array with all elements from the original array that match a predicate // //go:inline -func Filter[A any](pred func(A) bool) EM.Endomorphism[[]A] { +func Filter[A any](pred func(A) bool) Operator[A, A] { return G.Filter[[]A](pred) } // FilterWithIndex returns a new array with all elements from the original array that match a predicate // //go:inline -func FilterWithIndex[A any](pred func(int, A) bool) EM.Endomorphism[[]A] { +func FilterWithIndex[A any](pred func(int, A) bool) Operator[A, A] { return G.FilterWithIndex[[]A](pred) } // FilterRef returns a new array with all elements from the original array that match a predicate operating on pointers. -func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] { +func FilterRef[A any](pred func(*A) bool) Operator[A, A] { return F.Bind2nd(filterRef[A], pred) } @@ -138,7 +137,7 @@ func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] { // This is the monadic version that takes the array as the first parameter. // //go:inline -func MonadFilterMap[A, B any](fa []A, f func(A) O.Option[B]) []B { +func MonadFilterMap[A, B any](fa []A, f option.Kleisli[A, B]) []B { return G.MonadFilterMap[[]A, []B](fa, f) } @@ -146,33 +145,33 @@ func MonadFilterMap[A, B any](fa []A, f func(A) O.Option[B]) []B { // keeping only the Some values. This is the monadic version that takes the array as the first parameter. // //go:inline -func MonadFilterMapWithIndex[A, B any](fa []A, f func(int, A) O.Option[B]) []B { +func MonadFilterMapWithIndex[A, B any](fa []A, f func(int, A) Option[B]) []B { return G.MonadFilterMapWithIndex[[]A, []B](fa, f) } -// FilterMap maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones. +// FilterMap maps an array with an iterating function that returns an [Option] and it keeps only the Some values discarding the Nones. // //go:inline -func FilterMap[A, B any](f func(A) O.Option[B]) func([]A) []B { +func FilterMap[A, B any](f option.Kleisli[A, B]) Operator[A, B] { return G.FilterMap[[]A, []B](f) } -// FilterMapWithIndex maps an array with an iterating function that returns an [O.Option] and it keeps only the Some values discarding the Nones. +// FilterMapWithIndex maps an array with an iterating function that returns an [Option] and it keeps only the Some values discarding the Nones. // //go:inline -func FilterMapWithIndex[A, B any](f func(int, A) O.Option[B]) func([]A) []B { +func FilterMapWithIndex[A, B any](f func(int, A) Option[B]) Operator[A, B] { return G.FilterMapWithIndex[[]A, []B](f) } -// FilterChain maps an array with an iterating function that returns an [O.Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result. +// FilterChain maps an array with an iterating function that returns an [Option] of an array. It keeps only the Some values discarding the Nones and then flattens the result. // //go:inline -func FilterChain[A, B any](f func(A) O.Option[[]B]) func([]A) []B { +func FilterChain[A, B any](f option.Kleisli[A, []B]) Operator[A, B] { return G.FilterChain[[]A](f) } // FilterMapRef filters an array using a predicate on pointers and maps the matching elements using a function on pointers. -func FilterMapRef[A, B any](pred func(a *A) bool, f func(a *A) B) func([]A) []B { +func FilterMapRef[A, B any](pred func(a *A) bool, f func(*A) B) Operator[A, B] { return func(fa []A) []B { return filterMapRef(fa, pred, f) } @@ -180,8 +179,7 @@ func FilterMapRef[A, B any](pred func(a *A) bool, f func(a *A) B) func([]A) []B func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B { current := initial - count := len(fa) - for i := 0; i < count; i++ { + for i := range len(fa) { current = f(current, &fa[i]) } return current @@ -277,7 +275,7 @@ func Of[A any](a A) []A { // This is the monadic version that takes the array as the first parameter (also known as FlatMap). // //go:inline -func MonadChain[A, B any](fa []A, f func(a A) []B) []B { +func MonadChain[A, B any](fa []A, f Kleisli[A, B]) []B { return G.MonadChain(fa, f) } @@ -290,7 +288,7 @@ func MonadChain[A, B any](fa []A, f func(a A) []B) []B { // result := duplicate([]int{1, 2, 3}) // [1, 1, 2, 2, 3, 3] // //go:inline -func Chain[A, B any](f func(A) []B) func([]A) []B { +func Chain[A, B any](f Kleisli[A, B]) Operator[A, B] { return G.Chain[[]A](f) } @@ -306,7 +304,7 @@ func MonadAp[B, A any](fab []func(A) B, fa []A) []B { // This is the curried version. // //go:inline -func Ap[B, A any](fa []A) func([]func(A) B) []B { +func Ap[B, A any](fa []A) Operator[func(A) B, B] { return G.Ap[[]B, []func(A) B](fa) } @@ -328,7 +326,7 @@ func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) // Returns None if the array is empty. // //go:inline -func Tail[A any](as []A) O.Option[[]A] { +func Tail[A any](as []A) Option[[]A] { return G.Tail(as) } @@ -336,7 +334,7 @@ func Tail[A any](as []A) O.Option[[]A] { // Returns None if the array is empty. // //go:inline -func Head[A any](as []A) O.Option[A] { +func Head[A any](as []A) Option[A] { return G.Head(as) } @@ -344,7 +342,7 @@ func Head[A any](as []A) O.Option[A] { // Returns None if the array is empty. // //go:inline -func First[A any](as []A) O.Option[A] { +func First[A any](as []A) Option[A] { return G.First(as) } @@ -352,12 +350,12 @@ func First[A any](as []A) O.Option[A] { // Returns None if the array is empty. // //go:inline -func Last[A any](as []A) O.Option[A] { +func Last[A any](as []A) Option[A] { return G.Last(as) } // PrependAll inserts a separator before each element of an array. -func PrependAll[A any](middle A) EM.Endomorphism[[]A] { +func PrependAll[A any](middle A) Operator[A, A] { return func(as []A) []A { count := len(as) dst := count * 2 @@ -377,7 +375,7 @@ func PrependAll[A any](middle A) EM.Endomorphism[[]A] { // Example: // // result := array.Intersperse(0)([]int{1, 2, 3}) // [1, 0, 2, 0, 3] -func Intersperse[A any](middle A) EM.Endomorphism[[]A] { +func Intersperse[A any](middle A) Operator[A, A] { prepend := PrependAll(middle) return func(as []A) []A { if IsEmpty(as) { @@ -406,7 +404,7 @@ func Flatten[A any](mma [][]A) []A { } // Slice extracts a subarray from index low (inclusive) to high (exclusive). -func Slice[A any](low, high int) func(as []A) []A { +func Slice[A any](low, high int) Operator[A, A] { return array.Slice[[]A](low, high) } @@ -414,7 +412,7 @@ func Slice[A any](low, high int) func(as []A) []A { // Returns None if the index is out of bounds. // //go:inline -func Lookup[A any](idx int) func([]A) O.Option[A] { +func Lookup[A any](idx int) func([]A) Option[A] { return G.Lookup[[]A](idx) } @@ -422,7 +420,7 @@ func Lookup[A any](idx int) func([]A) O.Option[A] { // If the index is out of bounds, the element is appended. // //go:inline -func UpsertAt[A any](a A) EM.Endomorphism[[]A] { +func UpsertAt[A any](a A) Operator[A, A] { return G.UpsertAt[[]A](a) } @@ -468,7 +466,7 @@ func ConstNil[A any]() []A { // SliceRight extracts a subarray from the specified start index to the end. // //go:inline -func SliceRight[A any](start int) EM.Endomorphism[[]A] { +func SliceRight[A any](start int) Operator[A, A] { return G.SliceRight[[]A](start) } @@ -482,7 +480,7 @@ func Copy[A any](b []A) []A { // Clone creates a deep copy of the array using the provided endomorphism to clone the values // //go:inline -func Clone[A any](f func(A) A) func(as []A) []A { +func Clone[A any](f func(A) A) Operator[A, A] { return G.Clone[[]A](f) } @@ -510,8 +508,8 @@ func Fold[A any](m M.Monoid[A]) func([]A) A { // Push adds an element to the end of an array (alias for Append). // //go:inline -func Push[A any](a A) EM.Endomorphism[[]A] { - return G.Push[EM.Endomorphism[[]A]](a) +func Push[A any](a A) Operator[A, A] { + return G.Push[Operator[A, A]](a) } // MonadFlap applies a value to an array of functions, producing an array of results. @@ -526,13 +524,13 @@ func MonadFlap[B, A any](fab []func(A) B, a A) []B { // This is the curried version. // //go:inline -func Flap[B, A any](a A) func([]func(A) B) []B { +func Flap[B, A any](a A) Operator[func(A) B, B] { return G.Flap[func(A) B, []func(A) B, []B](a) } // Prepend adds an element to the beginning of an array, returning a new array. // //go:inline -func Prepend[A any](head A) EM.Endomorphism[[]A] { - return G.Prepend[EM.Endomorphism[[]A]](head) +func Prepend[A any](head A) Operator[A, A] { + return G.Prepend[Operator[A, A]](head) } diff --git a/v2/array/bind.go b/v2/array/bind.go index 8fd29d1..5948538 100644 --- a/v2/array/bind.go +++ b/v2/array/bind.go @@ -56,8 +56,8 @@ func Do[S any]( //go:inline func Bind[S1, S2, T any]( setter func(T) func(S1) S2, - f func(S1) []T, -) func([]S1) []S2 { + f Kleisli[S1, T], +) Operator[S1, S2] { return G.Bind[[]S1, []S2](setter, f) } @@ -79,7 +79,7 @@ func Bind[S1, S2, T any]( func Let[S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) T, -) func([]S1) []S2 { +) Operator[S1, S2] { return G.Let[[]S1, []S2](setter, f) } @@ -101,7 +101,7 @@ func Let[S1, S2, T any]( func LetTo[S1, S2, T any]( setter func(T) func(S1) S2, b T, -) func([]S1) []S2 { +) Operator[S1, S2] { return G.LetTo[[]S1, []S2](setter, b) } @@ -120,7 +120,7 @@ func LetTo[S1, S2, T any]( //go:inline func BindTo[S1, T any]( setter func(T) S1, -) func([]T) []S1 { +) Operator[T, S1] { return G.BindTo[[]S1, []T](setter) } @@ -143,6 +143,6 @@ func BindTo[S1, T any]( func ApS[S1, S2, T any]( setter func(T) func(S1) S2, fa []T, -) func([]S1) []S2 { +) Operator[S1, S2] { return G.ApS[[]S1, []S2](setter, fa) } diff --git a/v2/array/find.go b/v2/array/find.go index 83f6004..93d179e 100644 --- a/v2/array/find.go +++ b/v2/array/find.go @@ -17,7 +17,7 @@ package array import ( G "github.com/IBM/fp-go/v2/array/generic" - O "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/option" ) // FindFirst finds the first element which satisfies a predicate function. @@ -30,7 +30,7 @@ import ( // result2 := findGreaterThan3([]int{1, 2, 3}) // None // //go:inline -func FindFirst[A any](pred func(A) bool) func([]A) O.Option[A] { +func FindFirst[A any](pred func(A) bool) option.Kleisli[[]A, A] { return G.FindFirst[[]A](pred) } @@ -45,7 +45,7 @@ func FindFirst[A any](pred func(A) bool) func([]A) O.Option[A] { // result := findEvenAtEvenIndex([]int{1, 3, 4, 5}) // Some(4) // //go:inline -func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] { +func FindFirstWithIndex[A any](pred func(int, A) bool) option.Kleisli[[]A, A] { return G.FindFirstWithIndex[[]A](pred) } @@ -65,7 +65,7 @@ func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] { // result := parseFirst([]string{"a", "42", "b"}) // Some(42) // //go:inline -func FindFirstMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] { +func FindFirstMap[A, B any](sel option.Kleisli[A, B]) option.Kleisli[[]A, B] { return G.FindFirstMap[[]A](sel) } @@ -73,7 +73,7 @@ func FindFirstMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] { // The selector receives both the index and the element. // //go:inline -func FindFirstMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] { +func FindFirstMapWithIndex[A, B any](sel func(int, A) Option[B]) option.Kleisli[[]A, B] { return G.FindFirstMapWithIndex[[]A](sel) } @@ -86,7 +86,7 @@ func FindFirstMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.O // result := findGreaterThan3([]int{1, 4, 2, 5}) // Some(5) // //go:inline -func FindLast[A any](pred func(A) bool) func([]A) O.Option[A] { +func FindLast[A any](pred func(A) bool) option.Kleisli[[]A, A] { return G.FindLast[[]A](pred) } @@ -94,7 +94,7 @@ func FindLast[A any](pred func(A) bool) func([]A) O.Option[A] { // Returns Some(element) if found, None if no element matches. // //go:inline -func FindLastWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] { +func FindLastWithIndex[A any](pred func(int, A) bool) option.Kleisli[[]A, A] { return G.FindLastWithIndex[[]A](pred) } @@ -102,7 +102,7 @@ func FindLastWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] { // This combines finding and mapping in a single operation, searching from the end. // //go:inline -func FindLastMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] { +func FindLastMap[A, B any](sel option.Kleisli[A, B]) option.Kleisli[[]A, B] { return G.FindLastMap[[]A](sel) } @@ -110,6 +110,6 @@ func FindLastMap[A, B any](sel func(A) O.Option[B]) func([]A) O.Option[B] { // The selector receives both the index and the element, searching from the end. // //go:inline -func FindLastMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.Option[B] { +func FindLastMapWithIndex[A, B any](sel func(int, A) Option[B]) option.Kleisli[[]A, B] { return G.FindLastMapWithIndex[[]A](sel) } diff --git a/v2/array/generic/array.go b/v2/array/generic/array.go index ef48c4b..d210dd6 100644 --- a/v2/array/generic/array.go +++ b/v2/array/generic/array.go @@ -82,7 +82,7 @@ func MakeBy[AS ~[]A, F ~func(int) A, A any](n int, f F) AS { } // run the generator function across the input as := make(AS, n) - for i := n - 1; i >= 0; i-- { + for i := range n { as[i] = f(i) } return as @@ -165,10 +165,9 @@ func Size[GA ~[]A, A any](as GA) int { func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB { result := make(GB, 0, len(fa)) for _, a := range fa { - O.Map(func(b B) B { + if b, ok := O.Unwrap(f(a)); ok { result = append(result, b) - return b - })(f(a)) + } } return result } @@ -176,10 +175,9 @@ func filterMap[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(A) O.Option[B]) GB { func filterMapWithIndex[GA ~[]A, GB ~[]B, A, B any](fa GA, f func(int, A) O.Option[B]) GB { result := make(GB, 0, len(fa)) for i, a := range fa { - O.Map(func(b B) B { + if b, ok := O.Unwrap(f(i, a)); ok { result = append(result, b) - return b - })(f(i, a)) + } } return result } diff --git a/v2/array/generic/find.go b/v2/array/generic/find.go index d56d4bd..e9a98e5 100644 --- a/v2/array/generic/find.go +++ b/v2/array/generic/find.go @@ -42,8 +42,7 @@ func FindFirst[AS ~[]A, PRED ~func(A) bool, A any](pred PRED) func(AS) O.Option[ func FindFirstMapWithIndex[AS ~[]A, PRED ~func(int, A) O.Option[B], A, B any](pred PRED) func(AS) O.Option[B] { none := O.None[B]() return func(as AS) O.Option[B] { - count := len(as) - for i := 0; i < count; i++ { + for i := range len(as) { out := pred(i, as[i]) if O.IsSome(out) { return out diff --git a/v2/array/generic/zip.go b/v2/array/generic/zip.go index a19104a..9714ba6 100644 --- a/v2/array/generic/zip.go +++ b/v2/array/generic/zip.go @@ -26,7 +26,7 @@ import ( func ZipWith[AS ~[]A, BS ~[]B, CS ~[]C, FCT ~func(A, B) C, A, B, C any](fa AS, fb BS, f FCT) CS { l := N.Min(len(fa), len(fb)) res := make(CS, l) - for i := l - 1; i >= 0; i-- { + for i := range l { res[i] = f(fa[i], fb[i]) } return res @@ -43,7 +43,7 @@ func Unzip[AS ~[]A, BS ~[]B, CS ~[]T.Tuple2[A, B], A, B any](cs CS) T.Tuple2[AS, l := len(cs) as := make(AS, l) bs := make(BS, l) - for i := l - 1; i >= 0; i-- { + for i := range l { t := cs[i] as[i] = t.F1 bs[i] = t.F2 diff --git a/v2/array/sequence.go b/v2/array/sequence.go index c6aefe1..5a5d47a 100644 --- a/v2/array/sequence.go +++ b/v2/array/sequence.go @@ -86,7 +86,7 @@ func Sequence[A, HKTA, HKTRA, HKTFRA any]( // option.Some(3), // } // result2 := array.ArrayOption[int]()(opts2) // None -func ArrayOption[A any]() func([]O.Option[A]) O.Option[[]A] { +func ArrayOption[A any]() func([]Option[A]) Option[[]A] { return Sequence( O.Of[[]A], O.MonadMap[[]A, func(A) []A], diff --git a/v2/array/sort.go b/v2/array/sort.go index cbcad08..4ecefba 100644 --- a/v2/array/sort.go +++ b/v2/array/sort.go @@ -32,7 +32,7 @@ import ( // // Result: [1, 1, 2, 3, 4, 5, 6, 9] // //go:inline -func Sort[T any](ord O.Ord[T]) func(ma []T) []T { +func Sort[T any](ord O.Ord[T]) Operator[T, T] { return G.Sort[[]T](ord) } @@ -62,7 +62,7 @@ func Sort[T any](ord O.Ord[T]) func(ma []T) []T { // // Result: [{"Bob", 25}, {"Alice", 30}, {"Charlie", 35}] // //go:inline -func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T { +func SortByKey[K, T any](ord O.Ord[K], f func(T) K) Operator[T, T] { return G.SortByKey[[]T](ord, f) } @@ -93,6 +93,6 @@ func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T { // // Result: [{"Jones", "Bob"}, {"Smith", "Alice"}, {"Smith", "John"}] // //go:inline -func SortBy[T any](ord []O.Ord[T]) func(ma []T) []T { +func SortBy[T any](ord []O.Ord[T]) Operator[T, T] { return G.SortBy[[]T](ord) } diff --git a/v2/array/types.go b/v2/array/types.go new file mode 100644 index 0000000..8c41ca5 --- /dev/null +++ b/v2/array/types.go @@ -0,0 +1,9 @@ +package array + +import "github.com/IBM/fp-go/v2/option" + +type ( + Kleisli[A, B any] = func(A) []B + Operator[A, B any] = Kleisli[[]A, B] + Option[A any] = option.Option[A] +) diff --git a/v2/array/uniq.go b/v2/array/uniq.go index 32256e4..b30054f 100644 --- a/v2/array/uniq.go +++ b/v2/array/uniq.go @@ -46,6 +46,6 @@ func StrictUniq[A comparable](as []A) []A { // // Result: [{"Alice", 30}, {"Bob", 25}, {"Charlie", 30}] // //go:inline -func Uniq[A any, K comparable](f func(A) K) func(as []A) []A { +func Uniq[A any, K comparable](f func(A) K) Operator[A, A] { return G.Uniq[[]A](f) } diff --git a/v2/constant/monoid.go b/v2/constant/monoid.go new file mode 100644 index 0000000..5424f7a --- /dev/null +++ b/v2/constant/monoid.go @@ -0,0 +1,11 @@ +package constant + +import ( + "github.com/IBM/fp-go/v2/function" + M "github.com/IBM/fp-go/v2/monoid" +) + +// Monoid returns a [M.Monoid] that returns a constant value in all operations +func Monoid[A any](a A) M.Monoid[A] { + return M.MakeMonoid(function.Constant2[A, A](a), a) +} diff --git a/v2/endomorphism/from.go b/v2/endomorphism/from.go new file mode 100644 index 0000000..ad6f0b1 --- /dev/null +++ b/v2/endomorphism/from.go @@ -0,0 +1,10 @@ +package endomorphism + +import ( + "github.com/IBM/fp-go/v2/function" + S "github.com/IBM/fp-go/v2/semigroup" +) + +func FromSemigroup[A any](s S.Semigroup[A]) Kleisli[A] { + return function.Bind2of2(s.Concat) +} diff --git a/v2/iterator/iter/iter.go b/v2/iterator/iter/iter.go new file mode 100644 index 0000000..a2e602e --- /dev/null +++ b/v2/iterator/iter/iter.go @@ -0,0 +1,892 @@ +// 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 iter provides functional programming utilities for Go 1.23+ iterators. +// +// This package offers a comprehensive set of operations for working with lazy sequences +// using Go's native iter.Seq and iter.Seq2 types. It follows functional programming +// principles and provides monadic operations, transformations, and reductions. +// +// The package supports: +// - Functor operations (Map, MapWithIndex, MapWithKey) +// - Monad operations (Chain, Flatten, Ap) +// - Filtering (Filter, FilterMap, FilterWithIndex, FilterWithKey) +// - Folding and reduction (Reduce, Fold, FoldMap) +// - Sequence construction (Of, From, MakeBy, Replicate) +// - Sequence combination (Zip, Prepend, Append) +// +// All operations are lazy and only execute when the sequence is consumed via iteration. +// +// Example usage: +// +// // Create a sequence and transform it +// seq := From(1, 2, 3, 4, 5) +// doubled := Map(func(x int) int { return x * 2 })(seq) +// +// // Filter and reduce +// evens := Filter(func(x int) bool { return x%2 == 0 })(doubled) +// sum := MonadReduce(evens, func(acc, x int) int { return acc + x }, 0) +// // sum = 20 (2+4+6+8+10 from doubled evens) +package iter + +import ( + "slices" + + I "iter" + + F "github.com/IBM/fp-go/v2/function" + "github.com/IBM/fp-go/v2/internal/functor" + M "github.com/IBM/fp-go/v2/monoid" + "github.com/IBM/fp-go/v2/option" +) + +// Of creates a sequence containing a single element. +// +// Example: +// +// seq := Of(42) +// // yields: 42 +func Of[A any](a A) Seq[A] { + return func(yield Predicate[A]) { + yield(a) + } +} + +// Of2 creates a key-value sequence containing a single key-value pair. +// +// Example: +// +// seq := Of2("key", 100) +// // yields: ("key", 100) +func Of2[K, A any](k K, a A) Seq2[K, A] { + return func(yield func(K, A) bool) { + yield(k, a) + } +} + +// MonadMap transforms each element in a sequence using the provided function. +// This is the monadic version that takes the sequence as the first parameter. +// +// Example: +// +// seq := From(1, 2, 3) +// result := MonadMap(seq, func(x int) int { return x * 2 }) +// // yields: 2, 4, 6 +func MonadMap[A, B any](as Seq[A], f func(A) B) Seq[B] { + return func(yield Predicate[B]) { + for a := range as { + if !yield(f(a)) { + return + } + } + } +} + +// Map returns a function that transforms each element in a sequence. +// This is the curried version of MonadMap. +// +// Example: +// +// double := Map(func(x int) int { return x * 2 }) +// seq := From(1, 2, 3) +// result := double(seq) +// // yields: 2, 4, 6 +// +//go:inline +func Map[A, B any](f func(A) B) Operator[A, B] { + return F.Bind2nd(MonadMap[A, B], f) +} + +// MonadMapWithIndex transforms each element in a sequence using a function that also receives the element's index. +// +// Example: +// +// seq := From("a", "b", "c") +// result := MonadMapWithIndex(seq, func(i int, s string) string { +// return fmt.Sprintf("%d:%s", i, s) +// }) +// // yields: "0:a", "1:b", "2:c" +func MonadMapWithIndex[A, B any](as Seq[A], f func(int, A) B) Seq[B] { + return func(yield Predicate[B]) { + var i int + for a := range as { + if !yield(f(i, a)) { + return + } + i += 1 + } + } +} + +// MapWithIndex returns a function that transforms elements with their indices. +// This is the curried version of MonadMapWithIndex. +// +// Example: +// +// addIndex := MapWithIndex(func(i int, s string) string { +// return fmt.Sprintf("%d:%s", i, s) +// }) +// seq := From("a", "b", "c") +// result := addIndex(seq) +// // yields: "0:a", "1:b", "2:c" +// +//go:inline +func MapWithIndex[A, B any](f func(int, A) B) Operator[A, B] { + return F.Bind2nd(MonadMapWithIndex[A, B], f) +} + +// MonadMapWithKey transforms values in a key-value sequence using a function that receives both key and value. +// +// Example: +// +// seq := Of2("x", 10) +// result := MonadMapWithKey(seq, func(k string, v int) int { return v * 2 }) +// // yields: ("x", 20) +func MonadMapWithKey[K, A, B any](as Seq2[K, A], f func(K, A) B) Seq2[K, B] { + return func(yield func(K, B) bool) { + for k, a := range as { + if !yield(k, f(k, a)) { + return + } + } + } +} + +// MapWithKey returns a function that transforms values using their keys. +// This is the curried version of MonadMapWithKey. +// +// Example: +// +// doubleValue := MapWithKey(func(k string, v int) int { return v * 2 }) +// seq := Of2("x", 10) +// result := doubleValue(seq) +// // yields: ("x", 20) +// +//go:inline +func MapWithKey[K, A, B any](f func(K, A) B) Operator2[K, A, B] { + return F.Bind2nd(MonadMapWithKey[K, A, B], f) +} + +// MonadFilter returns a sequence containing only elements that satisfy the predicate. +// +// Example: +// +// seq := From(1, 2, 3, 4, 5) +// result := MonadFilter(seq, func(x int) bool { return x%2 == 0 }) +// // yields: 2, 4 +func MonadFilter[A any](as Seq[A], pred func(A) bool) Seq[A] { + return func(yield Predicate[A]) { + for a := range as { + if pred(a) { + if !yield(a) { + return + } + } + } + } +} + +// Filter returns a function that filters elements based on a predicate. +// This is the curried version of MonadFilter. +// +// Example: +// +// evens := Filter(func(x int) bool { return x%2 == 0 }) +// seq := From(1, 2, 3, 4, 5) +// result := evens(seq) +// // yields: 2, 4 +// +//go:inline +func Filter[A any](pred func(A) bool) Operator[A, A] { + return F.Bind2nd(MonadFilter[A], pred) +} + +// MonadFilterWithIndex filters elements using a predicate that also receives the element's index. +// +// Example: +// +// seq := From("a", "b", "c", "d") +// result := MonadFilterWithIndex(seq, func(i int, s string) bool { return i%2 == 0 }) +// // yields: "a", "c" (elements at even indices) +func MonadFilterWithIndex[A any](as Seq[A], pred func(int, A) bool) Seq[A] { + return func(yield Predicate[A]) { + var i int + for a := range as { + if pred(i, a) { + if !yield(a) { + return + } + } + i++ + } + } +} + +// FilterWithIndex returns a function that filters elements based on their index and value. +// This is the curried version of MonadFilterWithIndex. +// +// Example: +// +// evenIndices := FilterWithIndex(func(i int, s string) bool { return i%2 == 0 }) +// seq := From("a", "b", "c", "d") +// result := evenIndices(seq) +// // yields: "a", "c" +// +//go:inline +func FilterWithIndex[A any](pred func(int, A) bool) Operator[A, A] { + return F.Bind2nd(MonadFilterWithIndex[A], pred) +} + +// MonadFilterWithKey filters key-value pairs using a predicate that receives both key and value. +// +// Example: +// +// seq := Of2("x", 10) +// result := MonadFilterWithKey(seq, func(k string, v int) bool { return v > 5 }) +// // yields: ("x", 10) +func MonadFilterWithKey[K, A any](as Seq2[K, A], pred func(K, A) bool) Seq2[K, A] { + return func(yield func(K, A) bool) { + for k, a := range as { + if pred(k, a) { + if !yield(k, a) { + return + } + } + } + } +} + +// FilterWithKey returns a function that filters key-value pairs based on a predicate. +// This is the curried version of MonadFilterWithKey. +// +// Example: +// +// largeValues := FilterWithKey(func(k string, v int) bool { return v > 5 }) +// seq := Of2("x", 10) +// result := largeValues(seq) +// // yields: ("x", 10) +// +//go:inline +func FilterWithKey[K, A any](pred func(K, A) bool) Operator2[K, A, A] { + return F.Bind2nd(MonadFilterWithKey[K, A], pred) +} + +// MonadFilterMap applies a function that returns an Option to each element, +// keeping only the Some values and unwrapping them. +// +// Example: +// +// seq := From(1, 2, 3, 4, 5) +// result := MonadFilterMap(seq, func(x int) Option[int] { +// if x%2 == 0 { +// return option.Some(x * 10) +// } +// return option.None[int]() +// }) +// // yields: 20, 40 +func MonadFilterMap[A, B any](as Seq[A], f option.Kleisli[A, B]) Seq[B] { + return func(yield Predicate[B]) { + for a := range as { + if b, ok := option.Unwrap(f(a)); ok { + if !yield(b) { + return + } + } + } + } +} + +// FilterMap returns a function that filters and maps in one operation. +// This is the curried version of MonadFilterMap. +// +// Example: +// +// evenDoubled := FilterMap(func(x int) Option[int] { +// if x%2 == 0 { +// return option.Some(x * 2) +// } +// return option.None[int]() +// }) +// seq := From(1, 2, 3, 4) +// result := evenDoubled(seq) +// // yields: 4, 8 +// +//go:inline +func FilterMap[A, B any](f option.Kleisli[A, B]) Operator[A, B] { + return F.Bind2nd(MonadFilterMap[A, B], f) +} + +// MonadFilterMapWithIndex applies a function with index that returns an Option, +// keeping only the Some values. +// +// Example: +// +// seq := From("a", "b", "c") +// result := MonadFilterMapWithIndex(seq, func(i int, s string) Option[string] { +// if i%2 == 0 { +// return option.Some(fmt.Sprintf("%d:%s", i, s)) +// } +// return option.None[string]() +// }) +// // yields: "0:a", "2:c" +func MonadFilterMapWithIndex[A, B any](as Seq[A], f func(int, A) Option[B]) Seq[B] { + return func(yield Predicate[B]) { + var i int + for a := range as { + if b, ok := option.Unwrap(f(i, a)); ok { + if !yield(b) { + return + } + } + i++ + } + } +} + +// FilterMapWithIndex returns a function that filters and maps with index. +// This is the curried version of MonadFilterMapWithIndex. +// +// Example: +// +// evenIndexed := FilterMapWithIndex(func(i int, s string) Option[string] { +// if i%2 == 0 { +// return option.Some(s) +// } +// return option.None[string]() +// }) +// seq := From("a", "b", "c", "d") +// result := evenIndexed(seq) +// // yields: "a", "c" +// +//go:inline +func FilterMapWithIndex[A, B any](f func(int, A) Option[B]) Operator[A, B] { + return F.Bind2nd(MonadFilterMapWithIndex[A, B], f) +} + +// MonadFilterMapWithKey applies a function with key that returns an Option to key-value pairs, +// keeping only the Some values. +// +// Example: +// +// seq := Of2("x", 10) +// result := MonadFilterMapWithKey(seq, func(k string, v int) Option[int] { +// if v > 5 { +// return option.Some(v * 2) +// } +// return option.None[int]() +// }) +// // yields: ("x", 20) +func MonadFilterMapWithKey[K, A, B any](as Seq2[K, A], f func(K, A) Option[B]) Seq2[K, B] { + return func(yield func(K, B) bool) { + for k, a := range as { + if b, ok := option.Unwrap(f(k, a)); ok { + if !yield(k, b) { + return + } + } + } + } +} + +// FilterMapWithKey returns a function that filters and maps key-value pairs. +// This is the curried version of MonadFilterMapWithKey. +// +// Example: +// +// largeDoubled := FilterMapWithKey(func(k string, v int) Option[int] { +// if v > 5 { +// return option.Some(v * 2) +// } +// return option.None[int]() +// }) +// seq := Of2("x", 10) +// result := largeDoubled(seq) +// // yields: ("x", 20) +// +//go:inline +func FilterMapWithKey[K, A, B any](f func(K, A) Option[B]) Operator2[K, A, B] { + return F.Bind2nd(MonadFilterMapWithKey[K, A, B], f) +} + +// MonadChain applies a function that returns a sequence to each element and flattens the results. +// This is the monadic bind operation (flatMap). +// +// Example: +// +// seq := From(1, 2, 3) +// result := MonadChain(seq, func(x int) Seq[int] { +// return From(x, x*10) +// }) +// // yields: 1, 10, 2, 20, 3, 30 +func MonadChain[A, B any](as Seq[A], f Kleisli[A, B]) Seq[B] { + return func(yield Predicate[B]) { + for a := range as { + for b := range f(a) { + if !yield(b) { + return + } + } + } + } +} + +// Chain returns a function that chains (flatMaps) a sequence transformation. +// This is the curried version of MonadChain. +// +// Example: +// +// duplicate := Chain(func(x int) Seq[int] { return From(x, x) }) +// seq := From(1, 2, 3) +// result := duplicate(seq) +// // yields: 1, 1, 2, 2, 3, 3 +// +//go:inline +func Chain[A, B any](f func(A) Seq[B]) Operator[A, B] { + return F.Bind2nd(MonadChain[A, B], f) +} + +// Flatten flattens a sequence of sequences into a single sequence. +// +// Example: +// +// nested := From(From(1, 2), From(3, 4), From(5)) +// result := Flatten(nested) +// // yields: 1, 2, 3, 4, 5 +// +//go:inline +func Flatten[A any](mma Seq[Seq[A]]) Seq[A] { + return MonadChain(mma, F.Identity[Seq[A]]) +} + +// MonadAp applies a sequence of functions to a sequence of values. +// This is the applicative apply operation. +// +// Example: +// +// fns := From(func(x int) int { return x * 2 }, func(x int) int { return x + 10 }) +// vals := From(5, 3) +// result := MonadAp(fns, vals) +// // yields: 10, 6, 15, 13 (each function applied to each value) +// +//go:inline +func MonadAp[B, A any](fab Seq[func(A) B], fa Seq[A]) Seq[B] { + return MonadChain(fab, F.Bind1st(MonadMap[A, B], fa)) +} + +// Ap returns a function that applies functions to values. +// This is the curried version of MonadAp. +// +// Example: +// +// applyTo5 := Ap(From(5)) +// fns := From(func(x int) int { return x * 2 }, func(x int) int { return x + 10 }) +// result := applyTo5(fns) +// // yields: 10, 15 +// +//go:inline +func Ap[B, A any](fa Seq[A]) Operator[func(A) B, B] { + return F.Bind2nd(MonadAp[B, A], fa) +} + +// From creates a sequence from a variadic list of elements. +// +// Example: +// +// seq := From(1, 2, 3, 4, 5) +// // yields: 1, 2, 3, 4, 5 +// +//go:inline +func From[A any](data ...A) Seq[A] { + return slices.Values(data) +} + +// Empty returns an empty sequence that yields no elements. +// +// Example: +// +// seq := Empty[int]() +// // yields nothing +// +//go:inline +func Empty[A any]() Seq[A] { + return func(_ Predicate[A]) {} +} + +// MakeBy creates a sequence of n elements by applying a function to each index. +// Returns an empty sequence if n <= 0. +// +// Example: +// +// seq := MakeBy(5, func(i int) int { return i * i }) +// // yields: 0, 1, 4, 9, 16 +func MakeBy[A any](n int, f func(int) A) Seq[A] { + // sanity check + if n <= 0 { + return Empty[A]() + } + // run the generator function across the input + return func(yield Predicate[A]) { + for i := range n { + if !yield(f(i)) { + return + } + } + } +} + +// Replicate creates a sequence containing n copies of the same element. +// +// Example: +// +// seq := Replicate(3, "hello") +// // yields: "hello", "hello", "hello" +// +//go:inline +func Replicate[A any](n int, a A) Seq[A] { + return MakeBy(n, F.Constant1[int](a)) +} + +// MonadReduce reduces a sequence to a single value by applying a function to each element +// and an accumulator, starting with an initial value. +// +// Example: +// +// seq := From(1, 2, 3, 4, 5) +// sum := MonadReduce(seq, func(acc, x int) int { return acc + x }, 0) +// // returns: 15 +func MonadReduce[A, B any](fa Seq[A], f func(B, A) B, initial B) B { + current := initial + for a := range fa { + current = f(current, a) + } + return current +} + +// Reduce returns a function that reduces a sequence to a single value. +// This is the curried version of MonadReduce. +// +// Example: +// +// sum := Reduce(func(acc, x int) int { return acc + x }, 0) +// seq := From(1, 2, 3, 4, 5) +// result := sum(seq) +// // returns: 15 +func Reduce[A, B any](f func(B, A) B, initial B) func(Seq[A]) B { + return func(fa Seq[A]) B { + return MonadReduce(fa, f, initial) + } +} + +// MonadReduceWithIndex reduces a sequence using a function that also receives the element's index. +// +// Example: +// +// seq := From(10, 20, 30) +// result := MonadReduceWithIndex(seq, func(i, acc, x int) int { +// return acc + (i * x) +// }, 0) +// // returns: 0*10 + 1*20 + 2*30 = 80 +func MonadReduceWithIndex[A, B any](fa Seq[A], f func(int, B, A) B, initial B) B { + current := initial + var i int + for a := range fa { + current = f(i, current, a) + i += 1 + } + return current +} + +// ReduceWithIndex returns a function that reduces with index. +// This is the curried version of MonadReduceWithIndex. +// +// Example: +// +// weightedSum := ReduceWithIndex(func(i, acc, x int) int { +// return acc + (i * x) +// }, 0) +// seq := From(10, 20, 30) +// result := weightedSum(seq) +// // returns: 80 +func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func(Seq[A]) B { + return func(fa Seq[A]) B { + return MonadReduceWithIndex(fa, f, initial) + } +} + +// MonadReduceWithKey reduces a key-value sequence using a function that receives the key. +// +// Example: +// +// seq := Of2("x", 10) +// result := MonadReduceWithKey(seq, func(k string, acc int, v int) int { +// return acc + v +// }, 0) +// // returns: 10 +func MonadReduceWithKey[K, A, B any](fa Seq2[K, A], f func(K, B, A) B, initial B) B { + current := initial + for k, a := range fa { + current = f(k, current, a) + } + return current +} + +// ReduceWithKey returns a function that reduces key-value pairs. +// This is the curried version of MonadReduceWithKey. +// +// Example: +// +// sumValues := ReduceWithKey(func(k string, acc int, v int) int { +// return acc + v +// }, 0) +// seq := Of2("x", 10) +// result := sumValues(seq) +// // returns: 10 +func ReduceWithKey[K, A, B any](f func(K, B, A) B, initial B) func(Seq2[K, A]) B { + return func(fa Seq2[K, A]) B { + return MonadReduceWithKey(fa, f, initial) + } +} + +// MonadFold folds a sequence using a monoid's concat operation and empty value. +// +// Example: +// +// import "github.com/IBM/fp-go/v2/number" +// seq := From(1, 2, 3, 4, 5) +// sum := MonadFold(seq, number.MonoidSum[int]()) +// // returns: 15 +// +//go:inline +func MonadFold[A any](fa Seq[A], m M.Monoid[A]) A { + return MonadReduce(fa, m.Concat, m.Empty()) +} + +// Fold returns a function that folds a sequence using a monoid. +// This is the curried version of MonadFold. +// +// Example: +// +// import "github.com/IBM/fp-go/v2/number" +// sumAll := Fold(number.MonoidSum[int]()) +// seq := From(1, 2, 3, 4, 5) +// result := sumAll(seq) +// // returns: 15 +// +//go:inline +func Fold[A any](m M.Monoid[A]) func(Seq[A]) A { + return Reduce(m.Concat, m.Empty()) +} + +// MonadFoldMap maps each element to a monoid value and combines them using the monoid. +// +// Example: +// +// import "github.com/IBM/fp-go/v2/string" +// seq := From(1, 2, 3) +// result := MonadFoldMap(seq, func(x int) string { +// return fmt.Sprintf("%d ", x) +// }, string.Monoid) +// // returns: "1 2 3 " +// +//go:inline +func MonadFoldMap[A, B any](fa Seq[A], f func(A) B, m M.Monoid[B]) B { + return MonadReduce(fa, func(b B, a A) B { + return m.Concat(b, f(a)) + }, m.Empty()) +} + +// FoldMap returns a function that maps and folds using a monoid. +// This is the curried version of MonadFoldMap. +// +// Example: +// +// import "github.com/IBM/fp-go/v2/string" +// stringify := FoldMap(string.Monoid)(func(x int) string { +// return fmt.Sprintf("%d ", x) +// }) +// seq := From(1, 2, 3) +// result := stringify(seq) +// // returns: "1 2 3 " +// +//go:inline +func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func(Seq[A]) B { + return func(f func(A) B) func(Seq[A]) B { + return func(as Seq[A]) B { + return MonadFoldMap(as, f, m) + } + } +} + +// MonadFoldMapWithIndex maps each element with its index to a monoid value and combines them. +// +// Example: +// +// import "github.com/IBM/fp-go/v2/string" +// seq := From("a", "b", "c") +// result := MonadFoldMapWithIndex(seq, func(i int, s string) string { +// return fmt.Sprintf("%d:%s ", i, s) +// }, string.Monoid) +// // returns: "0:a 1:b 2:c " +// +//go:inline +func MonadFoldMapWithIndex[A, B any](fa Seq[A], f func(int, A) B, m M.Monoid[B]) B { + return MonadReduceWithIndex(fa, func(i int, b B, a A) B { + return m.Concat(b, f(i, a)) + }, m.Empty()) +} + +// FoldMapWithIndex returns a function that maps with index and folds. +// This is the curried version of MonadFoldMapWithIndex. +// +// Example: +// +// import "github.com/IBM/fp-go/v2/string" +// indexedStringify := FoldMapWithIndex(string.Monoid)(func(i int, s string) string { +// return fmt.Sprintf("%d:%s ", i, s) +// }) +// seq := From("a", "b", "c") +// result := indexedStringify(seq) +// // returns: "0:a 1:b 2:c " +// +//go:inline +func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func(Seq[A]) B { + return func(f func(int, A) B) func(Seq[A]) B { + return func(as Seq[A]) B { + return MonadFoldMapWithIndex(as, f, m) + } + } +} + +// MonadFoldMapWithKey maps each key-value pair to a monoid value and combines them. +// +// Example: +// +// import "github.com/IBM/fp-go/v2/string" +// seq := Of2("x", 10) +// result := MonadFoldMapWithKey(seq, func(k string, v int) string { +// return fmt.Sprintf("%s:%d ", k, v) +// }, string.Monoid) +// // returns: "x:10 " +// +//go:inline +func MonadFoldMapWithKey[K, A, B any](fa Seq2[K, A], f func(K, A) B, m M.Monoid[B]) B { + return MonadReduceWithKey(fa, func(k K, b B, a A) B { + return m.Concat(b, f(k, a)) + }, m.Empty()) +} + +// FoldMapWithKey returns a function that maps with key and folds. +// This is the curried version of MonadFoldMapWithKey. +// +//go:inline +func FoldMapWithKey[K, A, B any](m M.Monoid[B]) func(func(K, A) B) func(Seq2[K, A]) B { + return func(f func(K, A) B) func(Seq2[K, A]) B { + return func(as Seq2[K, A]) B { + return MonadFoldMapWithKey(as, f, m) + } + } +} + +// MonadFlap applies a fixed value to a sequence of functions. +// This is the dual of MonadAp. +// +// Example: +// +// fns := From(func(x int) int { return x * 2 }, func(x int) int { return x + 10 }) +// result := MonadFlap(fns, 5) +// // yields: 10, 15 +// +//go:inline +func MonadFlap[B, A any](fab Seq[func(A) B], a A) Seq[B] { + return functor.MonadFlap(MonadMap[func(A) B, B], fab, a) +} + +// Flap returns a function that applies a fixed value to functions. +// This is the curried version of MonadFlap. +// +//go:inline +func Flap[B, A any](a A) Operator[func(A) B, B] { + return functor.Flap(Map[func(A) B, B], a) +} + +// Prepend returns a function that adds an element to the beginning of a sequence. +// +// Example: +// +// seq := From(2, 3, 4) +// result := Prepend(1)(seq) +// // yields: 1, 2, 3, 4 +// +//go:inline +func Prepend[A any](head A) Operator[A, A] { + return F.Bind1st(concat[A], Of(head)) +} + +// Append returns a function that adds an element to the end of a sequence. +// +// Example: +// +// seq := From(1, 2, 3) +// result := Append(4)(seq) +// // yields: 1, 2, 3, 4 +// +//go:inline +func Append[A any](tail A) Operator[A, A] { + return F.Bind2nd(concat[A], Of(tail)) +} + +// MonadZip combines two sequences into a sequence of pairs. +// The resulting sequence stops when either input sequence is exhausted. +// +// Example: +// +// seqA := From(1, 2, 3) +// seqB := From("a", "b") +// result := MonadZip(seqB, seqA) +// // yields: (1, "a"), (2, "b") +func MonadZip[A, B any](fb Seq[B], fa Seq[A]) Seq2[A, B] { + + return func(yield func(A, B) bool) { + na, sa := I.Pull(fa) + defer sa() + + for b := range fb { + a, ok := na() + if !ok { + return + } + + if !yield(a, b) { + return + } + } + } +} + +// Zip returns a function that zips a sequence with another sequence. +// This is the curried version of MonadZip. +// +// Example: +// +// seqA := From(1, 2, 3) +// zipWithA := Zip(seqA) +// seqB := From("a", "b", "c") +// result := zipWithA(seqB) +// // yields: (1, "a"), (2, "b"), (3, "c") +// +//go:inline +func Zip[A, B any](fa Seq[A]) func(Seq[B]) Seq2[A, B] { + return F.Bind2nd(MonadZip[A, B], fa) +} diff --git a/v2/iterator/iter/iter_test.go b/v2/iterator/iter/iter_test.go new file mode 100644 index 0000000..c2ac3f2 --- /dev/null +++ b/v2/iterator/iter/iter_test.go @@ -0,0 +1,588 @@ +// 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 iter + +import ( + "fmt" + "maps" + "slices" + "strings" + "testing" + + F "github.com/IBM/fp-go/v2/function" + O "github.com/IBM/fp-go/v2/option" + S "github.com/IBM/fp-go/v2/string" + "github.com/stretchr/testify/assert" +) + +// Helper function to collect sequence into a slice +func toSlice[T any](seq Seq[T]) []T { + return slices.Collect(seq) +} + +// Helper function to collect Seq2 into a map +func toMap[K comparable, V any](seq Seq2[K, V]) map[K]V { + return maps.Collect(seq) +} + +func TestOf(t *testing.T) { + seq := Of(42) + result := toSlice(seq) + assert.Equal(t, []int{42}, result) +} + +func TestOf2(t *testing.T) { + seq := Of2("key", 100) + result := toMap(seq) + assert.Equal(t, map[string]int{"key": 100}, result) +} + +func TestFrom(t *testing.T) { + seq := From(1, 2, 3, 4, 5) + result := toSlice(seq) + assert.Equal(t, []int{1, 2, 3, 4, 5}, result) +} + +func TestEmpty(t *testing.T) { + seq := Empty[int]() + result := toSlice(seq) + assert.Empty(t, result) +} + +func TestMonadMap(t *testing.T) { + seq := From(1, 2, 3) + doubled := MonadMap(seq, func(x int) int { return x * 2 }) + result := toSlice(doubled) + assert.Equal(t, []int{2, 4, 6}, result) +} + +func TestMap(t *testing.T) { + seq := From(1, 2, 3) + double := Map(func(x int) int { return x * 2 }) + result := toSlice(double(seq)) + assert.Equal(t, []int{2, 4, 6}, result) +} + +func TestMonadMapWithIndex(t *testing.T) { + seq := From("a", "b", "c") + indexed := MonadMapWithIndex(seq, func(i int, s string) string { + return fmt.Sprintf("%d:%s", i, s) + }) + result := toSlice(indexed) + assert.Equal(t, []string{"0:a", "1:b", "2:c"}, result) +} + +func TestMapWithIndex(t *testing.T) { + seq := From("a", "b", "c") + indexer := MapWithIndex(func(i int, s string) string { + return fmt.Sprintf("%d:%s", i, s) + }) + result := toSlice(indexer(seq)) + assert.Equal(t, []string{"0:a", "1:b", "2:c"}, result) +} + +func TestMonadMapWithKey(t *testing.T) { + seq := Of2("x", 10) + doubled := MonadMapWithKey(seq, func(k string, v int) int { return v * 2 }) + result := toMap(doubled) + assert.Equal(t, map[string]int{"x": 20}, result) +} + +func TestMapWithKey(t *testing.T) { + seq := Of2("x", 10) + doubler := MapWithKey(func(k string, v int) int { return v * 2 }) + result := toMap(doubler(seq)) + assert.Equal(t, map[string]int{"x": 20}, result) +} + +func TestMonadFilter(t *testing.T) { + seq := From(1, 2, 3, 4, 5) + evens := MonadFilter(seq, func(x int) bool { return x%2 == 0 }) + result := toSlice(evens) + assert.Equal(t, []int{2, 4}, result) +} + +func TestFilter(t *testing.T) { + seq := From(1, 2, 3, 4, 5) + isEven := Filter(func(x int) bool { return x%2 == 0 }) + result := toSlice(isEven(seq)) + assert.Equal(t, []int{2, 4}, result) +} + +func TestMonadFilterWithIndex(t *testing.T) { + seq := From("a", "b", "c", "d") + oddIndices := MonadFilterWithIndex(seq, func(i int, _ string) bool { return i%2 == 1 }) + result := toSlice(oddIndices) + assert.Equal(t, []string{"b", "d"}, result) +} + +func TestFilterWithIndex(t *testing.T) { + seq := From("a", "b", "c", "d") + oddIndexFilter := FilterWithIndex(func(i int, _ string) bool { return i%2 == 1 }) + result := toSlice(oddIndexFilter(seq)) + assert.Equal(t, []string{"b", "d"}, result) +} + +func TestMonadFilterWithKey(t *testing.T) { + seq := Of2("x", 10) + filtered := MonadFilterWithKey(seq, func(k string, v int) bool { return v > 5 }) + result := toMap(filtered) + assert.Equal(t, map[string]int{"x": 10}, result) + + seq2 := Of2("y", 3) + filtered2 := MonadFilterWithKey(seq2, func(k string, v int) bool { return v > 5 }) + result2 := toMap(filtered2) + assert.Equal(t, map[string]int{}, result2) +} + +func TestFilterWithKey(t *testing.T) { + seq := Of2("x", 10) + filter := FilterWithKey(func(k string, v int) bool { return v > 5 }) + result := toMap(filter(seq)) + assert.Equal(t, map[string]int{"x": 10}, result) +} + +func TestMonadFilterMap(t *testing.T) { + seq := From(1, 2, 3, 4) + result := MonadFilterMap(seq, func(x int) Option[int] { + if x%2 == 0 { + return O.Some(x * 10) + } + return O.None[int]() + }) + assert.Equal(t, []int{20, 40}, toSlice(result)) +} + +func TestFilterMap(t *testing.T) { + seq := From(1, 2, 3, 4) + filterMapper := FilterMap(func(x int) Option[int] { + if x%2 == 0 { + return O.Some(x * 10) + } + return O.None[int]() + }) + result := toSlice(filterMapper(seq)) + assert.Equal(t, []int{20, 40}, result) +} + +func TestMonadFilterMapWithIndex(t *testing.T) { + seq := From("a", "b", "c") + result := MonadFilterMapWithIndex(seq, func(i int, s string) Option[string] { + if i%2 == 0 { + return O.Some(strings.ToUpper(s)) + } + return O.None[string]() + }) + assert.Equal(t, []string{"A", "C"}, toSlice(result)) +} + +func TestFilterMapWithIndex(t *testing.T) { + seq := From("a", "b", "c") + filterMapper := FilterMapWithIndex(func(i int, s string) Option[string] { + if i%2 == 0 { + return O.Some(strings.ToUpper(s)) + } + return O.None[string]() + }) + result := toSlice(filterMapper(seq)) + assert.Equal(t, []string{"A", "C"}, result) +} + +func TestMonadFilterMapWithKey(t *testing.T) { + seq := Of2("x", 10) + result := MonadFilterMapWithKey(seq, func(k string, v int) Option[int] { + if v > 5 { + return O.Some(v * 2) + } + return O.None[int]() + }) + assert.Equal(t, map[string]int{"x": 20}, toMap(result)) +} + +func TestFilterMapWithKey(t *testing.T) { + seq := Of2("x", 10) + filterMapper := FilterMapWithKey(func(k string, v int) Option[int] { + if v > 5 { + return O.Some(v * 2) + } + return O.None[int]() + }) + result := toMap(filterMapper(seq)) + assert.Equal(t, map[string]int{"x": 20}, result) +} + +func TestMonadChain(t *testing.T) { + seq := From(1, 2) + result := MonadChain(seq, func(x int) Seq[int] { + return From(x, x*10) + }) + assert.Equal(t, []int{1, 10, 2, 20}, toSlice(result)) +} + +func TestChain(t *testing.T) { + seq := From(1, 2) + chainer := Chain(func(x int) Seq[int] { + return From(x, x*10) + }) + result := toSlice(chainer(seq)) + assert.Equal(t, []int{1, 10, 2, 20}, result) +} + +func TestFlatten(t *testing.T) { + seq := From(From(1, 2), From(3, 4)) + result := Flatten(seq) + assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result)) +} + +func TestMonadAp(t *testing.T) { + fns := From( + func(x int) int { return x * 2 }, + func(x int) int { return x + 10 }, + ) + vals := From(1, 2) + result := MonadAp(fns, vals) + assert.Equal(t, []int{2, 4, 11, 12}, toSlice(result)) +} + +func TestAp(t *testing.T) { + fns := From( + func(x int) int { return x * 2 }, + func(x int) int { return x + 10 }, + ) + vals := From(1, 2) + applier := Ap[int](vals) + result := toSlice(applier(fns)) + assert.Equal(t, []int{2, 4, 11, 12}, result) +} + +func TestApCurried(t *testing.T) { + f := F.Curry3(func(s1 string, n int, s2 string) string { + return fmt.Sprintf("%s-%d-%s", s1, n, s2) + }) + + result := F.Pipe4( + Of(f), + Ap[func(int) func(string) string](From("a", "b")), + Ap[func(string) string](From(1, 2)), + Ap[string](From("c", "d")), + toSlice[string], + ) + + expected := []string{"a-1-c", "a-1-d", "a-2-c", "a-2-d", "b-1-c", "b-1-d", "b-2-c", "b-2-d"} + assert.Equal(t, expected, result) +} + +func TestMakeBy(t *testing.T) { + seq := MakeBy(5, func(i int) int { return i * i }) + result := toSlice(seq) + assert.Equal(t, []int{0, 1, 4, 9, 16}, result) +} + +func TestMakeByZero(t *testing.T) { + seq := MakeBy(0, func(i int) int { return i }) + result := toSlice(seq) + assert.Empty(t, result) +} + +func TestMakeByNegative(t *testing.T) { + seq := MakeBy(-5, func(i int) int { return i }) + result := toSlice(seq) + assert.Empty(t, result) +} + +func TestReplicate(t *testing.T) { + seq := Replicate(3, "hello") + result := toSlice(seq) + assert.Equal(t, []string{"hello", "hello", "hello"}, result) +} + +func TestMonadReduce(t *testing.T) { + seq := From(1, 2, 3, 4) + sum := MonadReduce(seq, func(acc, x int) int { return acc + x }, 0) + assert.Equal(t, 10, sum) +} + +func TestReduce(t *testing.T) { + seq := From(1, 2, 3, 4) + sum := Reduce(func(acc, x int) int { return acc + x }, 0) + result := sum(seq) + assert.Equal(t, 10, result) +} + +func TestMonadReduceWithIndex(t *testing.T) { + seq := From(10, 20, 30) + result := MonadReduceWithIndex(seq, func(i, acc, x int) int { + return acc + (i * x) + }, 0) + // 0*10 + 1*20 + 2*30 = 0 + 20 + 60 = 80 + assert.Equal(t, 80, result) +} + +func TestReduceWithIndex(t *testing.T) { + seq := From(10, 20, 30) + reducer := ReduceWithIndex(func(i, acc, x int) int { + return acc + (i * x) + }, 0) + result := reducer(seq) + assert.Equal(t, 80, result) +} + +func TestMonadReduceWithKey(t *testing.T) { + seq := Of2("x", 10) + result := MonadReduceWithKey(seq, func(k string, acc, v int) int { + return acc + v + }, 0) + assert.Equal(t, 10, result) +} + +func TestReduceWithKey(t *testing.T) { + seq := Of2("x", 10) + reducer := ReduceWithKey(func(k string, acc, v int) int { + return acc + v + }, 0) + result := reducer(seq) + assert.Equal(t, 10, result) +} + +func TestMonadFold(t *testing.T) { + seq := From("Hello", " ", "World") + result := MonadFold(seq, S.Monoid) + assert.Equal(t, "Hello World", result) +} + +func TestFold(t *testing.T) { + seq := From("Hello", " ", "World") + folder := Fold(S.Monoid) + result := folder(seq) + assert.Equal(t, "Hello World", result) +} + +func TestMonadFoldMap(t *testing.T) { + seq := From(1, 2, 3) + result := MonadFoldMap(seq, func(x int) string { + return fmt.Sprintf("%d", x) + }, S.Monoid) + assert.Equal(t, "123", result) +} + +func TestFoldMap(t *testing.T) { + seq := From(1, 2, 3) + folder := FoldMap[int](S.Monoid)(func(x int) string { + return fmt.Sprintf("%d", x) + }) + result := folder(seq) + assert.Equal(t, "123", result) +} + +func TestMonadFoldMapWithIndex(t *testing.T) { + seq := From("a", "b", "c") + result := MonadFoldMapWithIndex(seq, func(i int, s string) string { + return fmt.Sprintf("%d:%s ", i, s) + }, S.Monoid) + assert.Equal(t, "0:a 1:b 2:c ", result) +} + +func TestFoldMapWithIndex(t *testing.T) { + seq := From("a", "b", "c") + folder := FoldMapWithIndex[string](S.Monoid)(func(i int, s string) string { + return fmt.Sprintf("%d:%s ", i, s) + }) + result := folder(seq) + assert.Equal(t, "0:a 1:b 2:c ", result) +} + +func TestMonadFoldMapWithKey(t *testing.T) { + seq := Of2("x", 10) + result := MonadFoldMapWithKey(seq, func(k string, v int) string { + return fmt.Sprintf("%s:%d ", k, v) + }, S.Monoid) + assert.Equal(t, "x:10 ", result) +} + +func TestFoldMapWithKey(t *testing.T) { + seq := Of2("x", 10) + folder := FoldMapWithKey[string, int](S.Monoid)(func(k string, v int) string { + return fmt.Sprintf("%s:%d ", k, v) + }) + result := folder(seq) + assert.Equal(t, "x:10 ", result) +} + +func TestMonadFlap(t *testing.T) { + fns := From( + func(x int) int { return x * 2 }, + func(x int) int { return x + 10 }, + ) + result := MonadFlap(fns, 5) + assert.Equal(t, []int{10, 15}, toSlice(result)) +} + +func TestFlap(t *testing.T) { + fns := From( + func(x int) int { return x * 2 }, + func(x int) int { return x + 10 }, + ) + flapper := Flap[int](5) + result := toSlice(flapper(fns)) + assert.Equal(t, []int{10, 15}, result) +} + +func TestPrepend(t *testing.T) { + seq := From(2, 3, 4) + result := Prepend(1)(seq) + assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result)) +} + +func TestAppend(t *testing.T) { + seq := From(1, 2, 3) + result := Append(4)(seq) + assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result)) +} + +func TestMonadZip(t *testing.T) { + seqA := From(1, 2, 3) + seqB := From("a", "b") + result := MonadZip(seqB, seqA) + + var pairs []string + for a, b := range result { + pairs = append(pairs, fmt.Sprintf("%d:%s", a, b)) + } + assert.Equal(t, []string{"1:a", "2:b"}, pairs) +} + +func TestZip(t *testing.T) { + seqA := From(1, 2, 3) + seqB := From("a", "b", "c") + zipWithA := Zip[int, string](seqA) + result := zipWithA(seqB) + + var pairs []string + for a, b := range result { + pairs = append(pairs, fmt.Sprintf("%d:%s", a, b)) + } + assert.Equal(t, []string{"1:a", "2:b", "3:c"}, pairs) +} + +func TestMonoid(t *testing.T) { + m := Monoid[int]() + seq1 := From(1, 2) + seq2 := From(3, 4) + result := m.Concat(seq1, seq2) + assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result)) +} + +func TestMonoidEmpty(t *testing.T) { + m := Monoid[int]() + empty := m.Empty() + assert.Empty(t, toSlice(empty)) +} + +func TestMonoidAssociativity(t *testing.T) { + m := Monoid[int]() + seq1 := From(1, 2) + seq2 := From(3, 4) + seq3 := From(5, 6) + + // (seq1 + seq2) + seq3 + left := m.Concat(m.Concat(seq1, seq2), seq3) + // seq1 + (seq2 + seq3) + right := m.Concat(seq1, m.Concat(seq2, seq3)) + + assert.Equal(t, toSlice(left), toSlice(right)) +} + +func TestMonoidIdentity(t *testing.T) { + m := Monoid[int]() + seq := From(1, 2, 3) + empty := m.Empty() + + // seq + empty = seq + leftIdentity := m.Concat(seq, empty) + assert.Equal(t, []int{1, 2, 3}, toSlice(leftIdentity)) + + // empty + seq = seq + rightIdentity := m.Concat(empty, seq) + assert.Equal(t, []int{1, 2, 3}, toSlice(rightIdentity)) +} + +func TestPipelineComposition(t *testing.T) { + // Test a complex pipeline + result := F.Pipe4( + From(1, 2, 3, 4, 5, 6), + Filter(func(x int) bool { return x%2 == 0 }), + Map(func(x int) int { return x * 10 }), + Prepend(0), + toSlice[int], + ) + assert.Equal(t, []int{0, 20, 40, 60}, result) +} + +func TestLazyEvaluation(t *testing.T) { + // Test that operations are lazy + callCount := 0 + seq := From(1, 2, 3, 4, 5) + mapped := MonadMap(seq, func(x int) int { + callCount++ + return x * 2 + }) + + // No calls yet since we haven't iterated + assert.Equal(t, 0, callCount) + + // Iterate only first 2 elements + count := 0 + for range mapped { + count++ + if count == 2 { + break + } + } + + // Should have called the function only twice + assert.Equal(t, 2, callCount) +} + +func ExampleFoldMap() { + seq := From("a", "b", "c") + fold := FoldMap[string](S.Monoid)(strings.ToUpper) + result := fold(seq) + fmt.Println(result) + // Output: ABC +} + +func ExampleChain() { + seq := From(1, 2) + result := F.Pipe2( + seq, + Chain(func(x int) Seq[int] { + return From(x, x*10) + }), + toSlice[int], + ) + fmt.Println(result) + // Output: [1 10 2 20] +} + +func ExampleMonoid() { + m := Monoid[int]() + seq1 := From(1, 2, 3) + seq2 := From(4, 5, 6) + combined := m.Concat(seq1, seq2) + result := toSlice(combined) + fmt.Println(result) + // Output: [1 2 3 4 5 6] +} diff --git a/v2/iterator/iter/monid.go b/v2/iterator/iter/monid.go new file mode 100644 index 0000000..7a0a88e --- /dev/null +++ b/v2/iterator/iter/monid.go @@ -0,0 +1,52 @@ +// 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 iter + +import ( + M "github.com/IBM/fp-go/v2/monoid" +) + +// concat concatenates two sequences, yielding all elements from left followed by all elements from right. +func concat[T any](left, right Seq[T]) Seq[T] { + return func(yield Predicate[T]) { + for t := range left { + if !yield(t) { + return + } + } + for t := range right { + if !yield(t) { + return + } + } + } +} + +// Monoid returns a Monoid instance for Seq[T]. +// The monoid's concat operation concatenates sequences, and the empty value is an empty sequence. +// +// Example: +// +// m := Monoid[int]() +// seq1 := From(1, 2) +// seq2 := From(3, 4) +// result := m.Concat(seq1, seq2) +// // yields: 1, 2, 3, 4 +// +//go:inline +func Monoid[T any]() M.Monoid[Seq[T]] { + return M.MakeMonoid(concat[T], Empty[T]()) +} diff --git a/v2/iterator/iter/types.go b/v2/iterator/iter/types.go new file mode 100644 index 0000000..816d907 --- /dev/null +++ b/v2/iterator/iter/types.go @@ -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 iter + +import ( + I "iter" + + "github.com/IBM/fp-go/v2/iterator/stateless" + "github.com/IBM/fp-go/v2/optics/lens/option" + "github.com/IBM/fp-go/v2/predicate" +) + +type ( + // Option represents an optional value, either Some(value) or None. + Option[A any] = option.Option[A] + + // Seq is a single-value iterator sequence from Go 1.23+. + // It represents a lazy sequence of values that can be iterated using range. + Seq[T any] = I.Seq[T] + + // Seq2 is a key-value iterator sequence from Go 1.23+. + // It represents a lazy sequence of key-value pairs that can be iterated using range. + Seq2[K, V any] = I.Seq2[K, V] + + // Iterator is a stateless iterator type. + Iterator[T any] = stateless.Iterator[T] + + // Predicate is a function that tests a value and returns a boolean. + Predicate[T any] = predicate.Predicate[T] + + // Kleisli represents a function that takes a value and returns a sequence. + // This is the monadic bind operation for sequences. + Kleisli[A, B any] = func(A) Seq[B] + + // Kleisli2 represents a function that takes a value and returns a key-value sequence. + Kleisli2[K, A, B any] = func(A) Seq2[K, B] + + // Operator represents a transformation from one sequence to another. + // It's a function that takes a Seq[A] and returns a Seq[B]. + Operator[A, B any] = Kleisli[Seq[A], B] + + // Operator2 represents a transformation from one key-value sequence to another. + Operator2[K, A, B any] = Kleisli2[K, Seq2[K, A], B] +) diff --git a/v2/iterator/stateless/any.go b/v2/iterator/stateless/any.go index ff3da5b..2f6a8b9 100644 --- a/v2/iterator/stateless/any.go +++ b/v2/iterator/stateless/any.go @@ -21,6 +21,6 @@ import ( // Any returns `true` if any element of the iterable is `true`. If the iterable is empty, return `false` // Similar to the [https://docs.python.org/3/library/functions.html#any] function -func Any[U any](pred func(U) bool) func(ma Iterator[U]) bool { +func Any[U any](pred Predicate[U]) Predicate[Iterator[U]] { return G.Any[Iterator[U]](pred) } diff --git a/v2/iterator/stateless/bind.go b/v2/iterator/stateless/bind.go index b7b0e69..1c589d1 100644 --- a/v2/iterator/stateless/bind.go +++ b/v2/iterator/stateless/bind.go @@ -72,7 +72,7 @@ func Do[S any]( func Bind[S1, S2, T any]( setter func(T) func(S1) S2, f Kleisli[S1, T], -) Kleisli[Iterator[S1], S2] { +) Operator[S1, S2] { return G.Bind[Iterator[S1], Iterator[S2]](setter, f) } @@ -80,7 +80,7 @@ func Bind[S1, S2, T any]( func Let[S1, S2, T any]( setter func(T) func(S1) S2, f func(S1) T, -) Kleisli[Iterator[S1], S2] { +) Operator[S1, S2] { return G.Let[Iterator[S1], Iterator[S2]](setter, f) } @@ -88,14 +88,14 @@ func Let[S1, S2, T any]( func LetTo[S1, S2, T any]( setter func(T) func(S1) S2, b T, -) Kleisli[Iterator[S1], S2] { +) Operator[S1, S2] { return G.LetTo[Iterator[S1], Iterator[S2]](setter, b) } // BindTo initializes a new state [S1] from a value [T] func BindTo[S1, T any]( setter func(T) S1, -) Kleisli[Iterator[T], S1] { +) Operator[T, S1] { return G.BindTo[Iterator[S1], Iterator[T]](setter) } @@ -135,6 +135,6 @@ func BindTo[S1, T any]( func ApS[S1, S2, T any]( setter func(T) func(S1) S2, fa Iterator[T], -) Kleisli[Iterator[S1], S2] { +) Operator[S1, S2] { return G.ApS[Iterator[func(T) S2], Iterator[S1], Iterator[S2]](setter, fa) } diff --git a/v2/iterator/stateless/compress.go b/v2/iterator/stateless/compress.go index d499b89..74ab951 100644 --- a/v2/iterator/stateless/compress.go +++ b/v2/iterator/stateless/compress.go @@ -17,11 +17,10 @@ package stateless import ( G "github.com/IBM/fp-go/v2/iterator/stateless/generic" - P "github.com/IBM/fp-go/v2/pair" ) // Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`. // Stops when either the data or selectors iterator has been exhausted. -func Compress[U any](sel Iterator[bool]) Kleisli[Iterator[U], U] { - return G.Compress[Iterator[U], Iterator[bool], Iterator[P.Pair[U, bool]]](sel) +func Compress[U any](sel Iterator[bool]) Operator[U, U] { + return G.Compress[Iterator[U], Iterator[bool], Iterator[Pair[U, bool]]](sel) } diff --git a/v2/iterator/stateless/dropwhile.go b/v2/iterator/stateless/dropwhile.go index eddf60b..33b53fb 100644 --- a/v2/iterator/stateless/dropwhile.go +++ b/v2/iterator/stateless/dropwhile.go @@ -21,6 +21,6 @@ import ( // DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. // Note, the [Iterator] does not produce any output until the predicate first becomes false -func DropWhile[U any](pred func(U) bool) Kleisli[Iterator[U], U] { +func DropWhile[U any](pred Predicate[U]) Operator[U, U] { return G.DropWhile[Iterator[U]](pred) } diff --git a/v2/iterator/stateless/first.go b/v2/iterator/stateless/first.go index 5b4b389..a963774 100644 --- a/v2/iterator/stateless/first.go +++ b/v2/iterator/stateless/first.go @@ -17,10 +17,9 @@ package stateless import ( G "github.com/IBM/fp-go/v2/iterator/stateless/generic" - O "github.com/IBM/fp-go/v2/option" ) // First returns the first item in an iterator if such an item exists -func First[U any](mu Iterator[U]) O.Option[U] { +func First[U any](mu Iterator[U]) Option[U] { return G.First(mu) } diff --git a/v2/iterator/stateless/generic/any.go b/v2/iterator/stateless/generic/any.go index 7f68db3..efec30d 100644 --- a/v2/iterator/stateless/generic/any.go +++ b/v2/iterator/stateless/generic/any.go @@ -18,11 +18,10 @@ package generic import ( F "github.com/IBM/fp-go/v2/function" O "github.com/IBM/fp-go/v2/option" - P "github.com/IBM/fp-go/v2/pair" ) // Any returns `true` if any element of the iterable is `true`. If the iterable is empty, return `false` -func Any[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) bool, U any](pred FCT) func(ma GU) bool { +func Any[GU ~func() Option[Pair[GU, U]], FCT ~Predicate[U], U any](pred FCT) func(ma GU) bool { return F.Flow3( Filter[GU](pred), First[GU], diff --git a/v2/iterator/stateless/generic/bind.go b/v2/iterator/stateless/generic/bind.go index f6fce2a..45c253b 100644 --- a/v2/iterator/stateless/generic/bind.go +++ b/v2/iterator/stateless/generic/bind.go @@ -19,8 +19,6 @@ import ( "github.com/IBM/fp-go/v2/internal/apply" C "github.com/IBM/fp-go/v2/internal/chain" F "github.com/IBM/fp-go/v2/internal/functor" - O "github.com/IBM/fp-go/v2/option" - P "github.com/IBM/fp-go/v2/pair" ) // Do creates an empty context of type [S] to be used with the [Bind] operation. @@ -33,7 +31,7 @@ import ( // Y int // } // result := generic.Do[Iterator[State]](State{}) -func Do[GS ~func() O.Option[P.Pair[GS, S]], S any]( +func Do[GS ~func() Option[Pair[GS, S]], S any]( empty S, ) GS { return Of[GS](empty) @@ -73,7 +71,7 @@ func Do[GS ~func() O.Option[P.Pair[GS, S]], S any]( // }, // ), // ) // Produces: {1,10}, {1,20}, {2,20}, {2,40}, {3,30}, {3,60} -func Bind[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], GA ~func() O.Option[P.Pair[GA, A]], S1, S2, A any]( +func Bind[GS1 ~func() Option[Pair[GS1, S1]], GS2 ~func() Option[Pair[GS2, S2]], GA ~func() Option[Pair[GA, A]], S1, S2, A any]( setter func(A) func(S1) S2, f func(S1) GA, ) func(GS1) GS2 { @@ -87,7 +85,7 @@ func Bind[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2 } // Let attaches the result of a computation to a context [S1] to produce a context [S2] -func Let[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], S1, S2, A any]( +func Let[GS1 ~func() Option[Pair[GS1, S1]], GS2 ~func() Option[Pair[GS2, S2]], S1, S2, A any]( key func(A) func(S1) S2, f func(S1) A, ) func(GS1) GS2 { @@ -99,7 +97,7 @@ func Let[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, } // LetTo attaches the a value to a context [S1] to produce a context [S2] -func LetTo[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], S1, S2, B any]( +func LetTo[GS1 ~func() Option[Pair[GS1, S1]], GS2 ~func() Option[Pair[GS2, S2]], S1, S2, B any]( key func(B) func(S1) S2, b B, ) func(GS1) GS2 { @@ -111,7 +109,7 @@ func LetTo[GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS } // BindTo initializes a new state [S1] from a value [T] -func BindTo[GS1 ~func() O.Option[P.Pair[GS1, S1]], GA ~func() O.Option[P.Pair[GA, A]], S1, A any]( +func BindTo[GS1 ~func() Option[Pair[GS1, S1]], GA ~func() Option[Pair[GA, A]], S1, A any]( setter func(A) S1, ) func(GA) GS1 { return C.BindTo( @@ -153,7 +151,7 @@ func BindTo[GS1 ~func() O.Option[P.Pair[GS1, S1]], GA ~func() O.Option[P.Pair[GA // yIter, // ), // ) // Produces: {1,"a"}, {1,"b"}, {2,"a"}, {2,"b"}, {3,"a"}, {3,"b"} -func ApS[GAS2 ~func() O.Option[P.Pair[GAS2, func(A) S2]], GS1 ~func() O.Option[P.Pair[GS1, S1]], GS2 ~func() O.Option[P.Pair[GS2, S2]], GA ~func() O.Option[P.Pair[GA, A]], S1, S2, A any]( +func ApS[GAS2 ~func() Option[Pair[GAS2, func(A) S2]], GS1 ~func() Option[Pair[GS1, S1]], GS2 ~func() Option[Pair[GS2, S2]], GA ~func() Option[Pair[GA, A]], S1, S2, A any]( setter func(A) func(S1) S2, fa GA, ) func(GS1) GS2 { diff --git a/v2/iterator/stateless/generic/compress.go b/v2/iterator/stateless/generic/compress.go index 43cc050..6d74316 100644 --- a/v2/iterator/stateless/generic/compress.go +++ b/v2/iterator/stateless/generic/compress.go @@ -23,7 +23,7 @@ import ( // Compress returns an [Iterator] that filters elements from a data [Iterator] returning only those that have a corresponding element in selector [Iterator] that evaluates to `true`. // Stops when either the data or selectors iterator has been exhausted. -func Compress[GU ~func() O.Option[P.Pair[GU, U]], GB ~func() O.Option[P.Pair[GB, bool]], CS ~func() O.Option[P.Pair[CS, P.Pair[U, bool]]], U any](sel GB) func(GU) GU { +func Compress[GU ~func() Option[Pair[GU, U]], GB ~func() Option[Pair[GB, bool]], CS ~func() Option[Pair[CS, Pair[U, bool]]], U any](sel GB) func(GU) GU { return F.Flow2( Zip[GU, GB, CS](sel), FilterMap[GU, CS](F.Flow2( diff --git a/v2/iterator/stateless/generic/cycle.go b/v2/iterator/stateless/generic/cycle.go index 6d5be09..0ee2ccd 100644 --- a/v2/iterator/stateless/generic/cycle.go +++ b/v2/iterator/stateless/generic/cycle.go @@ -21,9 +21,9 @@ import ( P "github.com/IBM/fp-go/v2/pair" ) -func Cycle[GU ~func() O.Option[P.Pair[GU, U]], U any](ma GU) GU { +func Cycle[GU ~func() Option[Pair[GU, U]], U any](ma GU) GU { // avoid cyclic references - var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] + var m func(Option[Pair[GU, U]]) Option[Pair[GU, U]] recurse := func(mu GU) GU { return F.Nullary2( @@ -32,11 +32,11 @@ func Cycle[GU ~func() O.Option[P.Pair[GU, U]], U any](ma GU) GU { ) } - m = O.Fold(func() O.Option[P.Pair[GU, U]] { + m = O.Fold(func() Option[Pair[GU, U]] { return recurse(ma)() }, F.Flow2( P.BiMap(recurse, F.Identity[U]), - O.Of[P.Pair[GU, U]], + O.Of[Pair[GU, U]], )) return recurse(ma) diff --git a/v2/iterator/stateless/generic/dropwhile.go b/v2/iterator/stateless/generic/dropwhile.go index 6f7a1e3..7265cb1 100644 --- a/v2/iterator/stateless/generic/dropwhile.go +++ b/v2/iterator/stateless/generic/dropwhile.go @@ -24,9 +24,9 @@ import ( // DropWhile creates an [Iterator] that drops elements from the [Iterator] as long as the predicate is true; afterwards, returns every element. // Note, the [Iterator] does not produce any output until the predicate first becomes false -func DropWhile[GU ~func() O.Option[P.Pair[GU, U]], U any](pred func(U) bool) func(GU) GU { +func DropWhile[GU ~func() Option[Pair[GU, U]], U any](pred Predicate[U]) func(GU) GU { // avoid cyclic references - var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] + var m func(Option[Pair[GU, U]]) Option[Pair[GU, U]] fromPred := O.FromPredicate(PR.Not(PR.ContraMap(P.Tail[GU, U])(pred))) @@ -37,11 +37,11 @@ func DropWhile[GU ~func() O.Option[P.Pair[GU, U]], U any](pred func(U) bool) fun ) } - m = O.Chain(func(t P.Pair[GU, U]) O.Option[P.Pair[GU, U]] { + m = O.Chain(func(t Pair[GU, U]) Option[Pair[GU, U]] { return F.Pipe2( t, fromPred, - O.Fold(recurse(Next(t)), O.Of[P.Pair[GU, U]]), + O.Fold(recurse(Next(t)), O.Of[Pair[GU, U]]), ) }) diff --git a/v2/iterator/stateless/generic/first.go b/v2/iterator/stateless/generic/first.go index 304f64f..49c0e46 100644 --- a/v2/iterator/stateless/generic/first.go +++ b/v2/iterator/stateless/generic/first.go @@ -22,7 +22,7 @@ import ( ) // First returns the first item in an iterator if such an item exists -func First[GU ~func() O.Option[P.Pair[GU, U]], U any](mu GU) O.Option[U] { +func First[GU ~func() Option[Pair[GU, U]], U any](mu GU) Option[U] { return F.Pipe1( mu(), O.Map(P.Tail[GU, U]), diff --git a/v2/iterator/stateless/generic/io.go b/v2/iterator/stateless/generic/io.go index 7037621..800a32a 100644 --- a/v2/iterator/stateless/generic/io.go +++ b/v2/iterator/stateless/generic/io.go @@ -23,12 +23,12 @@ import ( ) // FromLazy returns an iterator on top of a lazy function -func FromLazy[GU ~func() O.Option[P.Pair[GU, U]], LZ ~func() U, U any](l LZ) GU { +func FromLazy[GU ~func() Option[Pair[GU, U]], LZ ~func() U, U any](l LZ) GU { return F.Pipe1( l, L.Map[LZ, GU](F.Flow2( F.Bind1st(P.MakePair[GU, U], Empty[GU]()), - O.Of[P.Pair[GU, U]], + O.Of[Pair[GU, U]], )), ) } diff --git a/v2/iterator/stateless/generic/iterator.go b/v2/iterator/stateless/generic/iterator.go index 37029bf..7de65e4 100644 --- a/v2/iterator/stateless/generic/iterator.go +++ b/v2/iterator/stateless/generic/iterator.go @@ -27,42 +27,42 @@ import ( P "github.com/IBM/fp-go/v2/pair" ) -// Next returns the iterator for the next element in an iterator `P.Pair` -func Next[GU ~func() O.Option[P.Pair[GU, U]], U any](m P.Pair[GU, U]) GU { +// Next returns the iterator for the next element in an iterator `Pair` +func Next[GU ~func() Option[Pair[GU, U]], U any](m Pair[GU, U]) GU { return P.Head(m) } -// Current returns the current element in an iterator `P.Pair` -func Current[GU ~func() O.Option[P.Pair[GU, U]], U any](m P.Pair[GU, U]) U { +// Current returns the current element in an iterator `Pair` +func Current[GU ~func() Option[Pair[GU, U]], U any](m Pair[GU, U]) U { return P.Tail(m) } // From constructs an array from a set of variadic arguments -func From[GU ~func() O.Option[P.Pair[GU, U]], U any](data ...U) GU { +func From[GU ~func() Option[Pair[GU, U]], U any](data ...U) GU { return FromArray[GU](data) } // Empty returns the empty iterator -func Empty[GU ~func() O.Option[P.Pair[GU, U]], U any]() GU { +func Empty[GU ~func() Option[Pair[GU, U]], U any]() GU { return IO.None[GU]() } // Of returns an iterator with one single element -func Of[GU ~func() O.Option[P.Pair[GU, U]], U any](a U) GU { +func Of[GU ~func() Option[Pair[GU, U]], U any](a U) GU { return IO.Of[GU](P.MakePair(Empty[GU](), a)) } // FromArray returns an iterator from multiple elements -func FromArray[GU ~func() O.Option[P.Pair[GU, U]], US ~[]U, U any](as US) GU { +func FromArray[GU ~func() Option[Pair[GU, U]], US ~[]U, U any](as US) GU { return A.MatchLeft(Empty[GU], func(head U, tail US) GU { - return func() O.Option[P.Pair[GU, U]] { + return func() Option[Pair[GU, U]] { return O.Of(P.MakePair(FromArray[GU](tail), head)) } })(as) } // reduce applies a function for each value of the iterator with a floating result -func reduce[GU ~func() O.Option[P.Pair[GU, U]], U, V any](as GU, f func(V, U) V, initial V) V { +func reduce[GU ~func() Option[Pair[GU, U]], U, V any](as GU, f func(V, U) V, initial V) V { next, ok := O.Unwrap(as()) current := initial for ok { @@ -74,18 +74,18 @@ func reduce[GU ~func() O.Option[P.Pair[GU, U]], U, V any](as GU, f func(V, U) V, } // Reduce applies a function for each value of the iterator with a floating result -func Reduce[GU ~func() O.Option[P.Pair[GU, U]], U, V any](f func(V, U) V, initial V) func(GU) V { +func Reduce[GU ~func() Option[Pair[GU, U]], U, V any](f func(V, U) V, initial V) func(GU) V { return F.Bind23of3(reduce[GU, U, V])(f, initial) } // ToArray converts the iterator to an array -func ToArray[GU ~func() O.Option[P.Pair[GU, U]], US ~[]U, U any](u GU) US { +func ToArray[GU ~func() Option[Pair[GU, U]], US ~[]U, U any](u GU) US { return Reduce[GU](A.Append[US], A.Empty[US]())(u) } -func Map[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) V, U, V any](f FCT) func(ma GU) GV { +func Map[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], FCT ~func(U) V, U, V any](f FCT) func(ma GU) GV { // pre-declare to avoid cyclic reference - var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] + var m func(Option[Pair[GU, U]]) Option[Pair[GV, V]] recurse := func(ma GU) GV { return F.Nullary2( @@ -99,12 +99,12 @@ func Map[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], return recurse } -func MonadMap[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU, f func(U) V) GV { +func MonadMap[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], U, V any](ma GU, f func(U) V) GV { return Map[GV, GU](f)(ma) } -func concat[GU ~func() O.Option[P.Pair[GU, U]], U any](right, left GU) GU { - var m func(ma O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GU, U]] +func concat[GU ~func() Option[Pair[GU, U]], U any](right, left GU) GU { + var m func(ma Option[Pair[GU, U]]) Option[Pair[GU, U]] recurse := func(left GU) GU { return F.Nullary2(left, m) @@ -114,15 +114,15 @@ func concat[GU ~func() O.Option[P.Pair[GU, U]], U any](right, left GU) GU { right, F.Flow2( P.BiMap(recurse, F.Identity[U]), - O.Some[P.Pair[GU, U]], + O.Some[Pair[GU, U]], )) return recurse(left) } -func Chain[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](f func(U) GV) func(GU) GV { +func Chain[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], U, V any](f func(U) GV) func(GU) GV { // pre-declare to avoid cyclic reference - var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] + var m func(Option[Pair[GU, U]]) Option[Pair[GV, V]] recurse := func(ma GU) GV { return F.Nullary2( @@ -134,7 +134,7 @@ func Chain[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U] F.Flow3( P.BiMap(recurse, f), P.Paired(concat[GV]), - func(v GV) O.Option[P.Pair[GV, V]] { + func(v GV) Option[Pair[GV, V]] { return v() }, ), @@ -143,11 +143,11 @@ func Chain[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U] return recurse } -func MonadChain[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU, f func(U) GV) GV { +func MonadChain[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], U, V any](ma GU, f func(U) GV) GV { return Chain[GV, GU](f)(ma) } -func MonadChainFirst[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU, f func(U) GV) GU { +func MonadChainFirst[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], U, V any](ma GU, f func(U) GV) GU { return C.MonadChainFirst( MonadChain[GU, GU, U, U], MonadMap[GU, GV, V, U], @@ -156,7 +156,7 @@ func MonadChainFirst[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.P ) } -func ChainFirst[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](f func(U) GV) func(GU) GU { +func ChainFirst[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], U, V any](f func(U) GV) func(GU) GU { return C.ChainFirst( Chain[GU, GU, U, U], Map[GU, GV, func(V) U, V, U], @@ -164,14 +164,14 @@ func ChainFirst[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[G ) } -func Flatten[GV ~func() O.Option[P.Pair[GV, GU]], GU ~func() O.Option[P.Pair[GU, U]], U any](ma GV) GU { +func Flatten[GV ~func() Option[Pair[GV, GU]], GU ~func() Option[Pair[GU, U]], U any](ma GV) GU { return MonadChain(ma, F.Identity[GU]) } // MakeBy returns an [Iterator] with an infinite number of elements initialized with `f(i)` -func MakeBy[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(int) U, U any](f FCT) GU { +func MakeBy[GU ~func() Option[Pair[GU, U]], FCT ~func(int) U, U any](f FCT) GU { - var m func(int) O.Option[P.Pair[GU, U]] + var m func(int) Option[Pair[GU, U]] recurse := func(i int) GU { return F.Nullary2( @@ -186,7 +186,7 @@ func MakeBy[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(int) U, U any](f FCT) utils.Inc, recurse), f), - O.Of[P.Pair[GU, U]], + O.Of[Pair[GU, U]], ) // bootstrap @@ -194,13 +194,13 @@ func MakeBy[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(int) U, U any](f FCT) } // Replicate creates an infinite [Iterator] containing a value. -func Replicate[GU ~func() O.Option[P.Pair[GU, U]], U any](a U) GU { +func Replicate[GU ~func() Option[Pair[GU, U]], U any](a U) GU { return MakeBy[GU](F.Constant1[int](a)) } // Repeat creates an [Iterator] containing a value repeated the specified number of times. // Alias of [Replicate] combined with [Take] -func Repeat[GU ~func() O.Option[P.Pair[GU, U]], U any](n int, a U) GU { +func Repeat[GU ~func() Option[Pair[GU, U]], U any](n int, a U) GU { return F.Pipe2( a, Replicate[GU], @@ -209,13 +209,13 @@ func Repeat[GU ~func() O.Option[P.Pair[GU, U]], U any](n int, a U) GU { } // Count creates an [Iterator] containing a consecutive sequence of integers starting with the provided start value -func Count[GU ~func() O.Option[P.Pair[GU, int]]](start int) GU { +func Count[GU ~func() Option[Pair[GU, int]]](start int) GU { return MakeBy[GU](N.Add(start)) } -func FilterMap[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) O.Option[V], U, V any](f FCT) func(ma GU) GV { +func FilterMap[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], FCT ~func(U) Option[V], U, V any](f FCT) func(ma GU) GV { // pre-declare to avoid cyclic reference - var m func(O.Option[P.Pair[GU, U]]) O.Option[P.Pair[GV, V]] + var m func(Option[Pair[GU, U]]) Option[Pair[GV, V]] recurse := func(ma GU) GV { return F.Nullary2( @@ -226,11 +226,11 @@ func FilterMap[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU m = O.Fold( Empty[GV](), - func(t P.Pair[GU, U]) O.Option[P.Pair[GV, V]] { + func(t Pair[GU, U]) Option[Pair[GV, V]] { r := recurse(Next(t)) return O.MonadFold(f(Current(t)), r, F.Flow2( F.Bind1st(P.MakePair[GV, V], r), - O.Some[P.Pair[GV, V]], + O.Some[Pair[GV, V]], )) }, ) @@ -238,26 +238,26 @@ func FilterMap[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU return recurse } -func Filter[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) bool, U any](f FCT) func(ma GU) GU { +func Filter[GU ~func() Option[Pair[GU, U]], FCT ~Predicate[U], U any](f FCT) func(ma GU) GU { return FilterMap[GU, GU](O.FromPredicate(f)) } -func Ap[GUV ~func() O.Option[P.Pair[GUV, func(U) V]], GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](ma GU) func(fab GUV) GV { +func Ap[GUV ~func() Option[Pair[GUV, func(U) V]], GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], U, V any](ma GU) func(fab GUV) GV { return Chain[GV, GUV](F.Bind1st(MonadMap[GV, GU], ma)) } -func MonadAp[GUV ~func() O.Option[P.Pair[GUV, func(U) V]], GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], U, V any](fab GUV, ma GU) GV { +func MonadAp[GUV ~func() Option[Pair[GUV, func(U) V]], GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], U, V any](fab GUV, ma GU) GV { return Ap[GUV, GV](ma)(fab) } -func FilterChain[GVV ~func() O.Option[P.Pair[GVV, GV]], GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) O.Option[GV], U, V any](f FCT) func(ma GU) GV { +func FilterChain[GVV ~func() Option[Pair[GVV, GV]], GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], FCT ~func(U) Option[GV], U, V any](f FCT) func(ma GU) GV { return F.Flow2( FilterMap[GVV, GU](f), Flatten[GVV], ) } -func FoldMap[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) V, U, V any](m M.Monoid[V]) func(FCT) func(ma GU) V { +func FoldMap[GU ~func() Option[Pair[GU, U]], FCT ~func(U) V, U, V any](m M.Monoid[V]) func(FCT) func(ma GU) V { return func(f FCT) func(ma GU) V { return Reduce[GU](func(cur V, a U) V { return m.Concat(cur, f(a)) @@ -265,6 +265,6 @@ func FoldMap[GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(U) V, U, V any](m M.M } } -func Fold[GU ~func() O.Option[P.Pair[GU, U]], U any](m M.Monoid[U]) func(ma GU) U { +func Fold[GU ~func() Option[Pair[GU, U]], U any](m M.Monoid[U]) func(ma GU) U { return Reduce[GU](m.Concat, m.Empty()) } diff --git a/v2/iterator/stateless/generic/last.go b/v2/iterator/stateless/generic/last.go index 81c6fd5..9653d8f 100644 --- a/v2/iterator/stateless/generic/last.go +++ b/v2/iterator/stateless/generic/last.go @@ -18,10 +18,9 @@ package generic import ( F "github.com/IBM/fp-go/v2/function" O "github.com/IBM/fp-go/v2/option" - P "github.com/IBM/fp-go/v2/pair" ) // Last returns the last item in an iterator if such an item exists -func Last[GU ~func() O.Option[P.Pair[GU, U]], U any](mu GU) O.Option[U] { - return reduce(mu, F.Ignore1of2[O.Option[U]](O.Of[U]), O.None[U]()) +func Last[GU ~func() Option[Pair[GU, U]], U any](mu GU) Option[U] { + return reduce(mu, F.Ignore1of2[Option[U]](O.Of[U]), O.None[U]()) } diff --git a/v2/iterator/stateless/generic/monad.go b/v2/iterator/stateless/generic/monad.go index 17e3e29..6c6e851 100644 --- a/v2/iterator/stateless/generic/monad.go +++ b/v2/iterator/stateless/generic/monad.go @@ -17,11 +17,9 @@ package generic import ( "github.com/IBM/fp-go/v2/internal/monad" - O "github.com/IBM/fp-go/v2/option" - P "github.com/IBM/fp-go/v2/pair" ) -type iteratorMonad[A, B any, GA ~func() O.Option[P.Pair[GA, A]], GB ~func() O.Option[P.Pair[GB, B]], GAB ~func() O.Option[P.Pair[GAB, func(A) B]]] struct{} +type iteratorMonad[A, B any, GA ~func() Option[Pair[GA, A]], GB ~func() Option[Pair[GB, B]], GAB ~func() Option[Pair[GAB, func(A) B]]] struct{} func (o *iteratorMonad[A, B, GA, GB, GAB]) Of(a A) GA { return Of[GA](a) @@ -40,6 +38,6 @@ func (o *iteratorMonad[A, B, GA, GB, GAB]) Ap(fa GA) func(GAB) GB { } // Monad implements the monadic operations for iterators -func Monad[A, B any, GA ~func() O.Option[P.Pair[GA, A]], GB ~func() O.Option[P.Pair[GB, B]], GAB ~func() O.Option[P.Pair[GAB, func(A) B]]]() monad.Monad[A, B, GA, GB, GAB] { +func Monad[A, B any, GA ~func() Option[Pair[GA, A]], GB ~func() Option[Pair[GB, B]], GAB ~func() Option[Pair[GAB, func(A) B]]]() monad.Monad[A, B, GA, GB, GAB] { return &iteratorMonad[A, B, GA, GB, GAB]{} } diff --git a/v2/iterator/stateless/generic/monoid.go b/v2/iterator/stateless/generic/monoid.go index f8217f1..55f87fa 100644 --- a/v2/iterator/stateless/generic/monoid.go +++ b/v2/iterator/stateless/generic/monoid.go @@ -18,11 +18,9 @@ package generic import ( F "github.com/IBM/fp-go/v2/function" M "github.com/IBM/fp-go/v2/monoid" - O "github.com/IBM/fp-go/v2/option" - P "github.com/IBM/fp-go/v2/pair" ) -func Monoid[GU ~func() O.Option[P.Pair[GU, U]], U any]() M.Monoid[GU] { +func Monoid[GU ~func() Option[Pair[GU, U]], U any]() M.Monoid[GU] { return M.MakeMonoid( F.Swap(concat[GU]), Empty[GU](), diff --git a/v2/iterator/stateless/generic/reflect.go b/v2/iterator/stateless/generic/reflect.go index 3db3a64..dd927d4 100644 --- a/v2/iterator/stateless/generic/reflect.go +++ b/v2/iterator/stateless/generic/reflect.go @@ -27,7 +27,7 @@ import ( P "github.com/IBM/fp-go/v2/pair" ) -func FromReflect[GR ~func() O.Option[P.Pair[GR, R.Value]]](val R.Value) GR { +func FromReflect[GR ~func() Option[Pair[GR, R.Value]]](val R.Value) GR { // recursive callback var recurse func(idx int) GR @@ -39,7 +39,7 @@ func FromReflect[GR ~func() O.Option[P.Pair[GR, R.Value]]](val R.Value) GR { idx, L.Of[int], L.Map(fromPred), - LG.Map[L.Lazy[O.Option[int]], GR](O.Map( + LG.Map[Lazy[Option[int]], GR](O.Map( F.Flow2( P.Of[int], P.BiMap(F.Flow2(N.Add(1), recurse), val.Index), diff --git a/v2/iterator/stateless/generic/scan.go b/v2/iterator/stateless/generic/scan.go index 712ccf2..4c1a3e2 100644 --- a/v2/iterator/stateless/generic/scan.go +++ b/v2/iterator/stateless/generic/scan.go @@ -21,11 +21,11 @@ import ( P "github.com/IBM/fp-go/v2/pair" ) -func apTuple[A, B any](t P.Pair[func(A) B, A]) P.Pair[B, A] { +func apTuple[A, B any](t Pair[func(A) B, A]) Pair[B, A] { return P.MakePair(P.Head(t)(P.Tail(t)), P.Tail(t)) } -func Scan[GV ~func() O.Option[P.Pair[GV, V]], GU ~func() O.Option[P.Pair[GU, U]], FCT ~func(V, U) V, U, V any](f FCT, initial V) func(ma GU) GV { +func Scan[GV ~func() Option[Pair[GV, V]], GU ~func() Option[Pair[GU, U]], FCT ~func(V, U) V, U, V any](f FCT, initial V) func(ma GU) GV { // pre-declare to avoid cyclic reference var m func(GU) func(V) GV diff --git a/v2/iterator/stateless/generic/take.go b/v2/iterator/stateless/generic/take.go index 2f23650..6dbe409 100644 --- a/v2/iterator/stateless/generic/take.go +++ b/v2/iterator/stateless/generic/take.go @@ -22,7 +22,7 @@ import ( P "github.com/IBM/fp-go/v2/pair" ) -func Take[GU ~func() O.Option[P.Pair[GU, U]], U any](n int) func(ma GU) GU { +func Take[GU ~func() Option[Pair[GU, U]], U any](n int) func(ma GU) GU { // pre-declare to avoid cyclic reference var recurse func(ma GU, idx int) GU diff --git a/v2/iterator/stateless/generic/types.go b/v2/iterator/stateless/generic/types.go new file mode 100644 index 0000000..e98f058 --- /dev/null +++ b/v2/iterator/stateless/generic/types.go @@ -0,0 +1,15 @@ +package generic + +import ( + "github.com/IBM/fp-go/v2/lazy" + "github.com/IBM/fp-go/v2/option" + "github.com/IBM/fp-go/v2/pair" + "github.com/IBM/fp-go/v2/predicate" +) + +type ( + Option[A any] = option.Option[A] + Lazy[A any] = lazy.Lazy[A] + Pair[L, R any] = pair.Pair[L, R] + Predicate[A any] = predicate.Predicate[A] +) diff --git a/v2/iterator/stateless/generic/uniq.go b/v2/iterator/stateless/generic/uniq.go index 8556f19..a6d42e0 100644 --- a/v2/iterator/stateless/generic/uniq.go +++ b/v2/iterator/stateless/generic/uniq.go @@ -31,14 +31,14 @@ func addToMap[A comparable](a A, m map[A]bool) map[A]bool { return cpy } -func Uniq[AS ~func() O.Option[P.Pair[AS, A]], K comparable, A any](f func(A) K) func(as AS) AS { +func Uniq[AS ~func() Option[Pair[AS, A]], K comparable, A any](f func(A) K) func(as AS) AS { var recurse func(as AS, mp map[K]bool) AS recurse = func(as AS, mp map[K]bool) AS { return F.Nullary2( as, - O.Chain(func(a P.Pair[AS, A]) O.Option[P.Pair[AS, A]] { + O.Chain(func(a Pair[AS, A]) Option[Pair[AS, A]] { return F.Pipe3( P.Tail(a), f, @@ -46,7 +46,7 @@ func Uniq[AS ~func() O.Option[P.Pair[AS, A]], K comparable, A any](f func(A) K) _, ok := mp[k] return !ok }), - O.Fold(recurse(P.Head(a), mp), func(k K) O.Option[P.Pair[AS, A]] { + O.Fold(recurse(P.Head(a), mp), func(k K) Option[Pair[AS, A]] { return O.Of(P.MakePair(recurse(P.Head(a), addToMap(k, mp)), P.Tail(a))) }), ) @@ -57,6 +57,6 @@ func Uniq[AS ~func() O.Option[P.Pair[AS, A]], K comparable, A any](f func(A) K) return F.Bind2nd(recurse, make(map[K]bool, 0)) } -func StrictUniq[AS ~func() O.Option[P.Pair[AS, A]], A comparable](as AS) AS { +func StrictUniq[AS ~func() Option[Pair[AS, A]], A comparable](as AS) AS { return Uniq[AS](F.Identity[A])(as) } diff --git a/v2/iterator/stateless/generic/zip.go b/v2/iterator/stateless/generic/zip.go index 13f90f3..7e013f4 100644 --- a/v2/iterator/stateless/generic/zip.go +++ b/v2/iterator/stateless/generic/zip.go @@ -23,12 +23,12 @@ import ( // ZipWith applies a function to pairs of elements at the same index in two iterators, collecting the results in a new iterator. If one // input iterator is short, excess elements of the longer iterator are discarded. -func ZipWith[AS ~func() O.Option[P.Pair[AS, A]], BS ~func() O.Option[P.Pair[BS, B]], CS ~func() O.Option[P.Pair[CS, C]], FCT ~func(A, B) C, A, B, C any](fa AS, fb BS, f FCT) CS { +func ZipWith[AS ~func() Option[Pair[AS, A]], BS ~func() Option[Pair[BS, B]], CS ~func() Option[Pair[CS, C]], FCT ~func(A, B) C, A, B, C any](fa AS, fb BS, f FCT) CS { // pre-declare to avoid cyclic reference - var m func(P.Pair[O.Option[P.Pair[AS, A]], O.Option[P.Pair[BS, B]]]) O.Option[P.Pair[CS, C]] + var m func(Pair[Option[Pair[AS, A]], Option[Pair[BS, B]]]) Option[Pair[CS, C]] recurse := func(as AS, bs BS) CS { - return func() O.Option[P.Pair[CS, C]] { + return func() Option[Pair[CS, C]] { // combine return F.Pipe1( P.MakePair(as(), bs()), @@ -38,8 +38,8 @@ func ZipWith[AS ~func() O.Option[P.Pair[AS, A]], BS ~func() O.Option[P.Pair[BS, } m = F.Flow2( - O.SequencePair[P.Pair[AS, A], P.Pair[BS, B]], - O.Map(func(t P.Pair[P.Pair[AS, A], P.Pair[BS, B]]) P.Pair[CS, C] { + O.SequencePair[Pair[AS, A], Pair[BS, B]], + O.Map(func(t Pair[Pair[AS, A], Pair[BS, B]]) Pair[CS, C] { return P.MakePair(recurse(P.Head(P.Head(t)), P.Head(P.Tail(t))), f(P.Tail(P.Head(t)), P.Tail(P.Tail(t)))) })) @@ -49,6 +49,6 @@ func ZipWith[AS ~func() O.Option[P.Pair[AS, A]], BS ~func() O.Option[P.Pair[BS, // Zip takes two iterators and returns an iterators of corresponding pairs. If one input iterators is short, excess elements of the // longer iterator are discarded -func Zip[AS ~func() O.Option[P.Pair[AS, A]], BS ~func() O.Option[P.Pair[BS, B]], CS ~func() O.Option[P.Pair[CS, P.Pair[A, B]]], A, B any](fb BS) func(AS) CS { - return F.Bind23of3(ZipWith[AS, BS, CS, func(A, B) P.Pair[A, B]])(fb, P.MakePair[A, B]) +func Zip[AS ~func() Option[Pair[AS, A]], BS ~func() Option[Pair[BS, B]], CS ~func() Option[Pair[CS, Pair[A, B]]], A, B any](fb BS) func(AS) CS { + return F.Bind23of3(ZipWith[AS, BS, CS, func(A, B) Pair[A, B]])(fb, P.MakePair[A, B]) } diff --git a/v2/iterator/stateless/io.go b/v2/iterator/stateless/io.go index c878f2e..e3ef359 100644 --- a/v2/iterator/stateless/io.go +++ b/v2/iterator/stateless/io.go @@ -16,17 +16,15 @@ package stateless import ( - IO "github.com/IBM/fp-go/v2/io" G "github.com/IBM/fp-go/v2/iterator/stateless/generic" - L "github.com/IBM/fp-go/v2/lazy" ) // FromLazy returns an [Iterator] on top of a lazy function -func FromLazy[U any](l L.Lazy[U]) Iterator[U] { +func FromLazy[U any](l Lazy[U]) Iterator[U] { return G.FromLazy[Iterator[U]](l) } // FromIO returns an [Iterator] on top of an IO function -func FromIO[U any](io IO.IO[U]) Iterator[U] { +func FromIO[U any](io IO[U]) Iterator[U] { return G.FromLazy[Iterator[U]](io) } diff --git a/v2/iterator/stateless/iterator.go b/v2/iterator/stateless/iterator.go index 3030c92..c39efbd 100644 --- a/v2/iterator/stateless/iterator.go +++ b/v2/iterator/stateless/iterator.go @@ -19,23 +19,22 @@ import ( "github.com/IBM/fp-go/v2/iooption" G "github.com/IBM/fp-go/v2/iterator/stateless/generic" M "github.com/IBM/fp-go/v2/monoid" - O "github.com/IBM/fp-go/v2/option" "github.com/IBM/fp-go/v2/pair" ) -// Next returns the [Iterator] for the next element in an iterator [pair.Pair] -func Next[U any](m pair.Pair[Iterator[U], U]) Iterator[U] { +// Next returns the [Iterator] for the next element in an iterator [Pair] +func Next[U any](m Pair[Iterator[U], U]) Iterator[U] { return pair.Head(m) } -// Current returns the current element in an [Iterator] [pair.Pair] -func Current[U any](m pair.Pair[Iterator[U], U]) U { +// Current returns the current element in an [Iterator] [Pair] +func Current[U any](m Pair[Iterator[U], U]) U { return pair.Tail(m) } // Empty returns the empty iterator func Empty[U any]() Iterator[U] { - return iooption.None[pair.Pair[Iterator[U], U]]() + return iooption.None[Pair[Iterator[U], U]]() } // Of returns an iterator with one single element @@ -97,12 +96,12 @@ func Replicate[U any](a U) Iterator[U] { } // FilterMap filters and transforms the content of an iterator -func FilterMap[U, V any](f func(U) O.Option[V]) Operator[U, V] { +func FilterMap[U, V any](f func(U) Option[V]) Operator[U, V] { return G.FilterMap[Iterator[V], Iterator[U]](f) } // Filter filters the content of an iterator -func Filter[U any](f func(U) bool) Operator[U, U] { +func Filter[U any](f Predicate[U]) Operator[U, U] { return G.Filter[Iterator[U]](f) } @@ -128,7 +127,7 @@ func Count(start int) Iterator[int] { } // FilterChain filters and transforms the content of an iterator -func FilterChain[U, V any](f func(U) O.Option[Iterator[V]]) Operator[U, V] { +func FilterChain[U, V any](f func(U) Option[Iterator[V]]) Operator[U, V] { return G.FilterChain[Iterator[Iterator[V]], Iterator[V], Iterator[U]](f) } diff --git a/v2/iterator/stateless/last.go b/v2/iterator/stateless/last.go index 771099e..ce58a4f 100644 --- a/v2/iterator/stateless/last.go +++ b/v2/iterator/stateless/last.go @@ -17,11 +17,10 @@ package stateless import ( G "github.com/IBM/fp-go/v2/iterator/stateless/generic" - O "github.com/IBM/fp-go/v2/option" ) // Last returns the last item in an iterator if such an item exists // Note that the function will consume the [Iterator] in this call completely, to identify the last element. Do not use this for infinite iterators -func Last[U any](mu Iterator[U]) O.Option[U] { +func Last[U any](mu Iterator[U]) Option[U] { return G.Last(mu) } diff --git a/v2/iterator/stateless/scan.go b/v2/iterator/stateless/scan.go index 13ea303..8090757 100644 --- a/v2/iterator/stateless/scan.go +++ b/v2/iterator/stateless/scan.go @@ -22,6 +22,6 @@ import ( // Scan takes an [Iterator] and returns a new [Iterator] of the same length, where the values // of the new [Iterator] are the result of the application of `f` to the value of the // source iterator with the previously accumulated value -func Scan[FCT ~func(V, U) V, U, V any](f FCT, initial V) func(ma Iterator[U]) Iterator[V] { +func Scan[FCT ~func(V, U) V, U, V any](f FCT, initial V) Operator[U, V] { return G.Scan[Iterator[V], Iterator[U]](f, initial) } diff --git a/v2/iterator/stateless/scan_test.go b/v2/iterator/stateless/scan_test.go index fdf1ca0..683ee93 100644 --- a/v2/iterator/stateless/scan_test.go +++ b/v2/iterator/stateless/scan_test.go @@ -29,7 +29,7 @@ func TestScan(t *testing.T) { dst := F.Pipe1( src, - Scan(func(cur P.Pair[int, string], val string) P.Pair[int, string] { + Scan(func(cur Pair[int, string], val string) Pair[int, string] { return P.MakePair(P.Head(cur)+1, val) }, P.MakePair(0, "")), ) diff --git a/v2/iterator/stateless/seq.go b/v2/iterator/stateless/seq.go new file mode 100644 index 0000000..85588e5 --- /dev/null +++ b/v2/iterator/stateless/seq.go @@ -0,0 +1,29 @@ +package stateless + +import ( + O "github.com/IBM/fp-go/v2/option" + P "github.com/IBM/fp-go/v2/pair" +) + +// ToSeq converts the stateless [Iterator] to an idiomatic go iterator +func ToSeq[T any](it Iterator[T]) Seq[T] { + current := Current[T] + return func(yield Predicate[T]) { + next, ok := O.Unwrap(it()) + for ok && yield(current(next)) { + next, ok = O.Unwrap(Next(next)()) + } + } +} + +// ToSeq2 converts the stateless [Iterator] to an idiomatic go iterator +func ToSeq2[K, V any](it Iterator[Pair[K, V]]) Seq2[K, V] { + current := Current[Pair[K, V]] + return func(yield func(K, V) bool) { + yp := P.Paired(yield) + next, ok := O.Unwrap(it()) + for ok && yp(current(next)) { + next, ok = O.Unwrap(Next(next)()) + } + } +} diff --git a/v2/iterator/stateless/take.go b/v2/iterator/stateless/take.go index f9aef51..ef067fb 100644 --- a/v2/iterator/stateless/take.go +++ b/v2/iterator/stateless/take.go @@ -20,6 +20,6 @@ import ( ) // Take limits the number of values in the [Iterator] to a maximum number -func Take[U any](n int) func(ma Iterator[U]) Iterator[U] { +func Take[U any](n int) Operator[U, U] { return G.Take[Iterator[U]](n) } diff --git a/v2/iterator/stateless/types.go b/v2/iterator/stateless/types.go index e27fe6a..5edcaa8 100644 --- a/v2/iterator/stateless/types.go +++ b/v2/iterator/stateless/types.go @@ -16,18 +16,29 @@ package stateless import ( - L "github.com/IBM/fp-go/v2/lazy" + "iter" + + "github.com/IBM/fp-go/v2/io" + "github.com/IBM/fp-go/v2/lazy" "github.com/IBM/fp-go/v2/option" "github.com/IBM/fp-go/v2/pair" + "github.com/IBM/fp-go/v2/predicate" "github.com/IBM/fp-go/v2/reader" ) type ( - Option[A any] = option.Option[A] + Option[A any] = option.Option[A] + Lazy[A any] = lazy.Lazy[A] + Pair[L, R any] = pair.Pair[L, R] + Predicate[A any] = predicate.Predicate[A] + IO[A any] = io.IO[A] // Iterator represents a stateless, pure way to iterate over a sequence - Iterator[U any] L.Lazy[Option[pair.Pair[Iterator[U], U]]] + Iterator[U any] Lazy[Option[Pair[Iterator[U], U]]] Kleisli[A, B any] = reader.Reader[A, Iterator[B]] Operator[A, B any] = Kleisli[Iterator[A], B] + + Seq[T any] = iter.Seq[T] + Seq2[K, V any] = iter.Seq2[K, V] ) diff --git a/v2/iterator/stateless/uniq.go b/v2/iterator/stateless/uniq.go index a0eb7a6..5e43559 100644 --- a/v2/iterator/stateless/uniq.go +++ b/v2/iterator/stateless/uniq.go @@ -27,6 +27,6 @@ func StrictUniq[A comparable](as Iterator[A]) Iterator[A] { // Uniq converts an [Iterator] of arbitrary items into an [Iterator] or unique items // where uniqueness is determined based on a key extractor function -func Uniq[A any, K comparable](f func(A) K) func(as Iterator[A]) Iterator[A] { +func Uniq[A any, K comparable](f func(A) K) Operator[A, A] { return G.Uniq[Iterator[A]](f) } diff --git a/v2/iterator/stateless/zip.go b/v2/iterator/stateless/zip.go index 4a2649b..573c3ce 100644 --- a/v2/iterator/stateless/zip.go +++ b/v2/iterator/stateless/zip.go @@ -17,7 +17,6 @@ package stateless import ( G "github.com/IBM/fp-go/v2/iterator/stateless/generic" - P "github.com/IBM/fp-go/v2/pair" ) // ZipWith applies a function to pairs of elements at the same index in two iterators, collecting the results in a new iterator. If one @@ -28,6 +27,6 @@ func ZipWith[FCT ~func(A, B) C, A, B, C any](fa Iterator[A], fb Iterator[B], f F // Zip takes two iterators and returns an iterators of corresponding pairs. If one input iterators is short, excess elements of the // longer iterator are discarded -func Zip[A, B any](fb Iterator[B]) func(Iterator[A]) Iterator[P.Pair[A, B]] { - return G.Zip[Iterator[A], Iterator[B], Iterator[P.Pair[A, B]]](fb) +func Zip[A, B any](fb Iterator[B]) Operator[A, Pair[A, B]] { + return G.Zip[Iterator[A], Iterator[B], Iterator[Pair[A, B]]](fb) } diff --git a/v2/optics/lens/lens.go b/v2/optics/lens/lens.go index d1f111f..39ed8e2 100644 --- a/v2/optics/lens/lens.go +++ b/v2/optics/lens/lens.go @@ -92,7 +92,7 @@ func setCopyCurried[SET ~func(A) Endomorphism[*S], S, A any](setter SET) func(A) // name := nameLens.Get(person) // "Alice" // updated := nameLens.Set("Bob")(person) // Person{Name: "Bob", Age: 30} func MakeLens[GET ~func(S) A, SET ~func(S, A) S, S, A any](get GET, set SET) Lens[S, A] { - return MakeLensCurried(get, F.Curry2(F.Swap(set))) + return MakeLensCurried(get, F.Bind2of2(set)) } // MakeLensCurried creates a [Lens] with a curried setter F. diff --git a/v2/optics/optional/optional.go b/v2/optics/optional/optional.go index 27e9466..2465f5b 100644 --- a/v2/optics/optional/optional.go +++ b/v2/optics/optional/optional.go @@ -42,7 +42,7 @@ func setCopy[SET ~func(*S, A) *S, S, A any](setter SET) func(s *S, a A) *S { // data. This happens automatically if the data is passed by value. For pointers consider to use `MakeOptionalRef` // and for other kinds of data structures that are copied by reference make sure the setter creates the copy. func MakeOptional[S, A any](get func(S) O.Option[A], set func(S, A) S) Optional[S, A] { - return Optional[S, A]{GetOption: get, Set: EM.Curry2(F.Swap(set))} + return Optional[S, A]{GetOption: get, Set: F.Bind2of2(set)} } // MakeOptionalRef creates an Optional based on a getter and a setter function. The setter passed in does not have to create a shallow diff --git a/v2/samples/presentation/examples/example_immutability_test.go b/v2/samples/presentation/examples/example_immutability_test.go index 136fc55..2643534 100644 --- a/v2/samples/presentation/examples/example_immutability_test.go +++ b/v2/samples/presentation/examples/example_immutability_test.go @@ -95,7 +95,7 @@ func Example_immutability_struct() { p1 := MakePerson("Carsten", 53) // func(int) func(Person) Person - setAge := F.Curry2(F.Swap(Person.SetAge)) + setAge := F.Bind2of2(Person.SetAge) p2 := F.Pipe1( p1, diff --git a/v2/string/string.go b/v2/string/string.go index be45855..b37cef5 100644 --- a/v2/string/string.go +++ b/v2/string/string.go @@ -40,10 +40,10 @@ var ( Equals = F.Curry2(Eq) // Includes returns a predicate that tests for the existence of the search string - Includes = F.Curry2(F.Swap(strings.Contains)) + Includes = F.Bind2of2(strings.Contains) // HasPrefix returns a predicate that checks if the prefis is included in the string - HasPrefix = F.Curry2(F.Swap(strings.HasPrefix)) + HasPrefix = F.Bind2of2(strings.HasPrefix) ) func Eq(left string, right string) bool {