mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
fix: add inline annotations
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
@@ -17,6 +17,37 @@ package array
|
|||||||
|
|
||||||
func Slice[GA ~[]A, A any](low, high int) func(as GA) GA {
|
func Slice[GA ~[]A, A any](low, high int) func(as GA) GA {
|
||||||
return func(as GA) GA {
|
return func(as GA) GA {
|
||||||
|
length := len(as)
|
||||||
|
|
||||||
|
// Handle negative indices - count backward from the end
|
||||||
|
if low < 0 {
|
||||||
|
low = length + low
|
||||||
|
if low < 0 {
|
||||||
|
low = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if high < 0 {
|
||||||
|
high = length + high
|
||||||
|
if high < 0 {
|
||||||
|
high = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start index > array length: return empty array
|
||||||
|
if low > length {
|
||||||
|
return Empty[GA, A]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End index > array length: slice to the end
|
||||||
|
if high > length {
|
||||||
|
high = length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start >= end: return empty array
|
||||||
|
if low >= high {
|
||||||
|
return Empty[GA, A]()
|
||||||
|
}
|
||||||
|
|
||||||
return as[low:high]
|
return as[low:high]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import (
|
|||||||
// return i%2 == 0 && x%2 == 0
|
// return i%2 == 0 && x%2 == 0
|
||||||
// })
|
// })
|
||||||
// result := hasEvenAtEvenIndex([]int{1, 3, 4, 5}) // true (4 is at index 2)
|
// result := hasEvenAtEvenIndex([]int{1, 3, 4, 5}) // true (4 is at index 2)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func AnyWithIndex[A any](pred func(int, A) bool) func([]A) bool {
|
func AnyWithIndex[A any](pred func(int, A) bool) func([]A) bool {
|
||||||
return G.AnyWithIndex[[]A](pred)
|
return G.AnyWithIndex[[]A](pred)
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,8 @@ func AnyWithIndex[A any](pred func(int, A) bool) func([]A) bool {
|
|||||||
//
|
//
|
||||||
// hasEven := array.Any(func(x int) bool { return x%2 == 0 })
|
// hasEven := array.Any(func(x int) bool { return x%2 == 0 })
|
||||||
// result := hasEven([]int{1, 3, 4, 5}) // true
|
// result := hasEven([]int{1, 3, 4, 5}) // true
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Any[A any](pred func(A) bool) func([]A) bool {
|
func Any[A any](pred func(A) bool) func([]A) bool {
|
||||||
return G.Any[[]A](pred)
|
return G.Any[[]A](pred)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,22 +26,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// From constructs an array from a set of variadic arguments
|
// From constructs an array from a set of variadic arguments
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func From[A any](data ...A) []A {
|
func From[A any](data ...A) []A {
|
||||||
return G.From[[]A](data...)
|
return G.From[[]A](data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`.
|
// MakeBy returns a `Array` of length `n` with element `i` initialized with `f(i)`.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MakeBy[F ~func(int) A, A any](n int, f F) []A {
|
func MakeBy[F ~func(int) A, A any](n int, f F) []A {
|
||||||
return G.MakeBy[[]A](n, f)
|
return G.MakeBy[[]A](n, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replicate creates a `Array` containing a value repeated the specified number of times.
|
// Replicate creates a `Array` containing a value repeated the specified number of times.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Replicate[A any](n int, a A) []A {
|
func Replicate[A any](n int, a A) []A {
|
||||||
return G.Replicate[[]A](n, a)
|
return G.Replicate[[]A](n, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadMap applies a function to each element of an array, returning a new array with the results.
|
// MonadMap applies a function to each element of an array, returning a new array with the results.
|
||||||
// This is the monadic version of Map that takes the array as the first parameter.
|
// 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 A) B) []B {
|
||||||
return G.MonadMap[[]A, []B](as, f)
|
return G.MonadMap[[]A, []B](as, f)
|
||||||
}
|
}
|
||||||
@@ -58,6 +66,8 @@ 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.
|
// 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) func([]A) []B {
|
||||||
return G.MapWithIndex[[]A, []B](f)
|
return G.MapWithIndex[[]A, []B](f)
|
||||||
}
|
}
|
||||||
@@ -69,6 +79,8 @@ func MapWithIndex[A, B any](f func(int, A) B) func([]A) []B {
|
|||||||
//
|
//
|
||||||
// double := array.Map(func(x int) int { return x * 2 })
|
// double := array.Map(func(x int) int { return x * 2 })
|
||||||
// result := double([]int{1, 2, 3}) // [2, 4, 6]
|
// 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 A) B) func([]A) []B {
|
||||||
return G.Map[[]A, []B, A, B](f)
|
return G.Map[[]A, []B, A, B](f)
|
||||||
}
|
}
|
||||||
@@ -104,11 +116,15 @@ 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
|
// 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) EM.Endomorphism[[]A] {
|
||||||
return G.Filter[[]A](pred)
|
return G.Filter[[]A](pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterWithIndex returns a new array with all elements from the original array that match a predicate
|
// 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) EM.Endomorphism[[]A] {
|
||||||
return G.FilterWithIndex[[]A](pred)
|
return G.FilterWithIndex[[]A](pred)
|
||||||
}
|
}
|
||||||
@@ -120,27 +136,37 @@ func FilterRef[A any](pred func(*A) bool) EM.Endomorphism[[]A] {
|
|||||||
|
|
||||||
// MonadFilterMap maps an array with a function that returns an Option and keeps only the Some values.
|
// MonadFilterMap maps an array with a function that returns an Option and keeps only the Some values.
|
||||||
// This is the monadic version that takes the array as the first parameter.
|
// 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 func(A) O.Option[B]) []B {
|
||||||
return G.MonadFilterMap[[]A, []B](fa, f)
|
return G.MonadFilterMap[[]A, []B](fa, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadFilterMapWithIndex maps an array with a function that takes an index and returns an Option,
|
// MonadFilterMapWithIndex maps an array with a function that takes an index and returns an Option,
|
||||||
// keeping only the Some values. This is the monadic version that takes the array as the first parameter.
|
// 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) O.Option[B]) []B {
|
||||||
return G.MonadFilterMapWithIndex[[]A, []B](fa, f)
|
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 [O.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 func(A) O.Option[B]) func([]A) []B {
|
||||||
return G.FilterMap[[]A, []B](f)
|
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 [O.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) O.Option[B]) func([]A) []B {
|
||||||
return G.FilterMapWithIndex[[]A, []B](f)
|
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 [O.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 func(A) O.Option[[]B]) func([]A) []B {
|
||||||
return G.FilterChain[[]A](f)
|
return G.FilterChain[[]A](f)
|
||||||
}
|
}
|
||||||
@@ -161,6 +187,7 @@ func reduceRef[A, B any](fa []A, f func(B, *A) B, initial B) B {
|
|||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func MonadReduce[A, B any](fa []A, f func(B, A) B, initial B) B {
|
func MonadReduce[A, B any](fa []A, f func(B, A) B, initial B) B {
|
||||||
return G.MonadReduce(fa, f, initial)
|
return G.MonadReduce(fa, f, initial)
|
||||||
}
|
}
|
||||||
@@ -171,23 +198,31 @@ func MonadReduce[A, B any](fa []A, f func(B, A) B, initial B) B {
|
|||||||
//
|
//
|
||||||
// sum := array.Reduce(func(acc, x int) int { return acc + x }, 0)
|
// sum := array.Reduce(func(acc, x int) int { return acc + x }, 0)
|
||||||
// result := sum([]int{1, 2, 3, 4, 5}) // 15
|
// result := sum([]int{1, 2, 3, 4, 5}) // 15
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
func Reduce[A, B any](f func(B, A) B, initial B) func([]A) B {
|
||||||
return G.Reduce[[]A](f, initial)
|
return G.Reduce[[]A](f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReduceWithIndex folds an array from left to right with access to the index,
|
// ReduceWithIndex folds an array from left to right with access to the index,
|
||||||
// applying a function to accumulate a result.
|
// applying a function to accumulate a result.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func([]A) B {
|
func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func([]A) B {
|
||||||
return G.ReduceWithIndex[[]A](f, initial)
|
return G.ReduceWithIndex[[]A](f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReduceRight folds an array from right to left, applying a function to accumulate a result.
|
// ReduceRight folds an array from right to left, applying a function to accumulate a result.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ReduceRight[A, B any](f func(A, B) B, initial B) func([]A) B {
|
func ReduceRight[A, B any](f func(A, B) B, initial B) func([]A) B {
|
||||||
return G.ReduceRight[[]A](f, initial)
|
return G.ReduceRight[[]A](f, initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReduceRightWithIndex folds an array from right to left with access to the index,
|
// ReduceRightWithIndex folds an array from right to left with access to the index,
|
||||||
// applying a function to accumulate a result.
|
// applying a function to accumulate a result.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ReduceRightWithIndex[A, B any](f func(int, A, B) B, initial B) func([]A) B {
|
func ReduceRightWithIndex[A, B any](f func(int, A, B) B, initial B) func([]A) B {
|
||||||
return G.ReduceRightWithIndex[[]A](f, initial)
|
return G.ReduceRightWithIndex[[]A](f, initial)
|
||||||
}
|
}
|
||||||
@@ -201,11 +236,15 @@ func ReduceRef[A, B any](f func(B, *A) B, initial B) func([]A) B {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append adds an element to the end of an array, returning a new array.
|
// Append adds an element to the end of an array, returning a new array.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Append[A any](as []A, a A) []A {
|
func Append[A any](as []A, a A) []A {
|
||||||
return G.Append(as, a)
|
return G.Append(as, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmpty checks if an array has no elements.
|
// IsEmpty checks if an array has no elements.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func IsEmpty[A any](as []A) bool {
|
func IsEmpty[A any](as []A) bool {
|
||||||
return G.IsEmpty(as)
|
return G.IsEmpty(as)
|
||||||
}
|
}
|
||||||
@@ -216,6 +255,8 @@ func IsNonEmpty[A any](as []A) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Empty returns an empty array of type A.
|
// Empty returns an empty array of type A.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Empty[A any]() []A {
|
func Empty[A any]() []A {
|
||||||
return G.Empty[[]A]()
|
return G.Empty[[]A]()
|
||||||
}
|
}
|
||||||
@@ -226,12 +267,16 @@ func Zero[A any]() []A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Of constructs a single element array
|
// Of constructs a single element array
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Of[A any](a A) []A {
|
func Of[A any](a A) []A {
|
||||||
return G.Of[[]A](a)
|
return G.Of[[]A](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadChain applies a function that returns an array to each element and flattens the results.
|
// MonadChain applies a function that returns an array to each element and flattens the results.
|
||||||
// This is the monadic version that takes the array as the first parameter (also known as FlatMap).
|
// 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 func(a A) []B) []B {
|
||||||
return G.MonadChain[[]A, []B](fa, f)
|
return G.MonadChain[[]A, []B](fa, f)
|
||||||
}
|
}
|
||||||
@@ -243,52 +288,70 @@ func MonadChain[A, B any](fa []A, f func(a A) []B) []B {
|
|||||||
//
|
//
|
||||||
// duplicate := array.Chain(func(x int) []int { return []int{x, x} })
|
// duplicate := array.Chain(func(x int) []int { return []int{x, x} })
|
||||||
// result := duplicate([]int{1, 2, 3}) // [1, 1, 2, 2, 3, 3]
|
// 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 func(A) []B) func([]A) []B {
|
||||||
return G.Chain[[]A, []B](f)
|
return G.Chain[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadAp applies an array of functions to an array of values, producing all combinations.
|
// MonadAp applies an array of functions to an array of values, producing all combinations.
|
||||||
// This is the monadic version that takes both arrays as parameters.
|
// This is the monadic version that takes both arrays as parameters.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadAp[B, A any](fab []func(A) B, fa []A) []B {
|
func MonadAp[B, A any](fab []func(A) B, fa []A) []B {
|
||||||
return G.MonadAp[[]B](fab, fa)
|
return G.MonadAp[[]B](fab, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ap applies an array of functions to an array of values, producing all combinations.
|
// Ap applies an array of functions to an array of values, producing all combinations.
|
||||||
// This is the curried version.
|
// 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) func([]func(A) B) []B {
|
||||||
return G.Ap[[]B, []func(A) B](fa)
|
return G.Ap[[]B, []func(A) B](fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match performs pattern matching on an array, calling onEmpty if empty or onNonEmpty if not.
|
// Match performs pattern matching on an array, calling onEmpty if empty or onNonEmpty if not.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
func Match[A, B any](onEmpty func() B, onNonEmpty func([]A) B) func([]A) B {
|
||||||
return G.Match[[]A](onEmpty, onNonEmpty)
|
return G.Match[[]A](onEmpty, onNonEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchLeft performs pattern matching on an array, calling onEmpty if empty or onNonEmpty with head and tail if not.
|
// MatchLeft performs pattern matching on an array, calling onEmpty if empty or onNonEmpty with head and tail if not.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
|
func MatchLeft[A, B any](onEmpty func() B, onNonEmpty func(A, []A) B) func([]A) B {
|
||||||
return G.MatchLeft[[]A](onEmpty, onNonEmpty)
|
return G.MatchLeft[[]A](onEmpty, onNonEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tail returns all elements except the first, wrapped in an Option.
|
// Tail returns all elements except the first, wrapped in an Option.
|
||||||
// Returns None if the array is empty.
|
// Returns None if the array is empty.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Tail[A any](as []A) O.Option[[]A] {
|
func Tail[A any](as []A) O.Option[[]A] {
|
||||||
return G.Tail(as)
|
return G.Tail(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Head returns the first element of an array, wrapped in an Option.
|
// Head returns the first element of an array, wrapped in an Option.
|
||||||
// Returns None if the array is empty.
|
// Returns None if the array is empty.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Head[A any](as []A) O.Option[A] {
|
func Head[A any](as []A) O.Option[A] {
|
||||||
return G.Head(as)
|
return G.Head(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
// First returns the first element of an array, wrapped in an Option (alias for Head).
|
// First returns the first element of an array, wrapped in an Option (alias for Head).
|
||||||
// Returns None if the array is empty.
|
// Returns None if the array is empty.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func First[A any](as []A) O.Option[A] {
|
func First[A any](as []A) O.Option[A] {
|
||||||
return G.First(as)
|
return G.First(as)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last returns the last element of an array, wrapped in an Option.
|
// Last returns the last element of an array, wrapped in an Option.
|
||||||
// Returns None if the array is empty.
|
// Returns None if the array is empty.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Last[A any](as []A) O.Option[A] {
|
func Last[A any](as []A) O.Option[A] {
|
||||||
return G.Last(as)
|
return G.Last(as)
|
||||||
}
|
}
|
||||||
@@ -336,6 +399,8 @@ func Intercalate[A any](m M.Monoid[A]) func(A) func([]A) A {
|
|||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// result := array.Flatten([][]int{{1, 2}, {3, 4}, {5}}) // [1, 2, 3, 4, 5]
|
// result := array.Flatten([][]int{{1, 2}, {3, 4}, {5}}) // [1, 2, 3, 4, 5]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Flatten[A any](mma [][]A) []A {
|
func Flatten[A any](mma [][]A) []A {
|
||||||
return G.Flatten(mma)
|
return G.Flatten(mma)
|
||||||
}
|
}
|
||||||
@@ -347,17 +412,23 @@ func Slice[A any](low, high int) func(as []A) []A {
|
|||||||
|
|
||||||
// Lookup returns the element at the specified index, wrapped in an Option.
|
// Lookup returns the element at the specified index, wrapped in an Option.
|
||||||
// Returns None if the index is out of bounds.
|
// 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) O.Option[A] {
|
||||||
return G.Lookup[[]A](idx)
|
return G.Lookup[[]A](idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpsertAt returns a function that inserts or updates an element at a specific index.
|
// UpsertAt returns a function that inserts or updates an element at a specific index.
|
||||||
// If the index is out of bounds, the element is appended.
|
// 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) EM.Endomorphism[[]A] {
|
||||||
return G.UpsertAt[[]A](a)
|
return G.UpsertAt[[]A](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the number of elements in an array.
|
// Size returns the number of elements in an array.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Size[A any](as []A) int {
|
func Size[A any](as []A) int {
|
||||||
return G.Size(as)
|
return G.Size(as)
|
||||||
}
|
}
|
||||||
@@ -365,12 +436,16 @@ func Size[A any](as []A) int {
|
|||||||
// MonadPartition splits an array into two arrays based on a predicate.
|
// MonadPartition splits an array into two arrays based on a predicate.
|
||||||
// The first array contains elements for which the predicate returns false,
|
// The first array contains elements for which the predicate returns false,
|
||||||
// the second contains elements for which it returns true.
|
// the second contains elements for which it returns true.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadPartition[A any](as []A, pred func(A) bool) tuple.Tuple2[[]A, []A] {
|
func MonadPartition[A any](as []A, pred func(A) bool) tuple.Tuple2[[]A, []A] {
|
||||||
return G.MonadPartition(as, pred)
|
return G.MonadPartition(as, pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Partition creates two new arrays out of one, the left result contains the elements
|
// Partition creates two new arrays out of one, the left result contains the elements
|
||||||
// for which the predicate returns false, the right one those for which the predicate returns true
|
// for which the predicate returns false, the right one those for which the predicate returns true
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Partition[A any](pred func(A) bool) func([]A) tuple.Tuple2[[]A, []A] {
|
func Partition[A any](pred func(A) bool) func([]A) tuple.Tuple2[[]A, []A] {
|
||||||
return G.Partition[[]A](pred)
|
return G.Partition[[]A](pred)
|
||||||
}
|
}
|
||||||
@@ -391,53 +466,73 @@ func ConstNil[A any]() []A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SliceRight extracts a subarray from the specified start index to the end.
|
// 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) EM.Endomorphism[[]A] {
|
||||||
return G.SliceRight[[]A](start)
|
return G.SliceRight[[]A](start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy creates a shallow copy of the array
|
// Copy creates a shallow copy of the array
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Copy[A any](b []A) []A {
|
func Copy[A any](b []A) []A {
|
||||||
return G.Copy(b)
|
return G.Copy(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone creates a deep copy of the array using the provided endomorphism to clone the values
|
// 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) func(as []A) []A {
|
||||||
return G.Clone[[]A](f)
|
return G.Clone[[]A](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
// FoldMap maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func([]A) B {
|
||||||
return G.FoldMap[[]A](m)
|
return G.FoldMap[[]A](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FoldMapWithIndex maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
// FoldMapWithIndex maps and folds an array. Map the Array passing each value to the iterating function. Then fold the results using the provided Monoid.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func([]A) B {
|
func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func([]A) B {
|
||||||
return G.FoldMapWithIndex[[]A](m)
|
return G.FoldMapWithIndex[[]A](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fold folds the array using the provided Monoid.
|
// Fold folds the array using the provided Monoid.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
func Fold[A any](m M.Monoid[A]) func([]A) A {
|
||||||
return G.Fold[[]A](m)
|
return G.Fold[[]A](m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push adds an element to the end of an array (alias for Append).
|
// Push adds an element to the end of an array (alias for Append).
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Push[A any](a A) EM.Endomorphism[[]A] {
|
func Push[A any](a A) EM.Endomorphism[[]A] {
|
||||||
return G.Push[EM.Endomorphism[[]A]](a)
|
return G.Push[EM.Endomorphism[[]A]](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonadFlap applies a value to an array of functions, producing an array of results.
|
// MonadFlap applies a value to an array of functions, producing an array of results.
|
||||||
// This is the monadic version that takes both parameters.
|
// This is the monadic version that takes both parameters.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
func MonadFlap[B, A any](fab []func(A) B, a A) []B {
|
||||||
return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a)
|
return G.MonadFlap[func(A) B, []func(A) B, []B, A, B](fab, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flap applies a value to an array of functions, producing an array of results.
|
// Flap applies a value to an array of functions, producing an array of results.
|
||||||
// This is the curried version.
|
// 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) func([]func(A) B) []B {
|
||||||
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
|
return G.Flap[func(A) B, []func(A) B, []B, A, B](a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend adds an element to the beginning of an array, returning a new array.
|
// 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] {
|
func Prepend[A any](head A) EM.Endomorphism[[]A] {
|
||||||
return G.Prepend[EM.Endomorphism[[]A]](head)
|
return G.Prepend[EM.Endomorphism[[]A]](head)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import (
|
|||||||
// Y int
|
// Y int
|
||||||
// }
|
// }
|
||||||
// result := array.Do(State{})
|
// result := array.Do(State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Do[S any](
|
func Do[S any](
|
||||||
empty S,
|
empty S,
|
||||||
) []S {
|
) []S {
|
||||||
@@ -50,6 +52,8 @@ func Do[S any](
|
|||||||
// func(s struct{}) []int { return []int{1, 2} },
|
// func(s struct{}) []int { return []int{1, 2} },
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Bind[S1, S2, T any](
|
func Bind[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) []T,
|
f func(S1) []T,
|
||||||
@@ -70,6 +74,8 @@ func Bind[S1, S2, T any](
|
|||||||
// },
|
// },
|
||||||
// func(s struct{ X int }) int { return s.X * 2 },
|
// func(s struct{ X int }) int { return s.X * 2 },
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Let[S1, S2, T any](
|
func Let[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
@@ -90,6 +96,8 @@ func Let[S1, S2, T any](
|
|||||||
// },
|
// },
|
||||||
// "constant",
|
// "constant",
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func LetTo[S1, S2, T any](
|
func LetTo[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
@@ -108,6 +116,8 @@ func LetTo[S1, S2, T any](
|
|||||||
// return struct{ X int }{x}
|
// return struct{ X int }{x}
|
||||||
// }),
|
// }),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func BindTo[S1, T any](
|
func BindTo[S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func([]T) []S1 {
|
) func([]T) []S1 {
|
||||||
@@ -128,6 +138,8 @@ func BindTo[S1, T any](
|
|||||||
// },
|
// },
|
||||||
// []int{10, 20},
|
// []int{10, 20},
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApS[S1, S2, T any](
|
func ApS[S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa []T,
|
fa []T,
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import (
|
|||||||
// findGreaterThan3 := array.FindFirst(func(x int) bool { return x > 3 })
|
// findGreaterThan3 := array.FindFirst(func(x int) bool { return x > 3 })
|
||||||
// result := findGreaterThan3([]int{1, 2, 4, 5}) // Some(4)
|
// result := findGreaterThan3([]int{1, 2, 4, 5}) // Some(4)
|
||||||
// result2 := findGreaterThan3([]int{1, 2, 3}) // None
|
// 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) func([]A) O.Option[A] {
|
||||||
return G.FindFirst[[]A](pred)
|
return G.FindFirst[[]A](pred)
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,8 @@ func FindFirst[A any](pred func(A) bool) func([]A) O.Option[A] {
|
|||||||
// return i%2 == 0 && x%2 == 0
|
// return i%2 == 0 && x%2 == 0
|
||||||
// })
|
// })
|
||||||
// result := findEvenAtEvenIndex([]int{1, 3, 4, 5}) // Some(4)
|
// 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) func([]A) O.Option[A] {
|
||||||
return G.FindFirstWithIndex[[]A](pred)
|
return G.FindFirstWithIndex[[]A](pred)
|
||||||
}
|
}
|
||||||
@@ -59,12 +63,16 @@ func FindFirstWithIndex[A any](pred func(int, A) bool) func([]A) O.Option[A] {
|
|||||||
// return option.None[int]()
|
// return option.None[int]()
|
||||||
// })
|
// })
|
||||||
// result := parseFirst([]string{"a", "42", "b"}) // Some(42)
|
// 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 func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
return G.FindFirstMap[[]A](sel)
|
return G.FindFirstMap[[]A](sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindFirstMapWithIndex finds the first element for which the selector function returns Some.
|
// FindFirstMapWithIndex finds the first element for which the selector function returns Some.
|
||||||
// The selector receives both the index and the element.
|
// 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) O.Option[B]) func([]A) O.Option[B] {
|
||||||
return G.FindFirstMapWithIndex[[]A](sel)
|
return G.FindFirstMapWithIndex[[]A](sel)
|
||||||
}
|
}
|
||||||
@@ -76,24 +84,32 @@ func FindFirstMapWithIndex[A, B any](sel func(int, A) O.Option[B]) func([]A) O.O
|
|||||||
//
|
//
|
||||||
// findGreaterThan3 := array.FindLast(func(x int) bool { return x > 3 })
|
// findGreaterThan3 := array.FindLast(func(x int) bool { return x > 3 })
|
||||||
// result := findGreaterThan3([]int{1, 4, 2, 5}) // Some(5)
|
// 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) func([]A) O.Option[A] {
|
||||||
return G.FindLast[[]A](pred)
|
return G.FindLast[[]A](pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindLastWithIndex finds the last element which satisfies a predicate function that also receives the index.
|
// FindLastWithIndex finds the last element which satisfies a predicate function that also receives the index.
|
||||||
// Returns Some(element) if found, None if no element matches.
|
// 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) func([]A) O.Option[A] {
|
||||||
return G.FindLastWithIndex[[]A](pred)
|
return G.FindLastWithIndex[[]A](pred)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindLastMap finds the last element for which the selector function returns Some.
|
// FindLastMap finds the last element for which the selector function returns Some.
|
||||||
// This combines finding and mapping in a single operation, searching from the end.
|
// 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 func(A) O.Option[B]) func([]A) O.Option[B] {
|
||||||
return G.FindLastMap[[]A](sel)
|
return G.FindLastMap[[]A](sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindLastMapWithIndex finds the last element for which the selector function returns Some.
|
// FindLastMapWithIndex finds the last element for which the selector function returns Some.
|
||||||
// The selector receives both the index and the element, searching from the end.
|
// 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) O.Option[B]) func([]A) O.Option[B] {
|
||||||
return G.FindLastMapWithIndex[[]A](sel)
|
return G.FindLastMapWithIndex[[]A](sel)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,16 +296,14 @@ func MatchLeft[AS ~[]A, A, B any](onEmpty func() B, onNonEmpty func(A, AS) B) fu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func Slice[AS ~[]A, A any](start int, end int) func(AS) AS {
|
func Slice[AS ~[]A, A any](start int, end int) func(AS) AS {
|
||||||
return func(a AS) AS {
|
return array.Slice[AS](start, end)
|
||||||
return a[start:end]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func SliceRight[AS ~[]A, A any](start int) func(AS) AS {
|
func SliceRight[AS ~[]A, A any](start int) func(AS) AS {
|
||||||
return func(a AS) AS {
|
return array.SliceRight[AS](start)
|
||||||
return a[start:]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Copy[AS ~[]A, A any](b AS) AS {
|
func Copy[AS ~[]A, A any](b AS) AS {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import (
|
|||||||
// // Concatenate all strings
|
// // Concatenate all strings
|
||||||
// concatStrings := array.ConcatAll(monoid.MonoidString())
|
// concatStrings := array.ConcatAll(monoid.MonoidString())
|
||||||
// result2 := concatStrings([]string{"Hello", " ", "World"}) // "Hello World"
|
// result2 := concatStrings([]string{"Hello", " ", "World"}) // "Hello World"
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ConcatAll[A any](m M.Monoid[A]) func([]A) A {
|
func ConcatAll[A any](m M.Monoid[A]) func([]A) A {
|
||||||
return Reduce(m.Concat, m.Empty())
|
return Reduce(m.Concat, m.Empty())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import (
|
|||||||
// return []string{fmt.Sprintf("%d", x), fmt.Sprintf("%d!", x)}
|
// return []string{fmt.Sprintf("%d", x), fmt.Sprintf("%d!", x)}
|
||||||
// })
|
// })
|
||||||
// // Result: ["1", "1!", "2", "2!", "3", "3!"]
|
// // Result: ["1", "1!", "2", "2!", "3", "3!"]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Monad[A, B any]() monad.Monad[A, B, []A, []B, []func(A) B] {
|
func Monad[A, B any]() monad.Monad[A, B, []A, []B, []func(A) B] {
|
||||||
return G.Monad[A, B, []A, []B, []func(A) B]()
|
return G.Monad[A, B, []A, []B, []func(A) B]()
|
||||||
}
|
}
|
||||||
|
|||||||
407
v2/array/slice_test.go
Normal file
407
v2/array/slice_test.go
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
// 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 array
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSliceBasicCases tests normal slicing operations
|
||||||
|
func TestSliceBasicCases(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
t.Run("normal slice from middle", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, Slice[int](1, 4)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice from start", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{0, 1, 2}, Slice[int](0, 3)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice to end", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{3, 4, 5}, Slice[int](3, 6)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice single element", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{2}, Slice[int](2, 3)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice entire array", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{0, 1, 2, 3, 4, 5}, Slice[int](0, 6)(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceNegativeIndices tests negative index handling (counting from end)
|
||||||
|
func TestSliceNegativeIndices(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
t.Run("negative start index", func(t *testing.T) {
|
||||||
|
// -2 means length + (-2) = 6 - 2 = 4
|
||||||
|
assert.Equal(t, []int{4, 5}, Slice[int](-2, 6)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative end index", func(t *testing.T) {
|
||||||
|
// -2 means length + (-2) = 6 - 2 = 4
|
||||||
|
assert.Equal(t, []int{0, 1, 2, 3}, Slice[int](0, -2)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("both negative indices", func(t *testing.T) {
|
||||||
|
// -4 = 2, -2 = 4
|
||||||
|
assert.Equal(t, []int{2, 3}, Slice[int](-4, -2)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative index beyond array start", func(t *testing.T) {
|
||||||
|
// -10 would be -4, clamped to 0
|
||||||
|
assert.Equal(t, []int{0, 1, 2}, Slice[int](-10, 3)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative end index beyond array start", func(t *testing.T) {
|
||||||
|
// -10 would be -4, clamped to 0
|
||||||
|
assert.Equal(t, []int{}, Slice[int](0, -10)(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceEmptyArray tests slicing on empty arrays (totality proof)
|
||||||
|
func TestSliceEmptyArray(t *testing.T) {
|
||||||
|
empty := []int{}
|
||||||
|
|
||||||
|
t.Run("slice empty array with zero indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](0, 0)(empty))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice empty array with positive indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](0, 5)(empty))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice empty array with negative indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](-1, -1)(empty))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice empty array with mixed indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](-5, 5)(empty))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceOutOfBounds tests out-of-bounds scenarios (totality proof)
|
||||||
|
func TestSliceOutOfBounds(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2, 3, 4}
|
||||||
|
|
||||||
|
t.Run("start index beyond array length", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](10, 15)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("end index beyond array length", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{2, 3, 4}, Slice[int](2, 100)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("both indices beyond array length", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](10, 20)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("start equals array length", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](5, 10)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("end equals array length", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{3, 4}, Slice[int](3, 5)(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceInvalidRanges tests invalid range scenarios (totality proof)
|
||||||
|
func TestSliceInvalidRanges(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2, 3, 4}
|
||||||
|
|
||||||
|
t.Run("start equals end", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](2, 2)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("start greater than end", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](4, 2)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("start greater than end with negative indices", func(t *testing.T) {
|
||||||
|
// -1 = 4, -3 = 2
|
||||||
|
assert.Equal(t, []int{}, Slice[int](-1, -3)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("zero range at start", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](0, 0)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("zero range at end", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](5, 5)(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceEdgeCases tests additional edge cases (totality proof)
|
||||||
|
func TestSliceEdgeCases(t *testing.T) {
|
||||||
|
t.Run("single element array - slice all", func(t *testing.T) {
|
||||||
|
data := []int{42}
|
||||||
|
assert.Equal(t, []int{42}, Slice[int](0, 1)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("single element array - slice none", func(t *testing.T) {
|
||||||
|
data := []int{42}
|
||||||
|
assert.Equal(t, []int{}, Slice[int](1, 1)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("single element array - negative indices", func(t *testing.T) {
|
||||||
|
data := []int{42}
|
||||||
|
assert.Equal(t, []int{42}, Slice[int](-1, 1)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("large array slice", func(t *testing.T) {
|
||||||
|
data := MakeBy(1000, func(i int) int { return i })
|
||||||
|
result := Slice[int](100, 200)(data)
|
||||||
|
assert.Equal(t, 100, len(result))
|
||||||
|
assert.Equal(t, 100, result[0])
|
||||||
|
assert.Equal(t, 199, result[99])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceWithDifferentTypes tests that Slice works with different types (totality proof)
|
||||||
|
func TestSliceWithDifferentTypes(t *testing.T) {
|
||||||
|
t.Run("string slice", func(t *testing.T) {
|
||||||
|
data := []string{"a", "b", "c", "d", "e"}
|
||||||
|
assert.Equal(t, []string{"b", "c", "d"}, Slice[string](1, 4)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("float slice", func(t *testing.T) {
|
||||||
|
data := []float64{1.1, 2.2, 3.3, 4.4, 5.5}
|
||||||
|
assert.Equal(t, []float64{2.2, 3.3}, Slice[float64](1, 3)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bool slice", func(t *testing.T) {
|
||||||
|
data := []bool{true, false, true, false}
|
||||||
|
assert.Equal(t, []bool{false, true}, Slice[bool](1, 3)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("struct slice", func(t *testing.T) {
|
||||||
|
type Point struct{ X, Y int }
|
||||||
|
data := []Point{{1, 2}, {3, 4}, {5, 6}}
|
||||||
|
assert.Equal(t, []Point{{3, 4}}, Slice[Point](1, 2)(data))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("pointer slice", func(t *testing.T) {
|
||||||
|
a, b, c := 1, 2, 3
|
||||||
|
data := []*int{&a, &b, &c}
|
||||||
|
result := Slice[*int](1, 3)(data)
|
||||||
|
assert.Equal(t, 2, len(result))
|
||||||
|
assert.Equal(t, 2, *result[0])
|
||||||
|
assert.Equal(t, 3, *result[1])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceNilArray tests behavior with nil arrays (totality proof)
|
||||||
|
func TestSliceNilArray(t *testing.T) {
|
||||||
|
var nilArray []int
|
||||||
|
|
||||||
|
t.Run("slice nil array with zero indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](0, 0)(nilArray))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice nil array with positive indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](0, 5)(nilArray))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice nil array with negative indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](-1, 1)(nilArray))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice nil array with out of bounds indices", func(t *testing.T) {
|
||||||
|
assert.Equal(t, []int{}, Slice[int](10, 20)(nilArray))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceComposition tests that Slice can be composed with other functions
|
||||||
|
func TestSliceComposition(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
|
||||||
|
t.Run("compose multiple slices", func(t *testing.T) {
|
||||||
|
// First slice [2:8], then slice [1:4] of result
|
||||||
|
slice1 := Slice[int](2, 8)
|
||||||
|
slice2 := Slice[int](1, 4)
|
||||||
|
result := slice2(slice1(data))
|
||||||
|
// [2,3,4,5,6,7] -> [3,4,5]
|
||||||
|
assert.Equal(t, []int{3, 4, 5}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice then map", func(t *testing.T) {
|
||||||
|
sliced := Slice[int](2, 5)(data)
|
||||||
|
mapped := Map(func(x int) int { return x * 2 })(sliced)
|
||||||
|
assert.Equal(t, []int{4, 6, 8}, mapped)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice then filter", func(t *testing.T) {
|
||||||
|
sliced := Slice[int](0, 6)(data)
|
||||||
|
filtered := Filter(func(x int) bool { return x%2 == 0 })(sliced)
|
||||||
|
assert.Equal(t, []int{0, 2, 4}, filtered)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceImmutability tests that Slice doesn't modify the original array
|
||||||
|
func TestSliceImmutability(t *testing.T) {
|
||||||
|
original := []int{0, 1, 2, 3, 4}
|
||||||
|
originalCopy := []int{0, 1, 2, 3, 4}
|
||||||
|
|
||||||
|
t.Run("slicing doesn't modify original", func(t *testing.T) {
|
||||||
|
result := Slice[int](1, 4)(original)
|
||||||
|
assert.Equal(t, []int{1, 2, 3}, result)
|
||||||
|
assert.Equal(t, originalCopy, original)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice shares underlying array with original", func(t *testing.T) {
|
||||||
|
// Note: Go's slice operation creates a view of the underlying array,
|
||||||
|
// not a deep copy. This is expected behavior and matches Go's built-in slice semantics.
|
||||||
|
result := Slice[int](1, 4)(original)
|
||||||
|
result[0] = 999
|
||||||
|
// The original array is affected because slices share the underlying array
|
||||||
|
assert.Equal(t, 999, original[1], "Slices share underlying array (expected Go behavior)")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSliceTotality is a comprehensive test proving Slice is a total function
|
||||||
|
// A total function is defined for all possible inputs and never panics
|
||||||
|
func TestSliceTotality(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
data []int
|
||||||
|
low int
|
||||||
|
high int
|
||||||
|
panic bool // Should always be false for a total function
|
||||||
|
}{
|
||||||
|
// Normal cases
|
||||||
|
{"normal range", []int{1, 2, 3, 4, 5}, 1, 3, false},
|
||||||
|
{"full range", []int{1, 2, 3}, 0, 3, false},
|
||||||
|
{"empty result", []int{1, 2, 3}, 1, 1, false},
|
||||||
|
|
||||||
|
// Edge cases with empty/nil arrays
|
||||||
|
{"empty array", []int{}, 0, 0, false},
|
||||||
|
{"empty array with indices", []int{}, 1, 5, false},
|
||||||
|
{"nil array", nil, 0, 5, false},
|
||||||
|
|
||||||
|
// Negative indices
|
||||||
|
{"negative low", []int{1, 2, 3, 4, 5}, -2, 5, false},
|
||||||
|
{"negative high", []int{1, 2, 3, 4, 5}, 0, -1, false},
|
||||||
|
{"both negative", []int{1, 2, 3, 4, 5}, -3, -1, false},
|
||||||
|
{"negative beyond bounds", []int{1, 2, 3}, -100, -50, false},
|
||||||
|
|
||||||
|
// Out of bounds
|
||||||
|
{"low beyond length", []int{1, 2, 3}, 10, 20, false},
|
||||||
|
{"high beyond length", []int{1, 2, 3}, 1, 100, false},
|
||||||
|
{"both beyond length", []int{1, 2, 3}, 10, 20, false},
|
||||||
|
|
||||||
|
// Invalid ranges
|
||||||
|
{"low equals high", []int{1, 2, 3}, 2, 2, false},
|
||||||
|
{"low greater than high", []int{1, 2, 3}, 3, 1, false},
|
||||||
|
{"negative invalid range", []int{1, 2, 3, 4, 5}, -1, -3, false},
|
||||||
|
|
||||||
|
// Extreme values
|
||||||
|
{"very large indices", []int{1, 2, 3}, 1000000, 2000000, false},
|
||||||
|
{"very negative indices", []int{1, 2, 3}, -1000000, -500000, false},
|
||||||
|
{"mixed extreme", []int{1, 2, 3}, -1000000, 1000000, false},
|
||||||
|
|
||||||
|
// Zero values
|
||||||
|
{"zero indices", []int{1, 2, 3}, 0, 0, false},
|
||||||
|
{"zero low", []int{1, 2, 3}, 0, 3, false},
|
||||||
|
{"zero high", []int{1, 2, 3}, 0, 0, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// This test proves totality by ensuring no panic occurs
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if !tc.panic {
|
||||||
|
t.Errorf("Slice panicked unexpectedly: %v", r)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if tc.panic {
|
||||||
|
t.Errorf("Slice should have panicked but didn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Execute the function - if it's total, it will never panic
|
||||||
|
result := Slice[int](tc.low, tc.high)(tc.data)
|
||||||
|
|
||||||
|
// Additional verification: result should always be a valid slice
|
||||||
|
assert.NotNil(t, result, "Result should never be nil")
|
||||||
|
assert.True(t, len(result) >= 0, "Result length should be non-negative")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSlicePropertyBased tests mathematical properties of Slice
|
||||||
|
func TestSlicePropertyBased(t *testing.T) {
|
||||||
|
data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
|
||||||
|
t.Run("identity: Slice(0, len) returns copy of array", func(t *testing.T) {
|
||||||
|
result := Slice[int](0, len(data))(data)
|
||||||
|
assert.Equal(t, data, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("empty: Slice(i, i) always returns empty", func(t *testing.T) {
|
||||||
|
for i := 0; i <= len(data); i++ {
|
||||||
|
result := Slice[int](i, i)(data)
|
||||||
|
assert.Equal(t, []int{}, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("length property: len(Slice(i, j)) = max(0, min(j, len) - max(i, 0))", func(t *testing.T) {
|
||||||
|
testCases := []struct{ low, high, expected int }{
|
||||||
|
{0, 5, 5},
|
||||||
|
{2, 7, 5},
|
||||||
|
{5, 5, 0},
|
||||||
|
{3, 2, 0}, // invalid range
|
||||||
|
{-2, 10, 2}, // -2 becomes 8, so slice [8:10] has length 2
|
||||||
|
{0, 100, 10},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
result := Slice[int](tc.low, tc.high)(data)
|
||||||
|
assert.Equal(t, tc.expected, len(result),
|
||||||
|
"Slice(%d, %d) should have length %d", tc.low, tc.high, tc.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("concatenation: Slice(0,i) + Slice(i,len) = original", func(t *testing.T) {
|
||||||
|
for i := 0; i <= len(data); i++ {
|
||||||
|
left := Slice[int](0, i)(data)
|
||||||
|
right := Slice[int](i, len(data))(data)
|
||||||
|
concatenated := append(left, right...)
|
||||||
|
assert.Equal(t, data, concatenated)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("subset property: all elements in slice are in original", func(t *testing.T) {
|
||||||
|
result := Slice[int](2, 7)(data)
|
||||||
|
for _, elem := range result {
|
||||||
|
found := false
|
||||||
|
for _, orig := range data {
|
||||||
|
if elem == orig {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, found, "Element %d should be in original array", elem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Made with Bob
|
||||||
@@ -30,6 +30,8 @@ import (
|
|||||||
// numbers := []int{3, 1, 4, 1, 5, 9, 2, 6}
|
// numbers := []int{3, 1, 4, 1, 5, 9, 2, 6}
|
||||||
// sorted := array.Sort(ord.FromStrictCompare[int]())(numbers)
|
// sorted := array.Sort(ord.FromStrictCompare[int]())(numbers)
|
||||||
// // Result: [1, 1, 2, 3, 4, 5, 6, 9]
|
// // 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]) func(ma []T) []T {
|
||||||
return G.Sort[[]T](ord)
|
return G.Sort[[]T](ord)
|
||||||
}
|
}
|
||||||
@@ -58,6 +60,8 @@ func Sort[T any](ord O.Ord[T]) func(ma []T) []T {
|
|||||||
// )
|
// )
|
||||||
// sorted := sortByAge(people)
|
// sorted := sortByAge(people)
|
||||||
// // Result: [{"Bob", 25}, {"Alice", 30}, {"Charlie", 35}]
|
// // 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) func(ma []T) []T {
|
||||||
return G.SortByKey[[]T](ord, f)
|
return G.SortByKey[[]T](ord, f)
|
||||||
}
|
}
|
||||||
@@ -87,6 +91,8 @@ func SortByKey[K, T any](ord O.Ord[K], f func(T) K) func(ma []T) []T {
|
|||||||
// })
|
// })
|
||||||
// sorted := sortByName(people)
|
// sorted := sortByName(people)
|
||||||
// // Result: [{"Jones", "Bob"}, {"Smith", "Alice"}, {"Smith", "John"}]
|
// // 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]) func(ma []T) []T {
|
||||||
return G.SortBy[[]T, []O.Ord[T]](ord)
|
return G.SortBy[[]T, []O.Ord[T]](ord)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ import (
|
|||||||
//
|
//
|
||||||
// result := parseAll([]string{"1", "2", "3"}) // Some([1, 2, 3])
|
// result := parseAll([]string{"1", "2", "3"}) // Some([1, 2, 3])
|
||||||
// result2 := parseAll([]string{"1", "x", "3"}) // None
|
// result2 := parseAll([]string{"1", "x", "3"}) // None
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Traverse[A, B, HKTB, HKTAB, HKTRB any](
|
func Traverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||||
fof func([]B) HKTRB,
|
fof func([]B) HKTRB,
|
||||||
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
||||||
@@ -66,6 +68,8 @@ func Traverse[A, B, HKTB, HKTAB, HKTRB any](
|
|||||||
// into an effect of an array.
|
// into an effect of an array.
|
||||||
//
|
//
|
||||||
// This is useful when you want to apply the traverse operation directly without currying.
|
// This is useful when you want to apply the traverse operation directly without currying.
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any](
|
func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any](
|
||||||
fof func([]B) HKTRB,
|
fof func([]B) HKTRB,
|
||||||
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import (
|
|||||||
//
|
//
|
||||||
// strings := []string{"a", "b", "a", "c", "b"}
|
// strings := []string{"a", "b", "a", "c", "b"}
|
||||||
// unique2 := array.StrictUniq(strings) // ["a", "b", "c"]
|
// unique2 := array.StrictUniq(strings) // ["a", "b", "c"]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func StrictUniq[A comparable](as []A) []A {
|
func StrictUniq[A comparable](as []A) []A {
|
||||||
return G.StrictUniq[[]A](as)
|
return G.StrictUniq[[]A](as)
|
||||||
}
|
}
|
||||||
@@ -42,6 +44,8 @@ func StrictUniq[A comparable](as []A) []A {
|
|||||||
// uniqueByName := array.Uniq(func(p Person) string { return p.Name })
|
// uniqueByName := array.Uniq(func(p Person) string { return p.Name })
|
||||||
// result := uniqueByName(people)
|
// result := uniqueByName(people)
|
||||||
// // Result: [{"Alice", 30}, {"Bob", 25}, {"Charlie", 30}]
|
// // 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) func(as []A) []A {
|
||||||
return G.Uniq[[]A](f)
|
return G.Uniq[[]A](f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import (
|
|||||||
// return fmt.Sprintf("%s is %d years old", name, age)
|
// return fmt.Sprintf("%s is %d years old", name, age)
|
||||||
// })
|
// })
|
||||||
// // Result: ["Alice is 30 years old", "Bob is 25 years old", "Charlie is 35 years old"]
|
// // Result: ["Alice is 30 years old", "Bob is 25 years old", "Charlie is 35 years old"]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ZipWith[FCT ~func(A, B) C, A, B, C any](fa []A, fb []B, f FCT) []C {
|
func ZipWith[FCT ~func(A, B) C, A, B, C any](fa []A, fb []B, f FCT) []C {
|
||||||
return G.ZipWith[[]A, []B, []C, FCT](fa, fb, f)
|
return G.ZipWith[[]A, []B, []C, FCT](fa, fb, f)
|
||||||
}
|
}
|
||||||
@@ -51,6 +53,8 @@ func ZipWith[FCT ~func(A, B) C, A, B, C any](fa []A, fb []B, f FCT) []C {
|
|||||||
// // With different lengths
|
// // With different lengths
|
||||||
// pairs2 := array.Zip([]int{1, 2})([]string{"a", "b", "c"})
|
// pairs2 := array.Zip([]int{1, 2})([]string{"a", "b", "c"})
|
||||||
// // Result: [(a, 1), (b, 2)]
|
// // Result: [(a, 1), (b, 2)]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Zip[A, B any](fb []B) func([]A) []T.Tuple2[A, B] {
|
func Zip[A, B any](fb []B) func([]A) []T.Tuple2[A, B] {
|
||||||
return G.Zip[[]A, []B, []T.Tuple2[A, B]](fb)
|
return G.Zip[[]A, []B, []T.Tuple2[A, B]](fb)
|
||||||
}
|
}
|
||||||
@@ -72,6 +76,8 @@ func Zip[A, B any](fb []B) func([]A) []T.Tuple2[A, B] {
|
|||||||
// // Result: (["Alice", "Bob", "Charlie"], [30, 25, 35])
|
// // Result: (["Alice", "Bob", "Charlie"], [30, 25, 35])
|
||||||
// names := result.Head // ["Alice", "Bob", "Charlie"]
|
// names := result.Head // ["Alice", "Bob", "Charlie"]
|
||||||
// ages := result.Tail // [30, 25, 35]
|
// ages := result.Tail // [30, 25, 35]
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Unzip[A, B any](cs []T.Tuple2[A, B]) T.Tuple2[[]A, []B] {
|
func Unzip[A, B any](cs []T.Tuple2[A, B]) T.Tuple2[[]A, []B] {
|
||||||
return G.Unzip[[]A, []B, []T.Tuple2[A, B]](cs)
|
return G.Unzip[[]A, []B, []T.Tuple2[A, B]](cs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import (
|
|||||||
// intAdd := semigroup.MakeSemigroup(func(a, b int) int { return a + b })
|
// intAdd := semigroup.MakeSemigroup(func(a, b int) int { return a + b })
|
||||||
// eitherSemi := either.ApplySemigroup[error](intAdd)
|
// eitherSemi := either.ApplySemigroup[error](intAdd)
|
||||||
// result := eitherSemi.Concat(either.Right[error](2), either.Right[error](3)) // Right(5)
|
// result := eitherSemi.Concat(either.Right[error](2), either.Right[error](3)) // Right(5)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] {
|
func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] {
|
||||||
return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[A, E, A], s)
|
return S.ApplySemigroup(MonadMap[E, A, func(A) A], MonadAp[A, E, A], s)
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,8 @@ func ApplySemigroup[E, A any](s S.Semigroup[A]) S.Semigroup[Either[E, A]] {
|
|||||||
// intAddMonoid := monoid.MakeMonoid(0, func(a, b int) int { return a + b })
|
// intAddMonoid := monoid.MakeMonoid(0, func(a, b int) int { return a + b })
|
||||||
// eitherMon := either.ApplicativeMonoid[error](intAddMonoid)
|
// eitherMon := either.ApplicativeMonoid[error](intAddMonoid)
|
||||||
// empty := eitherMon.Empty() // Right(0)
|
// empty := eitherMon.Empty() // Right(0)
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
func ApplicativeMonoid[E, A any](m M.Monoid[A]) M.Monoid[Either[E, A]] {
|
||||||
return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[A, E, A], m)
|
return M.ApplicativeMonoid(Of[E, A], MonadMap[E, A, func(A) A], MonadAp[A, E, A], m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import (
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseArrayG[[]string, []int](parse)([]string{"1", "2", "3"})
|
// result := either.TraverseArrayG[[]string, []int](parse)([]string{"1", "2", "3"})
|
||||||
// // result is Right([]int{1, 2, 3})
|
// // result is Right([]int{1, 2, 3})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseArrayG[GA ~[]A, GB ~[]B, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
func TraverseArrayG[GA ~[]A, GB ~[]B, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
||||||
return RA.Traverse[GA](
|
return RA.Traverse[GA](
|
||||||
Of[E, GB],
|
Of[E, GB],
|
||||||
@@ -55,6 +57,8 @@ func TraverseArrayG[GA ~[]A, GB ~[]B, E, A, B any](f func(A) Either[E, B]) func(
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseArray(parse)([]string{"1", "2", "3"})
|
// result := either.TraverseArray(parse)([]string{"1", "2", "3"})
|
||||||
// // result is Right([]int{1, 2, 3})
|
// // result is Right([]int{1, 2, 3})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseArray[E, A, B any](f func(A) Either[E, B]) func([]A) Either[E, []B] {
|
func TraverseArray[E, A, B any](f func(A) Either[E, B]) func([]A) Either[E, []B] {
|
||||||
return TraverseArrayG[[]A, []B](f)
|
return TraverseArrayG[[]A, []B](f)
|
||||||
}
|
}
|
||||||
@@ -74,6 +78,8 @@ func TraverseArray[E, A, B any](f func(A) Either[E, B]) func([]A) Either[E, []B]
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseArrayWithIndexG[[]string, []string](validate)([]string{"a", "b"})
|
// result := either.TraverseArrayWithIndexG[[]string, []string](validate)([]string{"a", "b"})
|
||||||
// // result is Right([]string{"0:a", "1:b"})
|
// // result is Right([]string{"0:a", "1:b"})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, E, A, B any](f func(int, A) Either[E, B]) func(GA) Either[E, GB] {
|
func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, E, A, B any](f func(int, A) Either[E, B]) func(GA) Either[E, GB] {
|
||||||
return RA.TraverseWithIndex[GA](
|
return RA.TraverseWithIndex[GA](
|
||||||
Of[E, GB],
|
Of[E, GB],
|
||||||
@@ -98,6 +104,8 @@ func TraverseArrayWithIndexG[GA ~[]A, GB ~[]B, E, A, B any](f func(int, A) Eithe
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseArrayWithIndex(validate)([]string{"a", "b"})
|
// result := either.TraverseArrayWithIndex(validate)([]string{"a", "b"})
|
||||||
// // result is Right([]string{"0:a", "1:b"})
|
// // result is Right([]string{"0:a", "1:b"})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseArrayWithIndex[E, A, B any](f func(int, A) Either[E, B]) func([]A) Either[E, []B] {
|
func TraverseArrayWithIndex[E, A, B any](f func(int, A) Either[E, B]) func([]A) Either[E, []B] {
|
||||||
return TraverseArrayWithIndexG[[]A, []B](f)
|
return TraverseArrayWithIndexG[[]A, []B](f)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import (
|
|||||||
//
|
//
|
||||||
// type State struct { x, y int }
|
// type State struct { x, y int }
|
||||||
// result := either.Do[error](State{})
|
// result := either.Do[error](State{})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Do[E, S any](
|
func Do[E, S any](
|
||||||
empty S,
|
empty S,
|
||||||
) Either[E, S] {
|
) Either[E, S] {
|
||||||
@@ -51,6 +53,8 @@ func Do[E, S any](
|
|||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Bind[E, S1, S2, T any](
|
func Bind[E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
f func(S1) Either[E, T],
|
f func(S1) Either[E, T],
|
||||||
@@ -78,6 +82,8 @@ func Bind[E, S1, S2, T any](
|
|||||||
// func(s State) int { return 32 },
|
// func(s State) int { return 32 },
|
||||||
// ),
|
// ),
|
||||||
// ) // Right(State{value: 42})
|
// ) // Right(State{value: 42})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func Let[E, S1, S2, T any](
|
func Let[E, S1, S2, T any](
|
||||||
key func(T) func(S1) S2,
|
key func(T) func(S1) S2,
|
||||||
f func(S1) T,
|
f func(S1) T,
|
||||||
@@ -103,6 +109,8 @@ func Let[E, S1, S2, T any](
|
|||||||
// "Alice",
|
// "Alice",
|
||||||
// ),
|
// ),
|
||||||
// ) // Right(State{name: "Alice"})
|
// ) // Right(State{name: "Alice"})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func LetTo[E, S1, S2, T any](
|
func LetTo[E, S1, S2, T any](
|
||||||
key func(T) func(S1) S2,
|
key func(T) func(S1) S2,
|
||||||
b T,
|
b T,
|
||||||
@@ -124,6 +132,8 @@ func LetTo[E, S1, S2, T any](
|
|||||||
// either.Right[error](42),
|
// either.Right[error](42),
|
||||||
// either.BindTo(func(v int) State { return State{value: v} }),
|
// either.BindTo(func(v int) State { return State{value: v} }),
|
||||||
// ) // Right(State{value: 42})
|
// ) // Right(State{value: 42})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func BindTo[E, S1, T any](
|
func BindTo[E, S1, T any](
|
||||||
setter func(T) S1,
|
setter func(T) S1,
|
||||||
) func(Either[E, T]) Either[E, S1] {
|
) func(Either[E, T]) Either[E, S1] {
|
||||||
@@ -148,6 +158,8 @@ func BindTo[E, S1, T any](
|
|||||||
// either.Right[error](32),
|
// either.Right[error](32),
|
||||||
// ),
|
// ),
|
||||||
// ) // Right(State{x: 10, y: 32})
|
// ) // Right(State{x: 10, y: 32})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func ApS[E, S1, S2, T any](
|
func ApS[E, S1, S2, T any](
|
||||||
setter func(T) func(S1) S2,
|
setter func(T) func(S1) S2,
|
||||||
fa Either[E, T],
|
fa Either[E, T],
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import (
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseRecordG[map[string]string, map[string]int](parse)(map[string]string{"a": "1", "b": "2"})
|
// result := either.TraverseRecordG[map[string]string, map[string]int](parse)(map[string]string{"a": "1", "b": "2"})
|
||||||
// // result is Right(map[string]int{"a": 1, "b": 2})
|
// // result is Right(map[string]int{"a": 1, "b": 2})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(A) Either[E, B]) func(GA) Either[E, GB] {
|
||||||
return RR.Traverse[GA](
|
return RR.Traverse[GA](
|
||||||
Of[E, GB],
|
Of[E, GB],
|
||||||
@@ -54,6 +56,8 @@ func TraverseRecordG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseRecord[string](parse)(map[string]string{"a": "1", "b": "2"})
|
// result := either.TraverseRecord[string](parse)(map[string]string{"a": "1", "b": "2"})
|
||||||
// // result is Right(map[string]int{"a": 1, "b": 2})
|
// // result is Right(map[string]int{"a": 1, "b": 2})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[K]A) Either[E, map[K]B] {
|
func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[K]A) Either[E, map[K]B] {
|
||||||
return TraverseRecordG[map[K]A, map[K]B](f)
|
return TraverseRecordG[map[K]A, map[K]B](f)
|
||||||
}
|
}
|
||||||
@@ -73,6 +77,8 @@ func TraverseRecord[K comparable, E, A, B any](f func(A) Either[E, B]) func(map[
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseRecordWithIndexG[map[string]string, map[string]string](validate)(map[string]string{"a": "1"})
|
// result := either.TraverseRecordWithIndexG[map[string]string, map[string]string](validate)(map[string]string{"a": "1"})
|
||||||
// // result is Right(map[string]string{"a": "a:1"})
|
// // result is Right(map[string]string{"a": "a:1"})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(K, A) Either[E, B]) func(GA) Either[E, GB] {
|
func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B any](f func(K, A) Either[E, B]) func(GA) Either[E, GB] {
|
||||||
return RR.TraverseWithIndex[GA](
|
return RR.TraverseWithIndex[GA](
|
||||||
Of[E, GB],
|
Of[E, GB],
|
||||||
@@ -96,10 +102,13 @@ func TraverseRecordWithIndexG[GA ~map[K]A, GB ~map[K]B, K comparable, E, A, B an
|
|||||||
// }
|
// }
|
||||||
// result := either.TraverseRecordWithIndex[string](validate)(map[string]string{"a": "1"})
|
// result := either.TraverseRecordWithIndex[string](validate)(map[string]string{"a": "1"})
|
||||||
// // result is Right(map[string]string{"a": "a:1"})
|
// // result is Right(map[string]string{"a": "a:1"})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func TraverseRecordWithIndex[K comparable, E, A, B any](f func(K, A) Either[E, B]) func(map[K]A) Either[E, map[K]B] {
|
func TraverseRecordWithIndex[K comparable, E, A, B any](f func(K, A) Either[E, B]) func(map[K]A) Either[E, map[K]B] {
|
||||||
return TraverseRecordWithIndexG[map[K]A, map[K]B](f)
|
return TraverseRecordWithIndexG[map[K]A, map[K]B](f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A any](ma GOA) Either[E, GA] {
|
func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A any](ma GOA) Either[E, GA] {
|
||||||
return TraverseRecordG[GOA, GA](F.Identity[Either[E, A]])(ma)
|
return TraverseRecordG[GOA, GA](F.Identity[Either[E, A]])(ma)
|
||||||
}
|
}
|
||||||
@@ -116,6 +125,8 @@ func SequenceRecordG[GA ~map[K]A, GOA ~map[K]Either[E, A], K comparable, E, A an
|
|||||||
// }
|
// }
|
||||||
// result := either.SequenceRecord(eithers)
|
// result := either.SequenceRecord(eithers)
|
||||||
// // result is Right(map[string]int{"a": 1, "b": 2})
|
// // result is Right(map[string]int{"a": 1, "b": 2})
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] {
|
func SequenceRecord[K comparable, E, A any](ma map[K]Either[E, A]) Either[E, map[K]A] {
|
||||||
return SequenceRecordG[map[K]A](ma)
|
return SequenceRecordG[map[K]A](ma)
|
||||||
}
|
}
|
||||||
@@ -158,6 +169,8 @@ func CompactRecordG[M1 ~map[K]Either[E, A], M2 ~map[K]A, K comparable, E, A any]
|
|||||||
// }
|
// }
|
||||||
// result := either.CompactRecord(eithers)
|
// result := either.CompactRecord(eithers)
|
||||||
// // result is map[string]int{"a": 1, "c": 3}
|
// // result is map[string]int{"a": 1, "c": 3}
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func CompactRecord[K comparable, E, A any](m map[K]Either[E, A]) map[K]A {
|
func CompactRecord[K comparable, E, A any](m map[K]Either[E, A]) map[K]A {
|
||||||
return CompactRecordG[map[K]Either[E, A], map[K]A](m)
|
return CompactRecordG[map[K]Either[E, A], map[K]A](m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import (
|
|||||||
// // result is Right(42)
|
// // result is Right(42)
|
||||||
// result2 := sg.Concat(either.Right[error](1), either.Right[error](2))
|
// result2 := sg.Concat(either.Right[error](1), either.Right[error](2))
|
||||||
// // result2 is Right(1) - first Right wins
|
// // result2 is Right(1) - first Right wins
|
||||||
|
//
|
||||||
|
//go:inline
|
||||||
func AltSemigroup[E, A any]() S.Semigroup[Either[E, A]] {
|
func AltSemigroup[E, A any]() S.Semigroup[Either[E, A]] {
|
||||||
return S.AltSemigroup(
|
return S.AltSemigroup(
|
||||||
MonadAlt[E, A],
|
MonadAlt[E, A],
|
||||||
|
|||||||
@@ -17,10 +17,52 @@ package array
|
|||||||
|
|
||||||
func Slice[GA ~[]A, A any](low, high int) func(as GA) GA {
|
func Slice[GA ~[]A, A any](low, high int) func(as GA) GA {
|
||||||
return func(as GA) GA {
|
return func(as GA) GA {
|
||||||
|
length := len(as)
|
||||||
|
|
||||||
|
// Handle negative indices - count backward from the end
|
||||||
|
if low < 0 {
|
||||||
|
low = max(length+low, 0)
|
||||||
|
}
|
||||||
|
if high < 0 {
|
||||||
|
high = max(length+high, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if low > length {
|
||||||
|
return Empty[GA, A]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// End index > array length: slice to the end
|
||||||
|
if high > length {
|
||||||
|
high = length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start >= end: return empty array
|
||||||
|
if low >= high {
|
||||||
|
return Empty[GA, A]()
|
||||||
|
}
|
||||||
|
|
||||||
return as[low:high]
|
return as[low:high]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SliceRight[GA ~[]A, A any](start int) func(as GA) GA {
|
||||||
|
return func(as GA) GA {
|
||||||
|
length := len(as)
|
||||||
|
|
||||||
|
// Handle negative indices - count backward from the end
|
||||||
|
if start < 0 {
|
||||||
|
start = max(length+start, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start index > array length: return empty array
|
||||||
|
if start > length {
|
||||||
|
return Empty[GA, A]()
|
||||||
|
}
|
||||||
|
|
||||||
|
return as[start:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func IsEmpty[GA ~[]A, A any](as GA) bool {
|
func IsEmpty[GA ~[]A, A any](as GA) bool {
|
||||||
return len(as) == 0
|
return len(as) == 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user