1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-12-09 23:11:40 +02:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Dr. Carsten Leue
ab868315d4 fix: traverse
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-15 12:13:37 +01:00
Dr. Carsten Leue
02d0be9dad fix: add traversal for sequences
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2025-11-14 14:12:44 +01:00
29 changed files with 1053 additions and 152 deletions

View File

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

View File

@@ -25,8 +25,10 @@ import (
)
// Of constructs a single element array
//
//go:inline
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 {

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 (
"testing"
O "github.com/IBM/fp-go/v2/option"
OR "github.com/IBM/fp-go/v2/ord"
"github.com/stretchr/testify/assert"
)
@@ -103,39 +102,6 @@ func TestSortByKey(t *testing.T) {
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) {
type Person struct {
Name string

View File

@@ -16,27 +16,12 @@
package array
import (
G "github.com/IBM/fp-go/v2/array/generic"
"github.com/IBM/fp-go/v2/internal/array"
M "github.com/IBM/fp-go/v2/monoid"
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.
// 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]()
// result := m.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
// empty := m.Empty() // []
//
//go:inline
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.
@@ -56,8 +43,10 @@ func Monoid[T any]() M.Monoid[[]T] {
//
// s := array.Semigroup[int]()
// result := s.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]
//
//go:inline
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 {

View File

@@ -16,10 +16,18 @@
package array
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"
)
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,
// using an applicative of that HKT, returns an HKT of []A.
//
@@ -55,16 +63,11 @@ import (
// option.MonadAp[[]int, int],
// )
// result := seq(opts) // Some([1, 2, 3])
func Sequence[A, HKTA, HKTRA, HKTFRA any](
_of func([]A) HKTRA,
_map func(HKTRA, func([]A) func(A) []A) HKTFRA,
_ap func(HKTFRA, HKTA) HKTRA,
func Sequence[HKTA, HKTRA any](
fof func(HKTA) HKTRA,
m M.Monoid[HKTRA],
) func([]HKTA) HKTRA {
ca := F.Curry2(Append[A])
empty := _of(Empty[A]())
return Reduce(func(fas HKTRA, fa HKTA) HKTRA {
return _ap(_map(fas, ca), fa)
}, empty)
return array.Sequence[[]HKTA](fof, m.Empty(), m.Concat)
}
// 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),
// }
// result2 := array.ArrayOption[int]()(opts2) // None
func ArrayOption[A any]() func([]Option[A]) Option[[]A] {
return Sequence(
O.Of[[]A],
O.MonadMap[[]A, func(A) []A],
O.MonadAp[[]A, A],
func ArrayOption[A any](ma []Option[A]) Option[[]A] {
return MonadSequence(
O.Map(Of[A]),
O.ApplicativeMonoid(Monoid[A]()),
ma,
)
}

View File

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

View File

@@ -16,8 +16,8 @@
package readerioresult
import (
"github.com/IBM/fp-go/v2/array"
"github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/array"
"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.
func TraverseArray[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
return array.Traverse[[]A](
return array.Traverse(
Of[[]B],
Map[[]B, func(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.
func TraverseArrayWithIndex[A, B any](f func(int, A) ReaderIOResult[B]) Kleisli[[]A, []B] {
return array.TraverseWithIndex[[]A](
return array.TraverseWithIndex(
Of[[]B],
Map[[]B, func(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.
func TraverseArraySeq[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
return array.Traverse[[]A](
return array.Traverse(
Of[[]B],
Map[[]B, func(B) []B],
ApSeq[[]B, B],
f,
)
}
// 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] {
return array.TraverseWithIndex[[]A](
return array.TraverseWithIndex(
Of[[]B],
Map[[]B, func(B) []B],
ApSeq[[]B, B],
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.
func TraverseArrayPar[A, B any](f Kleisli[A, B]) Kleisli[[]A, []B] {
return array.Traverse[[]A](
return array.Traverse(
Of[[]B],
Map[[]B, func(B) []B],
ApPar[[]B, B],
f,
)
}
// 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] {
return array.TraverseWithIndex[[]A](
return array.TraverseWithIndex(
Of[[]B],
Map[[]B, func(B) []B],
ApPar[[]B, B],
f,
)
}

View File

@@ -15,6 +15,10 @@
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 {
return func(as GA) GA {
length := len(as)
@@ -77,8 +81,7 @@ func IsNonNil[GA ~[]A, A any](as GA) bool {
func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
current := initial
count := len(fa)
for i := 0; i < count; i++ {
for i := range len(fa) {
current = f(current, fa[i])
}
return current
@@ -86,8 +89,7 @@ func Reduce[GA ~[]A, A, B any](fa GA, f func(B, A) B, initial B) B {
func ReduceWithIndex[GA ~[]A, A, B any](fa GA, f func(int, B, A) B, initial B) B {
current := initial
count := len(fa)
for i := 0; i < count; i++ {
for i := range len(fa) {
current = f(i, current, fa[i])
}
return current
@@ -142,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 {
count := len(as)
bs := make(GB, count)
for i := count - 1; i >= 0; i-- {
for i := range count {
bs[i] = f(as[i])
}
return bs
@@ -157,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 {
count := len(as)
bs := make(GB, count)
for i := count - 1; i >= 0; i-- {
for i := range count {
bs[i] = f(i, as[i])
}
return bs
@@ -166,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 {
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"
)
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
@@ -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](
fof func(GB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,

100
v2/internal/iter/iter.go Normal file
View File

@@ -0,0 +1,100 @@
package iter
import (
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 {
current := initial
var i int
for a := range fa {
current = f(i, current, a)
i += 1
}
return current
}
func MonadReduce[GA ~func(yield func(A) bool), A, B any](fa GA, f func(B, A) B, initial B) B {
current := initial
for a := range fa {
current = f(current, a)
}
return current
}
// Concat concatenates two sequences, yielding all elements from left followed by all elements from right.
func Concat[GT ~func(yield func(T) bool), T any](left, right GT) GT {
return func(yield func(T) bool) {
for t := range left {
if !yield(t) {
return
}
}
for t := range right {
if !yield(t) {
return
}
}
}
}
func Of[GA ~func(yield func(A) bool), A any](a A) GA {
return func(yield func(A) bool) {
yield(a)
}
}
func MonadAppend[GA ~func(yield func(A) bool), A any](f GA, tail A) GA {
return Concat(f, Of[GA](tail))
}
func Append[GA ~func(yield func(A) bool), A any](tail A) func(GA) GA {
return F.Bind2nd(Concat[GA], Of[GA](tail))
}
func Prepend[GA ~func(yield func(A) bool), A any](head A) func(GA) GA {
return F.Bind1st(Concat[GA], Of[GA](head))
}
func Empty[GA ~func(yield func(A) bool), A any]() GA {
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

@@ -0,0 +1,203 @@
// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package iter
import (
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"
)
/*
*
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 MonadTraverse[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(HKT_B, func(B) GB) HKT_GB,
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,
f func(A) HKT_B) HKT_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))
}
/*
*
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 MonadTraverseWithIndex[GA ~func(yield func(A) bool), A, HKTB, HKTRB any](
fof func(HKTB) HKTRB,
m M.Monoid[HKTRB],
ta GA,
f func(int, A) HKTB) HKTRB {
// convert to an array
hktb := MonadMapToArrayWithIndex[GA, []HKTB](ta, f)
return INTA.MonadSequenceSegment(fof, m.Empty(), m.Concat, hktb, 0, len(hktb))
}
func Sequence[GA ~func(yield func(HKTA) bool), HKTA, HKTRA any](
fof func(HKTA) HKTRA,
m M.Monoid[HKTRA]) func(GA) HKTRA {
return func(ma GA) HKTRA {
return MonadSequence(fof, m, ma)
}
}
func TraverseWithIndex[GA ~func(yield func(A) bool), A, HKTB, HKTRB any](
fof func(HKTB) HKTRB,
m M.Monoid[HKTRB],
f func(int, A) HKTB) func(GA) HKTRB {
return func(ma GA) HKTRB {
return MonadTraverseWithIndex(fof, m, ma, f)
}
}
func MonadTraverseReduce[GA ~func(yield func(A) bool), GB, A, B, HKTB, HKTAB, HKTRB any](
fof func(GB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
ta GA,
transform func(A) HKTB,
reduce func(GB, B) GB,
initial GB,
) HKTRB {
mmap := fmap(F.Curry2(reduce))
return MonadReduce(ta, func(r HKTRB, a A) HKTRB {
return F.Pipe2(
r,
mmap,
fap(transform(a)),
)
}, fof(initial))
}
func MonadTraverseReduceWithIndex[GA ~func(yield func(A) bool), GB, A, B, HKTB, HKTAB, HKTRB any](
fof func(GB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
ta GA,
transform func(int, A) HKTB,
reduce func(GB, B) GB,
initial GB,
) HKTRB {
mmap := fmap(F.Curry2(reduce))
return MonadReduceWithIndex(ta, func(idx int, r HKTRB, a A) HKTRB {
return F.Pipe2(
r,
mmap,
fap(transform(idx, a)),
)
}, fof(initial))
}
func TraverseReduce[GA ~func(yield func(A) bool), GB, A, B, HKTB, HKTAB, HKTRB any](
fof func(GB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
transform func(A) HKTB,
reduce func(GB, B) GB,
initial GB,
) func(GA) HKTRB {
return func(ta GA) HKTRB {
return MonadTraverseReduce(fof, fmap, fap, ta, transform, reduce, initial)
}
}
func TraverseReduceWithIndex[GA ~func(yield func(A) bool), GB, A, B, HKTB, HKTAB, HKTRB any](
fof func(GB) HKTRB,
fmap func(func(GB) func(B) GB) func(HKTRB) HKTAB,
fap func(HKTB) func(HKTAB) HKTRB,
transform func(int, A) HKTB,
reduce func(GB, B) GB,
initial GB,
) func(GA) HKTRB {
return func(ta GA) HKTRB {
return MonadTraverseReduceWithIndex(fof, fmap, fap, ta, transform, reduce, initial)
}
}

View File

@@ -0,0 +1,9 @@
package iter
import (
I "iter"
)
type (
Seq[A any] = I.Seq[A]
)

View File

@@ -18,6 +18,7 @@ package io
import (
F "github.com/IBM/fp-go/v2/function"
INTA "github.com/IBM/fp-go/v2/internal/array"
INTI "github.com/IBM/fp-go/v2/internal/iter"
INTR "github.com/IBM/fp-go/v2/internal/record"
)
@@ -60,6 +61,18 @@ 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]] {
return INTI.Traverse[Seq[A]](
Map[B],
Of[Seq[B]],
Map[Seq[B]],
MonadAp[Seq[B]],
f,
)
}
// TraverseArrayWithIndex is like TraverseArray but the function also receives the index.
// Executes in parallel by default.
//

7
v2/io/types.go Normal file
View File

@@ -0,0 +1,7 @@
package io
import "iter"
type (
Seq[T any] = iter.Seq[T]
)

View File

@@ -48,6 +48,7 @@ import (
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/functor"
G "github.com/IBM/fp-go/v2/internal/iter"
M "github.com/IBM/fp-go/v2/monoid"
"github.com/IBM/fp-go/v2/option"
)
@@ -58,10 +59,10 @@ import (
//
// seq := Of(42)
// // yields: 42
//
//go:inline
func Of[A any](a A) Seq[A] {
return func(yield Predicate[A]) {
yield(a)
}
return G.Of[Seq[A]](a)
}
// Of2 creates a key-value sequence containing a single key-value pair.
@@ -521,7 +522,7 @@ func From[A any](data ...A) Seq[A] {
//
//go:inline
func Empty[A any]() Seq[A] {
return func(_ Predicate[A]) {}
return G.Empty[Seq[A]]()
}
// MakeBy creates a sequence of n elements by applying a function to each index.
@@ -566,12 +567,10 @@ func Replicate[A any](n int, a A) Seq[A] {
// seq := From(1, 2, 3, 4, 5)
// sum := MonadReduce(seq, func(acc, x int) int { return acc + x }, 0)
// // returns: 15
//
//go:inline
func MonadReduce[A, B any](fa Seq[A], f func(B, A) B, initial B) B {
current := initial
for a := range fa {
current = f(current, a)
}
return current
return G.MonadReduce(fa, f, initial)
}
// Reduce returns a function that reduces a sequence to a single value.
@@ -598,14 +597,10 @@ func Reduce[A, B any](f func(B, A) B, initial B) func(Seq[A]) B {
// return acc + (i * x)
// }, 0)
// // returns: 0*10 + 1*20 + 2*30 = 80
//
//go:inline
func MonadReduceWithIndex[A, B any](fa Seq[A], f func(int, B, A) B, initial B) B {
current := initial
var i int
for a := range fa {
current = f(i, current, a)
i += 1
}
return current
return G.MonadReduceWithIndex(fa, f, initial)
}
// ReduceWithIndex returns a function that reduces with index.
@@ -831,7 +826,7 @@ func Flap[B, A any](a A) Operator[func(A) B, B] {
//
//go:inline
func Prepend[A any](head A) Operator[A, A] {
return F.Bind1st(concat[A], Of(head))
return G.Prepend[Seq[A]](head)
}
// Append returns a function that adds an element to the end of a sequence.
@@ -844,7 +839,7 @@ func Prepend[A any](head A) Operator[A, A] {
//
//go:inline
func Append[A any](tail A) Operator[A, A] {
return F.Bind2nd(concat[A], Of(tail))
return G.Append[Seq[A]](tail)
}
// MonadZip combines two sequences into a sequence of pairs.
@@ -890,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] {
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

@@ -16,25 +16,10 @@
package iter
import (
G "github.com/IBM/fp-go/v2/internal/iter"
M "github.com/IBM/fp-go/v2/monoid"
)
// concat concatenates two sequences, yielding all elements from left followed by all elements from right.
func concat[T any](left, right Seq[T]) Seq[T] {
return func(yield Predicate[T]) {
for t := range left {
if !yield(t) {
return
}
}
for t := range right {
if !yield(t) {
return
}
}
}
}
// Monoid returns a Monoid instance for Seq[T].
// The monoid's concat operation concatenates sequences, and the empty value is an empty sequence.
//
@@ -48,5 +33,5 @@ func concat[T any](left, right Seq[T]) Seq[T] {
//
//go:inline
func Monoid[T any]() M.Monoid[Seq[T]] {
return M.MakeMonoid(concat[T], Empty[T]())
return G.Monoid[Seq[T]]()
}

View File

@@ -21,7 +21,7 @@ import (
)
type (
Traversal[E, S, A any] T.Traversal[S, A, ET.Either[E, S], ET.Either[E, A]]
Traversal[E, S, A any] = T.Traversal[S, A, ET.Either[E, S], ET.Either[E, A]]
)
func Compose[

View File

@@ -22,7 +22,7 @@ import (
)
type (
Traversal[S, A, HKTS, HKTA any] func(func(A) HKTA) func(S) HKTS
Traversal[S, A, HKTS, HKTA any] = func(func(A) HKTA) func(S) HKTS
)
func Compose[

View File

@@ -0,0 +1,29 @@
// 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 result
import (
T "github.com/IBM/fp-go/v2/optics/traversal/generic"
)
func Compose[
S, A, B any](ab Traversal[A, B]) Operator[S, A, B] {
return T.Compose[
Traversal[A, B],
Traversal[S, A],
Traversal[S, B],
](ab)
}

View File

@@ -0,0 +1,12 @@
package result
import (
T "github.com/IBM/fp-go/v2/optics/traversal/generic"
"github.com/IBM/fp-go/v2/result"
)
type (
Traversal[S, A any] = T.Traversal[S, A, Result[S], Result[A]]
Result[T any] = result.Result[T]
Operator[S, A, B any] = func(Traversal[S, A]) Traversal[S, B]
)

View File

@@ -42,6 +42,8 @@ func ApplySemigroup[A any](s S.Semigroup[A]) S.Semigroup[Option[A]] {
// optMonoid := ApplicativeMonoid(intMonoid)
// result := optMonoid.Concat(Some(2), Some(3)) // Some(5)
// result := optMonoid.Empty() // Some(0)
//
//go:inline
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)
}

76
v2/option/iter.go Normal file
View File

@@ -0,0 +1,76 @@
// Copyright (c) 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 option
import (
INTI "github.com/IBM/fp-go/v2/internal/iter"
)
// TraverseIter transforms a sequence by applying a function that returns an Option to each element.
// Returns Some containing a sequence of results if all operations succeed, None if any fails.
// This function is useful for processing sequences where each element may fail validation or transformation.
//
// The traversal short-circuits on the first None encountered, making it efficient for validation pipelines.
// The resulting sequence is lazy and will only be evaluated when iterated.
//
// Example:
//
// // Parse a sequence of strings to integers
// parse := func(s string) Option[int] {
// n, err := strconv.Atoi(s)
// if err != nil { return None[int]() }
// return Some(n)
// }
//
// // Create a sequence of strings
// strings := func(yield func(string) bool) {
// for _, s := range []string{"1", "2", "3"} {
// if !yield(s) { return }
// }
// }
//
// result := TraverseIter(parse)(strings)
// // result is Some(sequence of [1, 2, 3])
//
// // With invalid input
// invalidStrings := func(yield func(string) bool) {
// for _, s := range []string{"1", "invalid", "3"} {
// if !yield(s) { return }
// }
// }
//
// result := TraverseIter(parse)(invalidStrings)
// // result is None because "invalid" cannot be parsed
func TraverseIter[A, B any](f Kleisli[A, B]) Kleisli[Seq[A], Seq[B]] {
return INTI.Traverse[Seq[A]](
Map[B],
Of[Seq[B]],
Map[Seq[B]],
MonadAp[Seq[B]],
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,
)
}

329
v2/option/iter_test.go Normal file
View File

@@ -0,0 +1,329 @@
// Copyright (c) 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 option
import (
"fmt"
"slices"
"strconv"
"testing"
F "github.com/IBM/fp-go/v2/function"
"github.com/stretchr/testify/assert"
)
// Helper function to create a sequence from a slice
func seqFromSlice[T any](items []T) Seq[T] {
return slices.Values(items)
}
// Helper function to collect a sequence into a slice
func collectSeq[T any](seq Seq[T]) []T {
return slices.Collect(seq)
}
func TestTraverseIter_AllSome(t *testing.T) {
// Test case where all transformations succeed
parse := func(s string) Option[int] {
n, err := strconv.Atoi(s)
if err != nil {
return None[int]()
}
return Some(n)
}
input := seqFromSlice([]string{"1", "2", "3", "4", "5"})
result := TraverseIter(parse)(input)
assert.True(t, IsSome(result), "Expected Some result when all transformations succeed")
collected := MonadFold(result, func() []int { return nil }, collectSeq[int])
expected := []int{1, 2, 3, 4, 5}
assert.Equal(t, expected, collected)
}
func TestTraverseIter_ContainsNone(t *testing.T) {
// Test case where one transformation fails
parse := func(s string) Option[int] {
n, err := strconv.Atoi(s)
if err != nil {
return None[int]()
}
return Some(n)
}
input := seqFromSlice([]string{"1", "invalid", "3"})
result := TraverseIter(parse)(input)
assert.True(t, IsNone(result), "Expected None when any transformation fails")
}
func TestTraverseIter_EmptySequence(t *testing.T) {
// Test with empty sequence
double := func(x int) Option[int] {
return Some(x * 2)
}
input := seqFromSlice([]int{})
result := TraverseIter(double)(input)
assert.True(t, IsSome(result), "Expected Some for empty sequence")
collected := MonadFold(result, func() []int { return nil }, collectSeq[int])
assert.Empty(t, collected)
}
func TestTraverseIter_SingleElement(t *testing.T) {
// Test with single element - success case
validate := func(x int) Option[int] {
if x > 0 {
return Some(x * 2)
}
return None[int]()
}
input := seqFromSlice([]int{5})
result := TraverseIter(validate)(input)
assert.True(t, IsSome(result))
collected := MonadFold(result, func() []int { return nil }, collectSeq[int])
assert.Equal(t, []int{10}, collected)
}
func TestTraverseIter_SingleElementFails(t *testing.T) {
// Test with single element - failure case
validate := func(x int) Option[int] {
if x > 0 {
return Some(x * 2)
}
return None[int]()
}
input := seqFromSlice([]int{-5})
result := TraverseIter(validate)(input)
assert.True(t, IsNone(result))
}
func TestTraverseIter_Validation(t *testing.T) {
// Test validation use case
validatePositive := func(x int) Option[int] {
if x > 0 {
return Some(x)
}
return None[int]()
}
// All positive
input1 := seqFromSlice([]int{1, 2, 3, 4})
result1 := TraverseIter(validatePositive)(input1)
assert.True(t, IsSome(result1))
// Contains negative
input2 := seqFromSlice([]int{1, -2, 3})
result2 := TraverseIter(validatePositive)(input2)
assert.True(t, IsNone(result2))
// Contains zero
input3 := seqFromSlice([]int{1, 0, 3})
result3 := TraverseIter(validatePositive)(input3)
assert.True(t, IsNone(result3))
}
func TestTraverseIter_Transformation(t *testing.T) {
// Test transformation use case
safeDivide := func(x int) Option[float64] {
if x != 0 {
return Some(100.0 / float64(x))
}
return None[float64]()
}
// All non-zero
input1 := seqFromSlice([]int{1, 2, 4, 5})
result1 := TraverseIter(safeDivide)(input1)
assert.True(t, IsSome(result1))
collected := MonadFold(result1, func() []float64 { return nil }, collectSeq[float64])
expected := []float64{100.0, 50.0, 25.0, 20.0}
assert.Equal(t, expected, collected)
// Contains zero
input2 := seqFromSlice([]int{1, 0, 4})
result2 := TraverseIter(safeDivide)(input2)
assert.True(t, IsNone(result2))
}
func TestTraverseIter_ShortCircuit(t *testing.T) {
// Test that traversal short-circuits on first None
callCount := 0
countingFunc := func(x int) Option[int] {
callCount++
if x < 0 {
return None[int]()
}
return Some(x * 2)
}
// First element fails
input := seqFromSlice([]int{-1, 2, 3, 4, 5})
result := TraverseIter(countingFunc)(input)
assert.True(t, IsNone(result))
// Should have called the function for elements until the first failure
// Note: The exact count depends on implementation details of the traverse function
assert.Greater(t, callCount, 0, "Function should be called at least once")
}
func TestTraverseIter_LazyEvaluation(t *testing.T) {
// Test that the result sequence is lazy
transform := func(x int) Option[int] {
return Some(x * 2)
}
input := seqFromSlice([]int{1, 2, 3, 4, 5})
result := TraverseIter(transform)(input)
assert.True(t, IsSome(result))
// Partially consume the sequence
callCount := 0
MonadFold(result, func() int { return 0 }, func(seq Seq[int]) int {
for val := range seq {
callCount++
_ = val
if callCount == 2 {
break
}
}
return callCount
})
assert.Equal(t, 2, callCount, "Should only evaluate consumed elements")
}
func TestTraverseIter_ComplexTransformation(t *testing.T) {
// Test with more complex transformation
type Person struct {
Name string
Age int
}
validatePerson := func(name string) Option[Person] {
if name == "" {
return None[Person]()
}
return Some(Person{Name: name, Age: len(name)})
}
input := seqFromSlice([]string{"Alice", "Bob", "Charlie"})
result := TraverseIter(validatePerson)(input)
assert.True(t, IsSome(result))
collected := MonadFold(result, func() []Person { return nil }, collectSeq[Person])
expected := []Person{
{Name: "Alice", Age: 5},
{Name: "Bob", Age: 3},
{Name: "Charlie", Age: 7},
}
assert.Equal(t, expected, collected)
}
func TestTraverseIter_WithPipeline(t *testing.T) {
// Test TraverseIter in a functional pipeline
parse := func(s string) Option[int] {
n, err := strconv.Atoi(s)
if err != nil {
return None[int]()
}
return Some(n)
}
input := seqFromSlice([]string{"1", "2", "3", "4", "5"})
result := F.Pipe2(
input,
TraverseIter(parse),
Map(collectSeq[int]),
)
collected := MonadFold(result, func() []int { return nil }, F.Identity[[]int])
expected := []int{1, 2, 3, 4, 5}
assert.Equal(t, expected, collected)
}
func TestTraverseIter_ChainedTransformations(t *testing.T) {
// Test chaining multiple transformations
parseAndValidate := func(s string) Option[int] {
n, err := strconv.Atoi(s)
if err != nil {
return None[int]()
}
if n > 0 {
return Some(n)
}
return None[int]()
}
// All valid
input1 := seqFromSlice([]string{"1", "2", "3"})
result1 := TraverseIter(parseAndValidate)(input1)
assert.True(t, IsSome(result1))
// Contains invalid number
input2 := seqFromSlice([]string{"1", "invalid", "3"})
result2 := TraverseIter(parseAndValidate)(input2)
assert.True(t, IsNone(result2))
// Contains non-positive number
input3 := seqFromSlice([]string{"1", "0", "3"})
result3 := TraverseIter(parseAndValidate)(input3)
assert.True(t, IsNone(result3))
}
// Example test demonstrating usage
func ExampleTraverseIter() {
// Parse a sequence of strings to integers
parse := func(s string) Option[int] {
n, err := strconv.Atoi(s)
if err != nil {
return None[int]()
}
return Some(n)
}
// Create a sequence of valid strings
validStrings := seqFromSlice([]string{"1", "2", "3"})
result := TraverseIter(parse)(validStrings)
if IsSome(result) {
numbers := MonadFold(result, func() []int { return nil }, collectSeq[int])
fmt.Println(numbers)
}
// Create a sequence with invalid string
invalidStrings := seqFromSlice([]string{"1", "invalid", "3"})
result2 := TraverseIter(parse)(invalidStrings)
if IsNone(result2) {
fmt.Println("Parsing failed")
}
// Output:
// [1 2 3]
// Parsing failed
}

7
v2/option/types.go Normal file
View File

@@ -0,0 +1,7 @@
package option
import "iter"
type (
Seq[T any] = iter.Seq[T]
)

View File

@@ -17,7 +17,7 @@ package reader
import (
"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,
@@ -38,13 +38,7 @@ import (
// r := reader.MonadTraverseArray(numbers, addPrefix)
// result := r(Config{Prefix: "num"}) // ["num1", "num2", "num3"]
func MonadTraverseArray[R, A, B any](ma []A, f Kleisli[R, A, B]) Reader[R, []B] {
return array.MonadTraverse(
Of[R, []B],
Map[R, []B, func(B) []B],
Ap[[]B, R, B],
ma,
f,
)
return G.MonadTraverseArray[Reader[R, B], Reader[R, []B], []A](ma, f)
}
// 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})
// result := r(Config{Multiplier: 10}) // [10, 20, 30]
func TraverseArray[R, A, B any](f Kleisli[R, A, B]) func([]A) Reader[R, []B] {
return array.Traverse[[]A](
Of[R, []B],
Map[R, []B, func(B) []B],
Ap[[]B, R, B],
f,
)
return G.TraverseArray[Reader[R, B], Reader[R, []B], []A](f)
}
// 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"})
// 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] {
return array.TraverseWithIndex[[]A](
Of[R, []B],
Map[R, []B, func(B) []B],
Ap[[]B, R, B],
f,
)
return G.TraverseArrayWithIndex[Reader[R, B], Reader[R, []B], []A](f)
}
// 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,
)
}