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

fix: traverse

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2025-11-15 12:13:37 +01:00
parent 02d0be9dad
commit ab868315d4
21 changed files with 381 additions and 140 deletions

View File

@@ -260,6 +260,8 @@ func Empty[A any]() []A {
} }
// Zero returns an empty array of type A (alias for Empty). // Zero returns an empty array of type A (alias for Empty).
//
//go:inline
func Zero[A any]() []A { func Zero[A any]() []A {
return Empty[A]() return Empty[A]()
} }

View File

@@ -25,8 +25,10 @@ import (
) )
// Of constructs a single element array // Of constructs a single element array
//
//go:inline
func Of[GA ~[]A, A any](value A) GA { func Of[GA ~[]A, A any](value A) GA {
return GA{value} return array.Of[GA](value)
} }
func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B { func Reduce[GA ~[]A, A, B any](f func(B, A) B, initial B) func(GA) B {

View File

@@ -0,0 +1,34 @@
package generic
import (
"github.com/IBM/fp-go/v2/internal/array"
M "github.com/IBM/fp-go/v2/monoid"
S "github.com/IBM/fp-go/v2/semigroup"
)
// Monoid returns a Monoid instance for arrays.
// The Monoid combines arrays through concatenation, with an empty array as the identity element.
//
// Example:
//
// m := array.Monoid[int]()
// result := m.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
// empty := m.Empty() // []
//
//go:inline
func Monoid[GT ~[]T, T any]() M.Monoid[GT] {
return M.MakeMonoid(array.Concat[GT], Empty[GT]())
}
// Semigroup returns a Semigroup instance for arrays.
// The Semigroup combines arrays through concatenation.
//
// Example:
//
// s := array.Semigroup[int]()
// result := s.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
//
//go:inline
func Semigroup[GT ~[]T, T any]() S.Semigroup[GT] {
return S.MakeSemigroup(array.Concat[GT])
}

View File

@@ -18,7 +18,6 @@ package array
import ( import (
"testing" "testing"
O "github.com/IBM/fp-go/v2/option"
OR "github.com/IBM/fp-go/v2/ord" OR "github.com/IBM/fp-go/v2/ord"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -103,39 +102,6 @@ func TestSortByKey(t *testing.T) {
assert.Equal(t, "Charlie", result[2].Name) assert.Equal(t, "Charlie", result[2].Name)
} }
func TestMonadTraverse(t *testing.T) {
result := MonadTraverse(
O.Of[[]int],
O.Map[[]int, func(int) []int],
O.Ap[[]int, int],
[]int{1, 3, 5},
func(n int) O.Option[int] {
if n%2 == 1 {
return O.Some(n * 2)
}
return O.None[int]()
},
)
assert.Equal(t, O.Some([]int{2, 6, 10}), result)
// Test with None case
result2 := MonadTraverse(
O.Of[[]int],
O.Map[[]int, func(int) []int],
O.Ap[[]int, int],
[]int{1, 2, 3},
func(n int) O.Option[int] {
if n%2 == 1 {
return O.Some(n * 2)
}
return O.None[int]()
},
)
assert.Equal(t, O.None[[]int](), result2)
}
func TestUniqByKey(t *testing.T) { func TestUniqByKey(t *testing.T) {
type Person struct { type Person struct {
Name string Name string

View File

@@ -16,27 +16,12 @@
package array package array
import ( import (
G "github.com/IBM/fp-go/v2/array/generic"
"github.com/IBM/fp-go/v2/internal/array" "github.com/IBM/fp-go/v2/internal/array"
M "github.com/IBM/fp-go/v2/monoid" M "github.com/IBM/fp-go/v2/monoid"
S "github.com/IBM/fp-go/v2/semigroup" S "github.com/IBM/fp-go/v2/semigroup"
) )
func concat[T any](left, right []T) []T {
// some performance checks
ll := len(left)
if ll == 0 {
return right
}
lr := len(right)
if lr == 0 {
return left
}
// need to copy
buf := make([]T, ll+lr)
copy(buf[copy(buf, left):], right)
return buf
}
// Monoid returns a Monoid instance for arrays. // Monoid returns a Monoid instance for arrays.
// The Monoid combines arrays through concatenation, with an empty array as the identity element. // The Monoid combines arrays through concatenation, with an empty array as the identity element.
// //
@@ -45,8 +30,10 @@ func concat[T any](left, right []T) []T {
// m := array.Monoid[int]() // m := array.Monoid[int]()
// result := m.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4] // result := m.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
// empty := m.Empty() // [] // empty := m.Empty() // []
//
//go:inline
func Monoid[T any]() M.Monoid[[]T] { func Monoid[T any]() M.Monoid[[]T] {
return M.MakeMonoid(concat[T], Empty[T]()) return G.Monoid[[]T]()
} }
// Semigroup returns a Semigroup instance for arrays. // Semigroup returns a Semigroup instance for arrays.
@@ -56,8 +43,10 @@ func Monoid[T any]() M.Monoid[[]T] {
// //
// s := array.Semigroup[int]() // s := array.Semigroup[int]()
// result := s.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4] // result := s.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
//
//go:inline
func Semigroup[T any]() S.Semigroup[[]T] { func Semigroup[T any]() S.Semigroup[[]T] {
return S.MakeSemigroup(concat[T]) return G.Semigroup[[]T]()
} }
func addLen[A any](count int, data []A) int { func addLen[A any](count int, data []A) int {

View File

@@ -16,10 +16,18 @@
package array package array
import ( import (
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" O "github.com/IBM/fp-go/v2/option"
) )
func MonadSequence[HKTA, HKTRA any](
fof func(HKTA) HKTRA,
m M.Monoid[HKTRA],
ma []HKTA) HKTRA {
return array.MonadSequence(fof, m.Empty(), m.Concat, ma)
}
// Sequence takes an array where elements are HKT<A> (higher kinded type) and, // Sequence takes an array where elements are HKT<A> (higher kinded type) and,
// using an applicative of that HKT, returns an HKT of []A. // using an applicative of that HKT, returns an HKT of []A.
// //
@@ -55,16 +63,11 @@ import (
// option.MonadAp[[]int, int], // option.MonadAp[[]int, int],
// ) // )
// result := seq(opts) // Some([1, 2, 3]) // result := seq(opts) // Some([1, 2, 3])
func Sequence[A, HKTA, HKTRA, HKTFRA any]( func Sequence[HKTA, HKTRA any](
_of func([]A) HKTRA, fof func(HKTA) HKTRA,
_map func(HKTRA, func([]A) func(A) []A) HKTFRA, m M.Monoid[HKTRA],
_ap func(HKTFRA, HKTA) HKTRA,
) func([]HKTA) HKTRA { ) func([]HKTA) HKTRA {
ca := F.Curry2(Append[A]) return array.Sequence[[]HKTA](fof, m.Empty(), m.Concat)
empty := _of(Empty[A]())
return Reduce(func(fas HKTRA, fa HKTA) HKTRA {
return _ap(_map(fas, ca), fa)
}, empty)
} }
// ArrayOption returns a function to convert a sequence of options into an option of a sequence. // ArrayOption returns a function to convert a sequence of options into an option of a sequence.
@@ -86,10 +89,10 @@ func Sequence[A, HKTA, HKTRA, HKTFRA any](
// option.Some(3), // option.Some(3),
// } // }
// result2 := array.ArrayOption[int]()(opts2) // None // result2 := array.ArrayOption[int]()(opts2) // None
func ArrayOption[A any]() func([]Option[A]) Option[[]A] { func ArrayOption[A any](ma []Option[A]) Option[[]A] {
return Sequence( return MonadSequence(
O.Of[[]A], O.Map(Of[A]),
O.MonadMap[[]A, func(A) []A], O.ApplicativeMonoid(Monoid[A]()),
O.MonadAp[[]A, A], ma,
) )
} }

View File

@@ -24,8 +24,7 @@ import (
) )
func TestSequenceOption(t *testing.T) { func TestSequenceOption(t *testing.T) {
seq := ArrayOption[int]()
assert.Equal(t, O.Of([]int{1, 3}), seq([]O.Option[int]{O.Of(1), O.Of(3)})) assert.Equal(t, O.Of([]int{1, 3}), ArrayOption([]O.Option[int]{O.Of(1), O.Of(3)}))
assert.Equal(t, O.None[[]int](), seq([]O.Option[int]{O.Of(1), O.None[int]()})) assert.Equal(t, O.None[[]int](), ArrayOption([]O.Option[int]{O.Of(1), O.None[int]()}))
} }

View File

@@ -80,3 +80,25 @@ func MonadTraverse[A, B, HKTB, HKTAB, HKTRB any](
return array.MonadTraverse(fof, fmap, fap, ta, f) return array.MonadTraverse(fof, fmap, fap, ta, f)
} }
//go:inline
func TraverseWithIndex[A, B, HKTB, HKTAB, HKTRB any](
fof func([]B) HKTRB,
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
f func(int, A) HKTB) func([]A) HKTRB {
return array.TraverseWithIndex[[]A](fof, fmap, fap, f)
}
//go:inline
func MonadTraverseWithIndex[A, B, HKTB, HKTAB, HKTRB any](
fof func([]B) HKTRB,
fmap func(func([]B) func(B) []B) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
ta []A,
f func(int, A) HKTB) HKTRB {
return array.MonadTraverseWithIndex(fof, fmap, fap, ta, f)
}

View File

@@ -312,7 +312,7 @@ func TestMonadChainFirstLeft(t *testing.T) {
Left[int](originalErr), Left[int](originalErr),
func(e error) ReaderIOResult[int] { func(e error) ReaderIOResult[int] {
capturedError = e capturedError = e
return Right[int](999) // This Right value is ignored return Right(999) // This Right value is ignored
}, },
) )
actualResult := result(ctx)() actualResult := result(ctx)()
@@ -324,7 +324,7 @@ func TestMonadChainFirstLeft(t *testing.T) {
t.Run("Right value passes through", func(t *testing.T) { t.Run("Right value passes through", func(t *testing.T) {
sideEffectCalled := false sideEffectCalled := false
result := MonadChainFirstLeft( result := MonadChainFirstLeft(
Right[int](42), Right(42),
func(e error) ReaderIOResult[int] { func(e error) ReaderIOResult[int] {
sideEffectCalled = true sideEffectCalled = true
return Left[int](fmt.Errorf("should not be called")) return Left[int](fmt.Errorf("should not be called"))
@@ -343,7 +343,7 @@ func TestMonadChainFirstLeft(t *testing.T) {
func(e error) ReaderIOResult[int] { func(e error) ReaderIOResult[int] {
effectCount++ effectCount++
// Try to return Right, but original Left should still be returned // Try to return Right, but original Left should still be returned
return Right[int](999) return Right(999)
}, },
) )
actualResult := result(ctx)() actualResult := result(ctx)()
@@ -378,7 +378,7 @@ func TestChainFirstLeft(t *testing.T) {
originalErr := fmt.Errorf("test error") originalErr := fmt.Errorf("test error")
chainFn := ChainFirstLeft[int](func(e error) ReaderIOResult[int] { chainFn := ChainFirstLeft[int](func(e error) ReaderIOResult[int] {
captured = e captured = e
return Right[int](42) // This Right is ignored return Right(42) // This Right is ignored
}) })
result := F.Pipe1( result := F.Pipe1(
Left[int](originalErr), Left[int](originalErr),
@@ -394,10 +394,10 @@ func TestChainFirstLeft(t *testing.T) {
called := false called := false
chainFn := ChainFirstLeft[int](func(e error) ReaderIOResult[int] { chainFn := ChainFirstLeft[int](func(e error) ReaderIOResult[int] {
called = true called = true
return Right[int](0) return Right(0)
}) })
result := F.Pipe1( result := F.Pipe1(
Right[int](100), Right(100),
chainFn, chainFn,
) )
assert.False(t, called) assert.False(t, called)
@@ -409,7 +409,7 @@ func TestChainFirstLeft(t *testing.T) {
originalErr := fmt.Errorf("original") originalErr := fmt.Errorf("original")
chainFn := ChainFirstLeft[int](func(e error) ReaderIOResult[int] { chainFn := ChainFirstLeft[int](func(e error) ReaderIOResult[int] {
// Try to return Right, but original Left should still be returned // Try to return Right, but original Left should still be returned
return Right[int](999) return Right(999)
}) })
result := F.Pipe1( result := F.Pipe1(

View File

@@ -16,8 +16,8 @@
package readerioresult package readerioresult
import ( import (
"github.com/IBM/fp-go/v2/array"
"github.com/IBM/fp-go/v2/function" "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/array"
"github.com/IBM/fp-go/v2/internal/record" "github.com/IBM/fp-go/v2/internal/record"
) )
@@ -29,7 +29,7 @@ import (
// //
// Returns a function that transforms an array into a ReaderIOResult of an array. // Returns a function that transforms an array into a ReaderIOResult of an array.
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] { func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
return array.Traverse[[]A]( return array.Traverse(
Of[[]B], Of[[]B],
Map[[]B, func(B) []B], Map[[]B, func(B) []B],
Ap[[]B, B], Ap[[]B, B],
@@ -46,7 +46,7 @@ func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
// //
// Returns a function that transforms an array into a ReaderIOResult of an array. // Returns a function that transforms an array into a ReaderIOResult of an array.
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] { func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
return array.TraverseWithIndex[[]A]( return array.TraverseWithIndex(
Of[[]B], Of[[]B],
Map[[]B, func(B) []B], Map[[]B, func(B) []B],
Ap[[]B, B], Ap[[]B, B],
@@ -135,22 +135,20 @@ func MonadTraverseArraySeq[A, B any](as []A, f Kleisli[A, B]) ReaderIOResult[[]B
// //
// Returns a function that transforms an array into a ReaderIOResult of an array. // Returns a function that transforms an array into a ReaderIOResult of an array.
func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] { func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
return array.Traverse[[]A]( return array.Traverse(
Of[[]B], Of[[]B],
Map[[]B, func(B) []B], Map[[]B, func(B) []B],
ApSeq[[]B, B], ApSeq[[]B, B],
f, f,
) )
} }
// TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]] // TraverseArrayWithIndexSeq uses transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]]
func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] { func TraverseArrayWithIndexSeq[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
return array.TraverseWithIndex[[]A]( return array.TraverseWithIndex(
Of[[]B], Of[[]B],
Map[[]B, func(B) []B], Map[[]B, func(B) []B],
ApSeq[[]B, B], ApSeq[[]B, B],
f, f,
) )
} }
@@ -230,22 +228,20 @@ func MonadTraverseArrayPar[A, B any](as []A, f Kleisli[A, B]) ReaderIOResult[[]B
// //
// Returns a function that transforms an array into a ReaderIOResult of an array. // Returns a function that transforms an array into a ReaderIOResult of an array.
func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] { func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
return array.Traverse[[]A]( return array.Traverse(
Of[[]B], Of[[]B],
Map[[]B, func(B) []B], Map[[]B, func(B) []B],
ApPar[[]B, B], ApPar[[]B, B],
f, f,
) )
} }
// TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]] // TraverseArrayWithIndexPar uses transforms an array [[]A] into [[]ReaderIOResult[B]] and then resolves that into a [ReaderIOResult[[]B]]
func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] { func TraverseArrayWithIndexPar[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
return array.TraverseWithIndex[[]A]( return array.TraverseWithIndex(
Of[[]B], Of[[]B],
Map[[]B, func(B) []B], Map[[]B, func(B) []B],
ApPar[[]B, B], ApPar[[]B, B],
f, f,
) )
} }

View File

@@ -15,6 +15,10 @@
package array package array
func Of[GA ~[]A, A any](a A) GA {
return GA{a}
}
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) length := len(as)
@@ -140,7 +144,7 @@ func UpsertAt[GA ~[]A, A any](a A) func(GA) GA {
func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB { func MonadMap[GA ~[]A, GB ~[]B, A, B any](as GA, f func(a A) B) GB {
count := len(as) count := len(as)
bs := make(GB, count) bs := make(GB, count)
for i := count - 1; i >= 0; i-- { for i := range count {
bs[i] = f(as[i]) bs[i] = f(as[i])
} }
return bs return bs
@@ -155,7 +159,7 @@ func Map[GA ~[]A, GB ~[]B, A, B any](f func(a A) B) func(GA) GB {
func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB { func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B) GB {
count := len(as) count := len(as)
bs := make(GB, count) bs := make(GB, count)
for i := count - 1; i >= 0; i-- { for i := range count {
bs[i] = f(i, as[i]) bs[i] = f(i, as[i])
} }
return bs return bs
@@ -164,3 +168,19 @@ func MonadMapWithIndex[GA ~[]A, GB ~[]B, A, B any](as GA, f func(idx int, a A) B
func ConstNil[GA ~[]A, A any]() GA { func ConstNil[GA ~[]A, A any]() GA {
return (GA)(nil) return (GA)(nil)
} }
func Concat[GT ~[]T, T any](left, right GT) GT {
// some performance checks
ll := len(left)
if ll == 0 {
return right
}
lr := len(right)
if lr == 0 {
return left
}
// need to copy
buf := make(GT, ll+lr)
copy(buf[copy(buf, left):], right)
return buf
}

View File

@@ -19,6 +19,72 @@ import (
F "github.com/IBM/fp-go/v2/function" F "github.com/IBM/fp-go/v2/function"
) )
func MonadSequenceSegment[HKTB, HKTRB any](
fof func(HKTB) HKTRB,
empty HKTRB,
concat func(HKTRB, HKTRB) HKTRB,
fbs []HKTB,
start, end int,
) HKTRB {
switch end - start {
case 0:
return empty
case 1:
return fof(fbs[start])
default:
mid := (start + end) / 2
return concat(
MonadSequenceSegment(fof, empty, concat, fbs, start, mid),
MonadSequenceSegment(fof, empty, concat, fbs, mid, end),
)
}
}
func SequenceSegment[HKTB, HKTRB any](
fof func(HKTB) HKTRB,
empty HKTRB,
concat func(HKTRB, HKTRB) HKTRB,
) func([]HKTB) HKTRB {
concat_f := func(left, right func([]HKTB) HKTRB) func([]HKTB) HKTRB {
return func(fbs []HKTB) HKTRB {
return concat(left(fbs), right(fbs))
}
}
empty_f := F.Constant1[[]HKTB](empty)
at := func(idx int) func([]HKTB) HKTRB {
return func(fbs []HKTB) HKTRB {
return fof(fbs[idx])
}
}
var divide func(start, end int) func([]HKTB) HKTRB
divide = func(start, end int) func([]HKTB) HKTRB {
switch end - start {
case 0:
return empty_f
case 1:
return at(start)
default:
mid := (start + end) / 2
left := divide(start, mid)
right := divide(mid, end)
return concat_f(left, right)
}
}
// TODO this could be cached by length
get_divide := func(len int) func([]HKTB) HKTRB {
return divide(0, len)
}
return func(fbs []HKTB) HKTRB {
return get_divide(len(fbs))(fbs)
}
}
/* /*
* *
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
@@ -79,6 +145,34 @@ func TraverseWithIndex[GA ~[]A, GB ~[]B, A, B, HKTB, HKTAB, HKTRB any](
} }
} }
/*
*
We need to pass the members of the applicative explicitly, because golang does neither support higher kinded types nor template methods on structs or interfaces
HKTRB = HKT<GB>
HKTB = HKT<B>
HKTAB = HKT<func(A)B>
*/
func MonadSequence[GA ~[]HKTA, HKTA, HKTRA any](
fof func(HKTA) HKTRA,
empty HKTRA,
concat func(HKTRA, HKTRA) HKTRA,
ta GA) HKTRA {
return MonadSequenceSegment(fof, empty, concat, ta, 0, len(ta))
}
func Sequence[GA ~[]HKTA, HKTA, HKTRA any](
fof func(HKTA) HKTRA,
empty HKTRA,
concat func(HKTRA, HKTRA) HKTRA,
) func(GA) HKTRA {
return func(ma GA) HKTRA {
return MonadSequence(fof, empty, concat, ma)
}
}
func MonadTraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any]( func MonadTraverseReduce[GA ~[]A, GB, A, B, HKTB, HKTAB, HKTRB any](
fof func(GB) HKTRB, fof func(GB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,

View File

@@ -2,6 +2,7 @@ package iter
import ( import (
F "github.com/IBM/fp-go/v2/function" F "github.com/IBM/fp-go/v2/function"
M "github.com/IBM/fp-go/v2/monoid"
) )
func MonadReduceWithIndex[GA ~func(yield func(A) bool), A, B any](fa GA, f func(int, B, A) B, initial B) B { func MonadReduceWithIndex[GA ~func(yield func(A) bool), A, B any](fa GA, f func(int, B, A) B, initial B) B {
@@ -59,3 +60,41 @@ func Prepend[GA ~func(yield func(A) bool), A any](head A) func(GA) GA {
func Empty[GA ~func(yield func(A) bool), A any]() GA { func Empty[GA ~func(yield func(A) bool), A any]() GA {
return func(_ func(A) bool) {} return func(_ func(A) bool) {}
} }
func ToArray[GA ~func(yield func(A) bool), GB ~[]A, A any](fa GA) GB {
bs := make(GB, 0)
for a := range fa {
bs = append(bs, a)
}
return bs
}
func MonadMapToArray[GA ~func(yield func(A) bool), GB ~[]B, A, B any](fa GA, f func(A) B) GB {
bs := make(GB, 0)
for a := range fa {
bs = append(bs, f(a))
}
return bs
}
func MapToArray[GA ~func(yield func(A) bool), GB ~[]B, A, B any](f func(A) B) func(GA) GB {
return F.Bind2nd(MonadMapToArray[GA, GB], f)
}
func MonadMapToArrayWithIndex[GA ~func(yield func(A) bool), GB ~[]B, A, B any](fa GA, f func(int, A) B) GB {
bs := make(GB, 0)
var i int
for a := range fa {
bs = append(bs, f(i, a))
i += 1
}
return bs
}
func MapToArrayWithIndex[GA ~func(yield func(A) bool), GB ~[]B, A, B any](f func(int, A) B) func(GA) GB {
return F.Bind2nd(MonadMapToArrayWithIndex[GA, GB], f)
}
func Monoid[GA ~func(yield func(A) bool), A any]() M.Monoid[GA] {
return M.MakeMonoid(Concat[GA], Empty[GA]())
}

View File

@@ -17,6 +17,8 @@ package iter
import ( import (
F "github.com/IBM/fp-go/v2/function" F "github.com/IBM/fp-go/v2/function"
INTA "github.com/IBM/fp-go/v2/internal/array"
M "github.com/IBM/fp-go/v2/monoid"
) )
/* /*
@@ -27,14 +29,65 @@ HKTRB = HKT<GB>
HKTB = HKT<B> HKTB = HKT<B>
HKTAB = HKT<func(A)B> HKTAB = HKT<func(A)B>
*/ */
func MonadTraverse[GA ~func(yield func(A) bool), GB ~func(yield func(B) bool), A, B, HKTB, HKTAB, HKTRB any]( func MonadTraverse[GA ~func(yield func(A) bool), GB ~func(yield func(B) bool), A, B, HKT_B, HKT_GB_GB, HKT_GB any](
fof func(GB) HKTRB, fmap_b func(HKT_B, func(B) GB) HKT_GB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB, fof_gb func(GB) HKT_GB,
fmap_gb func(HKT_GB, func(GB) func(GB) GB) HKT_GB_GB,
fap_gb func(HKT_GB_GB, HKT_GB) HKT_GB,
ta GA, ta GA,
f func(A) HKTB) HKTRB { f func(A) HKT_B) HKT_GB {
return MonadTraverseReduce(fof, fmap, fap, ta, f, MonadAppend[GB, B], Empty[GB]())
fof := F.Bind2nd(fmap_b, Of[GB])
empty := fof_gb(Empty[GB]())
cb := F.Curry2(Concat[GB])
concat_gb := F.Bind2nd(fmap_gb, cb)
concat := func(first HKT_GB, second HKT_GB) HKT_GB {
return fap_gb(concat_gb(first), second)
}
// convert to an array
hktb := MonadMapToArray[GA, []HKT_B](ta, f)
return INTA.MonadSequenceSegment(fof, empty, concat, hktb, 0, len(hktb))
}
func Traverse[GA ~func(yield func(A) bool), GB ~func(yield func(B) bool), A, B, HKT_B, HKT_GB_GB, HKT_GB any](
fmap_b func(func(B) GB) func(HKT_B) HKT_GB,
fof_gb func(GB) HKT_GB,
fmap_gb func(func(GB) func(GB) GB) func(HKT_GB) HKT_GB_GB,
fap_gb func(HKT_GB_GB, HKT_GB) HKT_GB,
f func(A) HKT_B) func(GA) HKT_GB {
fof := fmap_b(Of[GB])
empty := fof_gb(Empty[GB]())
cb := F.Curry2(Concat[GB])
concat_gb := fmap_gb(cb)
concat := func(first, second HKT_GB) HKT_GB {
return fap_gb(concat_gb(first), second)
}
return func(ma GA) HKT_GB {
// return INTA.SequenceSegment(fof, empty, concat)(MapToArray[GA, []HKT_B](f)(ma))
hktb := MonadMapToArray[GA, []HKT_B](ma, f)
return INTA.MonadSequenceSegment(fof, empty, concat, hktb, 0, len(hktb))
}
}
func MonadSequence[GA ~func(yield func(HKTA) bool), HKTA, HKTRA any](
fof func(HKTA) HKTRA,
m M.Monoid[HKTRA],
ta GA) HKTRA {
// convert to an array
hktb := ToArray[GA, []HKTA](ta)
return INTA.MonadSequenceSegment(fof, m.Empty(), m.Concat, hktb, 0, len(hktb))
} }
/* /*
@@ -45,37 +98,35 @@ HKTRB = HKT<GB>
HKTB = HKT<B> HKTB = HKT<B>
HKTAB = HKT<func(A)B> HKTAB = HKT<func(A)B>
*/ */
func MonadTraverseWithIndex[GA ~func(yield func(A) bool), GB ~func(yield func(B) bool), A, B, HKTB, HKTAB, HKTRB any]( func MonadTraverseWithIndex[GA ~func(yield func(A) bool), A, HKTB, HKTRB any](
fof func(GB) HKTRB, fof func(HKTB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, m M.Monoid[HKTRB],
fap func(HKTB) func(HKTAB) HKTRB,
ta GA, ta GA,
f func(int, A) HKTB) HKTRB { f func(int, A) HKTB) HKTRB {
return MonadTraverseReduceWithIndex(fof, fmap, fap, ta, f, MonadAppend[GB, B], Empty[GB]())
// convert to an array
hktb := MonadMapToArrayWithIndex[GA, []HKTB](ta, f)
return INTA.MonadSequenceSegment(fof, m.Empty(), m.Concat, hktb, 0, len(hktb))
} }
func Traverse[GA ~func(yield func(A) bool), GB ~func(yield func(B) bool), A, B, HKTB, HKTAB, HKTRB any]( func Sequence[GA ~func(yield func(HKTA) bool), HKTA, HKTRA any](
fof func(GB) HKTRB, fof func(HKTA) HKTRA,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, m M.Monoid[HKTRA]) func(GA) HKTRA {
fap func(HKTB) func(HKTAB) HKTRB,
f func(A) HKTB) func(GA) HKTRB { return func(ma GA) HKTRA {
return MonadSequence(fof, m, ma)
return func(ma GA) HKTRB {
return MonadTraverse(fof, fmap, fap, ma, f)
} }
} }
func TraverseWithIndex[GA ~func(yield func(A) bool), GB ~func(yield func(B) bool), A, B, HKTB, HKTAB, HKTRB any]( func TraverseWithIndex[GA ~func(yield func(A) bool), A, HKTB, HKTRB any](
fof func(GB) HKTRB, fof func(HKTB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB, m M.Monoid[HKTRB],
fap func(HKTB) func(HKTAB) HKTRB,
f func(int, A) HKTB) func(GA) HKTRB { f func(int, A) HKTB) func(GA) HKTRB {
return func(ma GA) HKTRB { return func(ma GA) HKTRB {
return MonadTraverseWithIndex(fof, fmap, fap, ma, f) return MonadTraverseWithIndex(fof, m, ma, f)
} }
} }

View File

@@ -63,9 +63,11 @@ func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
func TraverseIter[A, B any](f Kleisli[A, B]) Kleisli[Seq[A], Seq[B]] { func TraverseIter[A, B any](f Kleisli[A, B]) Kleisli[Seq[A], Seq[B]] {
return INTI.Traverse[Seq[A]]( return INTI.Traverse[Seq[A]](
Map[B],
Of[Seq[B]], Of[Seq[B]],
Map[Seq[B], func(B) Seq[B]], Map[Seq[B]],
Ap[Seq[B], B], MonadAp[Seq[B]],
f, f,
) )

View File

@@ -885,3 +885,13 @@ func MonadZip[A, B any](fb Seq[B], fa Seq[A]) Seq2[A, B] {
func Zip[A, B any](fa Seq[A]) func(Seq[B]) Seq2[A, B] { func Zip[A, B any](fa Seq[A]) func(Seq[B]) Seq2[A, B] {
return F.Bind2nd(MonadZip[A, B], fa) return F.Bind2nd(MonadZip[A, B], fa)
} }
//go:inline
func MonadMapToArray[A, B any](fa Seq[A], f func(A) B) []B {
return G.MonadMapToArray[Seq[A], []B](fa, f)
}
//go:inline
func MapToArray[A, B any](f func(A) B) func(Seq[A]) []B {
return G.MapToArray[Seq[A], []B](f)
}

View File

@@ -33,5 +33,5 @@ import (
// //
//go:inline //go:inline
func Monoid[T any]() M.Monoid[Seq[T]] { func Monoid[T any]() M.Monoid[Seq[T]] {
return M.MakeMonoid(G.Concat[Seq[T]], Empty[T]()) return G.Monoid[Seq[T]]()
} }

View File

@@ -42,6 +42,8 @@ func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Option[A]] {
// optMonoid := ApplicativeMonoid(intMonoid) // optMonoid := ApplicativeMonoid(intMonoid)
// result := optMonoid.Concat(Some(2), Some(3)) // Some(5) // result := optMonoid.Concat(Some(2), Some(3)) // Some(5)
// result := optMonoid.Empty() // Some(0) // result := optMonoid.Empty() // Some(0)
//
//go:inline
func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Option[A]] { func ApplicativeMonoid[A any](m M.Monoid[A]) M.Monoid[Option[A]] {
return M.ApplicativeMonoid(Of[A], MonadMap[A, func(A) A], MonadAp[A, A], m) return M.ApplicativeMonoid(Of[A], MonadMap[A, func(A) A], MonadAp[A, A], m)
} }

View File

@@ -55,11 +55,22 @@ import (
// result := TraverseIter(parse)(invalidStrings) // result := TraverseIter(parse)(invalidStrings)
// // result is None because "invalid" cannot be parsed // // result is None because "invalid" cannot be parsed
func TraverseIter[A, B any](f Kleisli[A, B]) Kleisli[Seq[A], Seq[B]] { func TraverseIter[A, B any](f Kleisli[A, B]) Kleisli[Seq[A], Seq[B]] {
return INTI.Traverse[Seq[A]]( return INTI.Traverse[Seq[A]](
Map[B],
Of[Seq[B]], Of[Seq[B]],
Map[Seq[B], func(B) Seq[B]], Map[Seq[B]],
Ap[Seq[B]], MonadAp[Seq[B]],
f, f,
) )
} }
func SequenceIter[A any](as Seq[Option[A]]) Option[Seq[A]] {
return INTI.MonadSequence(
Map(INTI.Of[Seq[A]]),
ApplicativeMonoid(INTI.Monoid[Seq[A]]()),
as,
)
}

View File

@@ -17,7 +17,7 @@ package reader
import ( import (
"github.com/IBM/fp-go/v2/function" "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/array" G "github.com/IBM/fp-go/v2/reader/generic"
) )
// MonadTraverseArray transforms each element of an array using a function that returns a Reader, // MonadTraverseArray transforms each element of an array using a function that returns a Reader,
@@ -38,13 +38,7 @@ import (
// r := reader.MonadTraverseArray(numbers, addPrefix) // r := reader.MonadTraverseArray(numbers, addPrefix)
// result := r(Config{Prefix: "num"}) // ["num1", "num2", "num3"] // result := r(Config{Prefix: "num"}) // ["num1", "num2", "num3"]
func MonadTraverseArray[R, A, B any](ma []A, f Kleisli[R, A, B]) Reader[R, []B] { func MonadTraverseArray[R, A, B any](ma []A, f Kleisli[R, A, B]) Reader[R, []B] {
return array.MonadTraverse( return G.MonadTraverseArray[Reader[R, B], Reader[R, []B], []A](ma, f)
Of[R, []B],
Map[R, []B, func(B) []B],
Ap[[]B, R, B],
ma,
f,
)
} }
// TraverseArray transforms each element of an array using a function that returns a Reader, // TraverseArray transforms each element of an array using a function that returns a Reader,
@@ -63,12 +57,7 @@ func MonadTraverseArray[R, A, B any](ma []A, f Kleisli[R, A, B]) Reader[R, []B]
// r := transform([]int{1, 2, 3}) // r := transform([]int{1, 2, 3})
// result := r(Config{Multiplier: 10}) // [10, 20, 30] // result := r(Config{Multiplier: 10}) // [10, 20, 30]
func TraverseArray[R, A, B any](f Kleisli[R, A, B]) func([]A) Reader[R, []B] { func TraverseArray[R, A, B any](f Kleisli[R, A, B]) func([]A) Reader[R, []B] {
return array.Traverse[[]A]( return G.TraverseArray[Reader[R, B], Reader[R, []B], []A](f)
Of[R, []B],
Map[R, []B, func(B) []B],
Ap[[]B, R, B],
f,
)
} }
// TraverseArrayWithIndex transforms each element of an array using a function that takes // TraverseArrayWithIndex transforms each element of an array using a function that takes
@@ -89,12 +78,7 @@ func TraverseArray[R, A, B any](f Kleisli[R, A, B]) func([]A) Reader[R, []B] {
// r := transform([]string{"a", "b", "c"}) // r := transform([]string{"a", "b", "c"})
// result := r(Config{Prefix: "item"}) // ["item[0]:a", "item[1]:b", "item[2]:c"] // result := r(Config{Prefix: "item"}) // ["item[0]:a", "item[1]:b", "item[2]:c"]
func TraverseArrayWithIndex[R, A, B any](f func(int, A) Reader[R, B]) func([]A) Reader[R, []B] { func TraverseArrayWithIndex[R, A, B any](f func(int, A) Reader[R, B]) func([]A) Reader[R, []B] {
return array.TraverseWithIndex[[]A]( return G.TraverseArrayWithIndex[Reader[R, B], Reader[R, []B], []A](f)
Of[R, []B],
Map[R, []B, func(B) []B],
Ap[[]B, R, B],
f,
)
} }
// SequenceArray converts an array of Readers into a single Reader containing an array. // SequenceArray converts an array of Readers into a single Reader containing an array.

View File

@@ -0,0 +1,15 @@
package generic
import (
M "github.com/IBM/fp-go/v2/monoid"
)
//go:inline
func ApplicativeMonoid[GA ~func(R) A, R, A any](m M.Monoid[A]) M.Monoid[GA] {
return M.ApplicativeMonoid(
Of[GA, R, A],
MonadMap[GA, func(R) func(A) A],
MonadAp[GA, GA, func(R) func(A) A],
m,
)
}