1
0
mirror of https://github.com/IBM/fp-go.git synced 2026-03-10 13:31:01 +02:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Dr. Carsten Leue
ce3c7d9359 fix: documentation of endomorphism
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2026-03-08 22:02:11 +01:00
Dr. Carsten Leue
3ed354cc8c fix: implement endomorphism.Read
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2026-03-08 19:01:22 +01:00
Dr. Carsten Leue
0932c8c464 fix: add tests for totality and move skills
Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
2026-03-08 14:12:41 +01:00
12 changed files with 1405 additions and 26 deletions

522
v2/array/array_nil_test.go Normal file
View File

@@ -0,0 +1,522 @@
// 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 (
"fmt"
"testing"
O "github.com/IBM/fp-go/v2/option"
P "github.com/IBM/fp-go/v2/pair"
S "github.com/IBM/fp-go/v2/string"
"github.com/stretchr/testify/assert"
)
// TestNilSlice_IsEmpty verifies that IsEmpty handles nil slices correctly
func TestNilSlice_IsEmpty(t *testing.T) {
var nilSlice []int
assert.True(t, IsEmpty(nilSlice), "nil slice should be empty")
}
// TestNilSlice_IsNonEmpty verifies that IsNonEmpty handles nil slices correctly
func TestNilSlice_IsNonEmpty(t *testing.T) {
var nilSlice []int
assert.False(t, IsNonEmpty(nilSlice), "nil slice should not be non-empty")
}
// TestNilSlice_MonadMap verifies that MonadMap handles nil slices correctly
func TestNilSlice_MonadMap(t *testing.T) {
var nilSlice []int
result := MonadMap(nilSlice, func(v int) string {
return fmt.Sprintf("%d", v)
})
assert.NotNil(t, result, "MonadMap should return non-nil slice")
assert.Equal(t, 0, len(result), "MonadMap should return empty slice for nil input")
}
// TestNilSlice_MonadMapRef verifies that MonadMapRef handles nil slices correctly
func TestNilSlice_MonadMapRef(t *testing.T) {
var nilSlice []int
result := MonadMapRef(nilSlice, func(v *int) string {
return fmt.Sprintf("%d", *v)
})
assert.NotNil(t, result, "MonadMapRef should return non-nil slice")
assert.Equal(t, 0, len(result), "MonadMapRef should return empty slice for nil input")
}
// TestNilSlice_Map verifies that Map handles nil slices correctly
func TestNilSlice_Map(t *testing.T) {
var nilSlice []int
mapper := Map(func(v int) string {
return fmt.Sprintf("%d", v)
})
result := mapper(nilSlice)
assert.NotNil(t, result, "Map should return non-nil slice")
assert.Equal(t, 0, len(result), "Map should return empty slice for nil input")
}
// TestNilSlice_MapRef verifies that MapRef handles nil slices correctly
func TestNilSlice_MapRef(t *testing.T) {
var nilSlice []int
mapper := MapRef(func(v *int) string {
return fmt.Sprintf("%d", *v)
})
result := mapper(nilSlice)
assert.NotNil(t, result, "MapRef should return non-nil slice")
assert.Equal(t, 0, len(result), "MapRef should return empty slice for nil input")
}
// TestNilSlice_MapWithIndex verifies that MapWithIndex handles nil slices correctly
func TestNilSlice_MapWithIndex(t *testing.T) {
var nilSlice []int
mapper := MapWithIndex(func(i int, v int) string {
return fmt.Sprintf("%d:%d", i, v)
})
result := mapper(nilSlice)
assert.NotNil(t, result, "MapWithIndex should return non-nil slice")
assert.Equal(t, 0, len(result), "MapWithIndex should return empty slice for nil input")
}
// TestNilSlice_Filter verifies that Filter handles nil slices correctly
func TestNilSlice_Filter(t *testing.T) {
var nilSlice []int
filter := Filter(func(v int) bool {
return v > 0
})
result := filter(nilSlice)
assert.NotNil(t, result, "Filter should return non-nil slice")
assert.Equal(t, 0, len(result), "Filter should return empty slice for nil input")
}
// TestNilSlice_FilterWithIndex verifies that FilterWithIndex handles nil slices correctly
func TestNilSlice_FilterWithIndex(t *testing.T) {
var nilSlice []int
filter := FilterWithIndex(func(i int, v int) bool {
return v > 0
})
result := filter(nilSlice)
assert.NotNil(t, result, "FilterWithIndex should return non-nil slice")
assert.Equal(t, 0, len(result), "FilterWithIndex should return empty slice for nil input")
}
// TestNilSlice_FilterRef verifies that FilterRef handles nil slices correctly
func TestNilSlice_FilterRef(t *testing.T) {
var nilSlice []int
filter := FilterRef(func(v *int) bool {
return *v > 0
})
result := filter(nilSlice)
assert.NotNil(t, result, "FilterRef should return non-nil slice")
assert.Equal(t, 0, len(result), "FilterRef should return empty slice for nil input")
}
// TestNilSlice_MonadFilterMap verifies that MonadFilterMap handles nil slices correctly
func TestNilSlice_MonadFilterMap(t *testing.T) {
var nilSlice []int
result := MonadFilterMap(nilSlice, func(v int) O.Option[string] {
return O.Some(fmt.Sprintf("%d", v))
})
assert.NotNil(t, result, "MonadFilterMap should return non-nil slice")
assert.Equal(t, 0, len(result), "MonadFilterMap should return empty slice for nil input")
}
// TestNilSlice_MonadFilterMapWithIndex verifies that MonadFilterMapWithIndex handles nil slices correctly
func TestNilSlice_MonadFilterMapWithIndex(t *testing.T) {
var nilSlice []int
result := MonadFilterMapWithIndex(nilSlice, func(i int, v int) O.Option[string] {
return O.Some(fmt.Sprintf("%d:%d", i, v))
})
assert.NotNil(t, result, "MonadFilterMapWithIndex should return non-nil slice")
assert.Equal(t, 0, len(result), "MonadFilterMapWithIndex should return empty slice for nil input")
}
// TestNilSlice_FilterMap verifies that FilterMap handles nil slices correctly
func TestNilSlice_FilterMap(t *testing.T) {
var nilSlice []int
filter := FilterMap(func(v int) O.Option[string] {
return O.Some(fmt.Sprintf("%d", v))
})
result := filter(nilSlice)
assert.NotNil(t, result, "FilterMap should return non-nil slice")
assert.Equal(t, 0, len(result), "FilterMap should return empty slice for nil input")
}
// TestNilSlice_FilterMapWithIndex verifies that FilterMapWithIndex handles nil slices correctly
func TestNilSlice_FilterMapWithIndex(t *testing.T) {
var nilSlice []int
filter := FilterMapWithIndex(func(i int, v int) O.Option[string] {
return O.Some(fmt.Sprintf("%d:%d", i, v))
})
result := filter(nilSlice)
assert.NotNil(t, result, "FilterMapWithIndex should return non-nil slice")
assert.Equal(t, 0, len(result), "FilterMapWithIndex should return empty slice for nil input")
}
// TestNilSlice_MonadReduce verifies that MonadReduce handles nil slices correctly
func TestNilSlice_MonadReduce(t *testing.T) {
var nilSlice []int
result := MonadReduce(nilSlice, func(acc int, v int) int {
return acc + v
}, 10)
assert.Equal(t, 10, result, "MonadReduce should return initial value for nil slice")
}
// TestNilSlice_MonadReduceWithIndex verifies that MonadReduceWithIndex handles nil slices correctly
func TestNilSlice_MonadReduceWithIndex(t *testing.T) {
var nilSlice []int
result := MonadReduceWithIndex(nilSlice, func(i int, acc int, v int) int {
return acc + v
}, 10)
assert.Equal(t, 10, result, "MonadReduceWithIndex should return initial value for nil slice")
}
// TestNilSlice_Reduce verifies that Reduce handles nil slices correctly
func TestNilSlice_Reduce(t *testing.T) {
var nilSlice []int
reducer := Reduce(func(acc int, v int) int {
return acc + v
}, 10)
result := reducer(nilSlice)
assert.Equal(t, 10, result, "Reduce should return initial value for nil slice")
}
// TestNilSlice_ReduceWithIndex verifies that ReduceWithIndex handles nil slices correctly
func TestNilSlice_ReduceWithIndex(t *testing.T) {
var nilSlice []int
reducer := ReduceWithIndex(func(i int, acc int, v int) int {
return acc + v
}, 10)
result := reducer(nilSlice)
assert.Equal(t, 10, result, "ReduceWithIndex should return initial value for nil slice")
}
// TestNilSlice_ReduceRight verifies that ReduceRight handles nil slices correctly
func TestNilSlice_ReduceRight(t *testing.T) {
var nilSlice []int
reducer := ReduceRight(func(v int, acc int) int {
return acc + v
}, 10)
result := reducer(nilSlice)
assert.Equal(t, 10, result, "ReduceRight should return initial value for nil slice")
}
// TestNilSlice_ReduceRightWithIndex verifies that ReduceRightWithIndex handles nil slices correctly
func TestNilSlice_ReduceRightWithIndex(t *testing.T) {
var nilSlice []int
reducer := ReduceRightWithIndex(func(i int, v int, acc int) int {
return acc + v
}, 10)
result := reducer(nilSlice)
assert.Equal(t, 10, result, "ReduceRightWithIndex should return initial value for nil slice")
}
// TestNilSlice_ReduceRef verifies that ReduceRef handles nil slices correctly
func TestNilSlice_ReduceRef(t *testing.T) {
var nilSlice []int
reducer := ReduceRef(func(acc int, v *int) int {
return acc + *v
}, 10)
result := reducer(nilSlice)
assert.Equal(t, 10, result, "ReduceRef should return initial value for nil slice")
}
// TestNilSlice_Append verifies that Append handles nil slices correctly
func TestNilSlice_Append(t *testing.T) {
var nilSlice []int
result := Append(nilSlice, 42)
assert.NotNil(t, result, "Append should return non-nil slice")
assert.Equal(t, 1, len(result), "Append should create slice with one element")
assert.Equal(t, 42, result[0], "Append should add element correctly")
}
// TestNilSlice_MonadChain verifies that MonadChain handles nil slices correctly
func TestNilSlice_MonadChain(t *testing.T) {
var nilSlice []int
result := MonadChain(nilSlice, func(v int) []string {
return []string{fmt.Sprintf("%d", v)}
})
assert.NotNil(t, result, "MonadChain should return non-nil slice")
assert.Equal(t, 0, len(result), "MonadChain should return empty slice for nil input")
}
// TestNilSlice_Chain verifies that Chain handles nil slices correctly
func TestNilSlice_Chain(t *testing.T) {
var nilSlice []int
chain := Chain(func(v int) []string {
return []string{fmt.Sprintf("%d", v)}
})
result := chain(nilSlice)
assert.NotNil(t, result, "Chain should return non-nil slice")
assert.Equal(t, 0, len(result), "Chain should return empty slice for nil input")
}
// TestNilSlice_MonadAp verifies that MonadAp handles nil slices correctly
func TestNilSlice_MonadAp(t *testing.T) {
var nilFuncs []func(int) string
var nilValues []int
// nil functions, nil values
result1 := MonadAp(nilFuncs, nilValues)
assert.NotNil(t, result1, "MonadAp should return non-nil slice")
assert.Equal(t, 0, len(result1), "MonadAp should return empty slice for nil inputs")
// nil functions, non-nil values
nonNilValues := []int{1, 2, 3}
result2 := MonadAp(nilFuncs, nonNilValues)
assert.NotNil(t, result2, "MonadAp should return non-nil slice")
assert.Equal(t, 0, len(result2), "MonadAp should return empty slice when functions are nil")
// non-nil functions, nil values
nonNilFuncs := []func(int) string{func(v int) string { return fmt.Sprintf("%d", v) }}
result3 := MonadAp(nonNilFuncs, nilValues)
assert.NotNil(t, result3, "MonadAp should return non-nil slice")
assert.Equal(t, 0, len(result3), "MonadAp should return empty slice when values are nil")
}
// TestNilSlice_Ap verifies that Ap handles nil slices correctly
func TestNilSlice_Ap(t *testing.T) {
var nilValues []int
ap := Ap[string](nilValues)
var nilFuncs []func(int) string
result := ap(nilFuncs)
assert.NotNil(t, result, "Ap should return non-nil slice")
assert.Equal(t, 0, len(result), "Ap should return empty slice for nil inputs")
}
// TestNilSlice_Head verifies that Head handles nil slices correctly
func TestNilSlice_Head(t *testing.T) {
var nilSlice []int
result := Head(nilSlice)
assert.True(t, O.IsNone(result), "Head should return None for nil slice")
}
// TestNilSlice_First verifies that First handles nil slices correctly
func TestNilSlice_First(t *testing.T) {
var nilSlice []int
result := First(nilSlice)
assert.True(t, O.IsNone(result), "First should return None for nil slice")
}
// TestNilSlice_Last verifies that Last handles nil slices correctly
func TestNilSlice_Last(t *testing.T) {
var nilSlice []int
result := Last(nilSlice)
assert.True(t, O.IsNone(result), "Last should return None for nil slice")
}
// TestNilSlice_Tail verifies that Tail handles nil slices correctly
func TestNilSlice_Tail(t *testing.T) {
var nilSlice []int
result := Tail(nilSlice)
assert.True(t, O.IsNone(result), "Tail should return None for nil slice")
}
// TestNilSlice_Flatten verifies that Flatten handles nil slices correctly
func TestNilSlice_Flatten(t *testing.T) {
var nilSlice [][]int
result := Flatten(nilSlice)
assert.NotNil(t, result, "Flatten should return non-nil slice")
assert.Equal(t, 0, len(result), "Flatten should return empty slice for nil input")
}
// TestNilSlice_Lookup verifies that Lookup handles nil slices correctly
func TestNilSlice_Lookup(t *testing.T) {
var nilSlice []int
lookup := Lookup[int](0)
result := lookup(nilSlice)
assert.True(t, O.IsNone(result), "Lookup should return None for nil slice")
}
// TestNilSlice_Size verifies that Size handles nil slices correctly
func TestNilSlice_Size(t *testing.T) {
var nilSlice []int
result := Size(nilSlice)
assert.Equal(t, 0, result, "Size should return 0 for nil slice")
}
// TestNilSlice_MonadPartition verifies that MonadPartition handles nil slices correctly
func TestNilSlice_MonadPartition(t *testing.T) {
var nilSlice []int
result := MonadPartition(nilSlice, func(v int) bool {
return v > 0
})
left := P.Head(result)
right := P.Tail(result)
assert.NotNil(t, left, "MonadPartition left should return non-nil slice")
assert.NotNil(t, right, "MonadPartition right should return non-nil slice")
assert.Equal(t, 0, len(left), "MonadPartition left should be empty for nil input")
assert.Equal(t, 0, len(right), "MonadPartition right should be empty for nil input")
}
// TestNilSlice_Partition verifies that Partition handles nil slices correctly
func TestNilSlice_Partition(t *testing.T) {
var nilSlice []int
partition := Partition(func(v int) bool {
return v > 0
})
result := partition(nilSlice)
left := P.Head(result)
right := P.Tail(result)
assert.NotNil(t, left, "Partition left should return non-nil slice")
assert.NotNil(t, right, "Partition right should return non-nil slice")
assert.Equal(t, 0, len(left), "Partition left should be empty for nil input")
assert.Equal(t, 0, len(right), "Partition right should be empty for nil input")
}
// TestNilSlice_IsNil verifies that IsNil handles nil slices correctly
func TestNilSlice_IsNil(t *testing.T) {
var nilSlice []int
assert.True(t, IsNil(nilSlice), "IsNil should return true for nil slice")
nonNilSlice := []int{}
assert.False(t, IsNil(nonNilSlice), "IsNil should return false for non-nil empty slice")
}
// TestNilSlice_IsNonNil verifies that IsNonNil handles nil slices correctly
func TestNilSlice_IsNonNil(t *testing.T) {
var nilSlice []int
assert.False(t, IsNonNil(nilSlice), "IsNonNil should return false for nil slice")
nonNilSlice := []int{}
assert.True(t, IsNonNil(nonNilSlice), "IsNonNil should return true for non-nil empty slice")
}
// TestNilSlice_Copy verifies that Copy handles nil slices correctly
func TestNilSlice_Copy(t *testing.T) {
var nilSlice []int
result := Copy(nilSlice)
assert.NotNil(t, result, "Copy should return non-nil slice")
assert.Equal(t, 0, len(result), "Copy should return empty slice for nil input")
}
// TestNilSlice_FoldMap verifies that FoldMap handles nil slices correctly
func TestNilSlice_FoldMap(t *testing.T) {
var nilSlice []int
monoid := S.Monoid
foldMap := FoldMap[int](monoid)(func(v int) string {
return fmt.Sprintf("%d", v)
})
result := foldMap(nilSlice)
assert.Equal(t, "", result, "FoldMap should return empty value for nil slice")
}
// TestNilSlice_FoldMapWithIndex verifies that FoldMapWithIndex handles nil slices correctly
func TestNilSlice_FoldMapWithIndex(t *testing.T) {
var nilSlice []int
monoid := S.Monoid
foldMap := FoldMapWithIndex[int](monoid)(func(i int, v int) string {
return fmt.Sprintf("%d:%d", i, v)
})
result := foldMap(nilSlice)
assert.Equal(t, "", result, "FoldMapWithIndex should return empty value for nil slice")
}
// TestNilSlice_Fold verifies that Fold handles nil slices correctly
func TestNilSlice_Fold(t *testing.T) {
var nilSlice []string
monoid := S.Monoid
fold := Fold[string](monoid)
result := fold(nilSlice)
assert.Equal(t, "", result, "Fold should return empty value for nil slice")
}
// TestNilSlice_Concat verifies that Concat handles nil slices correctly
func TestNilSlice_Concat(t *testing.T) {
var nilSlice []int
nonNilSlice := []int{1, 2, 3}
// nil concat non-nil
concat1 := Concat(nonNilSlice)
result1 := concat1(nilSlice)
assert.Equal(t, nonNilSlice, result1, "nil concat non-nil should return non-nil slice")
// non-nil concat nil
concat2 := Concat(nilSlice)
result2 := concat2(nonNilSlice)
assert.Equal(t, nonNilSlice, result2, "non-nil concat nil should return non-nil slice")
// nil concat nil
concat3 := Concat(nilSlice)
result3 := concat3(nilSlice)
assert.Nil(t, result3, "nil concat nil should return nil")
}
// TestNilSlice_MonadFlap verifies that MonadFlap handles nil slices correctly
func TestNilSlice_MonadFlap(t *testing.T) {
var nilSlice []func(int) string
result := MonadFlap(nilSlice, 42)
assert.NotNil(t, result, "MonadFlap should return non-nil slice")
assert.Equal(t, 0, len(result), "MonadFlap should return empty slice for nil input")
}
// TestNilSlice_Flap verifies that Flap handles nil slices correctly
func TestNilSlice_Flap(t *testing.T) {
var nilSlice []func(int) string
flap := Flap[string, int](42)
result := flap(nilSlice)
assert.NotNil(t, result, "Flap should return non-nil slice")
assert.Equal(t, 0, len(result), "Flap should return empty slice for nil input")
}
// TestNilSlice_Reverse verifies that Reverse handles nil slices correctly
func TestNilSlice_Reverse(t *testing.T) {
var nilSlice []int
result := Reverse(nilSlice)
assert.Nil(t, result, "Reverse should return nil for nil slice")
}
// TestNilSlice_Extend verifies that Extend handles nil slices correctly
func TestNilSlice_Extend(t *testing.T) {
var nilSlice []int
extend := Extend(func(as []int) string {
return fmt.Sprintf("%v", as)
})
result := extend(nilSlice)
assert.NotNil(t, result, "Extend should return non-nil slice")
assert.Equal(t, 0, len(result), "Extend should return empty slice for nil input")
}
// TestNilSlice_Empty verifies that Empty creates an empty non-nil slice
func TestNilSlice_Empty(t *testing.T) {
result := Empty[int]()
assert.NotNil(t, result, "Empty should return non-nil slice")
assert.Equal(t, 0, len(result), "Empty should return empty slice")
assert.False(t, IsNil(result), "Empty should not return nil slice")
}
// TestNilSlice_Zero verifies that Zero creates an empty non-nil slice
func TestNilSlice_Zero(t *testing.T) {
result := Zero[int]()
assert.NotNil(t, result, "Zero should return non-nil slice")
assert.Equal(t, 0, len(result), "Zero should return empty slice")
assert.False(t, IsNil(result), "Zero should not return nil slice")
}
// TestNilSlice_ConstNil verifies that ConstNil returns a nil slice
func TestNilSlice_ConstNil(t *testing.T) {
result := ConstNil[int]()
assert.Nil(t, result, "ConstNil should return nil slice")
assert.True(t, IsNil(result), "ConstNil should return nil slice")
}
// TestNilSlice_Of verifies that Of creates a proper singleton slice
func TestNilSlice_Of(t *testing.T) {
result := Of(42)
assert.NotNil(t, result, "Of should return non-nil slice")
assert.Equal(t, 1, len(result), "Of should create slice with one element")
assert.Equal(t, 42, result[0], "Of should set value correctly")
}

View File

@@ -452,5 +452,3 @@ func BenchmarkWithResource(b *testing.B) {
operation(ctx)()
}
}
// Made with Bob

View File

@@ -413,5 +413,3 @@ func isRight[A any](res Result[A]) bool {
func isLeft[A any](res Result[A]) bool {
return result.IsLeft(res)
}
// Made with Bob

View File

@@ -40,7 +40,7 @@
// increment := N.Add(1)
//
// // Compose them (RIGHT-TO-LEFT execution)
// composed := endomorphism.Compose(double, increment)
// composed := endomorphism.MonadCompose(double, increment)
// result := composed(5) // increment(5) then double: (5 + 1) * 2 = 12
//
// // Chain them (LEFT-TO-RIGHT execution)
@@ -61,11 +61,11 @@
// monoid := endomorphism.Monoid[int]()
//
// // Combine multiple endomorphisms (RIGHT-TO-LEFT execution)
// combined := M.ConcatAll(monoid)(
// combined := M.ConcatAll(monoid)([]endomorphism.Endomorphism[int]{
// N.Mul(2), // applied third
// N.Add(1), // applied second
// N.Mul(3), // applied first
// )
// })
// result := combined(5) // (5 * 3) = 15, (15 + 1) = 16, (16 * 2) = 32
//
// # Monad Operations
@@ -87,7 +87,7 @@
// increment := N.Add(1)
//
// // Compose: RIGHT-TO-LEFT (mathematical composition)
// composed := endomorphism.Compose(double, increment)
// composed := endomorphism.MonadCompose(double, increment)
// result1 := composed(5) // increment(5) * 2 = (5 + 1) * 2 = 12
//
// // MonadChain: LEFT-TO-RIGHT (sequential application)

View File

@@ -111,15 +111,19 @@ func MonadCompose[A any](f, g Endomorphism[A]) Endomorphism[A] {
// This is the functor map operation for endomorphisms.
//
// IMPORTANT: Execution order is RIGHT-TO-LEFT:
// - g is applied first to the input
// - ma is applied first to the input
// - f is applied to the result
//
// Note: unlike most other packages where MonadMap takes (fa, f) with the container
// first, here f (the morphism) comes first to match the right-to-left composition
// convention: MonadMap(f, ma) = f ∘ ma.
//
// Parameters:
// - f: The function to map (outer function)
// - g: The endomorphism to map over (inner function)
// - f: The function to map (outer function, applied second)
// - ma: The endomorphism to map over (inner function, applied first)
//
// Returns:
// - A new endomorphism that applies g, then f
// - A new endomorphism that applies ma, then f
//
// Example:
//
@@ -127,8 +131,8 @@ func MonadCompose[A any](f, g Endomorphism[A]) Endomorphism[A] {
// increment := N.Add(1)
// mapped := endomorphism.MonadMap(double, increment)
// // mapped(5) = double(increment(5)) = double(6) = 12
func MonadMap[A any](f, g Endomorphism[A]) Endomorphism[A] {
return MonadCompose(f, g)
func MonadMap[A any](f, ma Endomorphism[A]) Endomorphism[A] {
return MonadCompose(f, ma)
}
// Compose returns a function that composes an endomorphism with another, executing right to left.
@@ -386,3 +390,91 @@ func Join[A any](f Kleisli[A]) Endomorphism[A] {
return f(a)(a)
}
}
// Read captures a value and returns a function that applies endomorphisms to it.
//
// This function implements a "reader" pattern for endomorphisms. It takes a value
// and returns a function that can apply any endomorphism to that captured value.
// This is useful for creating reusable evaluation contexts where you want to apply
// different transformations to the same initial value.
//
// The returned function has the signature func(Endomorphism[A]) A, which means
// it takes an endomorphism and returns the result of applying that endomorphism
// to the captured value.
//
// # Type Parameters
//
// - A: The type of the value being captured and transformed
//
// # Parameters
//
// - a: The value to capture for later transformation
//
// # Returns
//
// - A function that applies endomorphisms to the captured value
//
// # Example - Basic Usage
//
// // Capture a value
// applyTo5 := Read(5)
//
// // Apply different endomorphisms to the same value
// doubled := applyTo5(N.Mul(2)) // 10
// incremented := applyTo5(N.Add(1)) // 6
// squared := applyTo5(func(x int) int { return x * x }) // 25
//
// # Example - Reusable Evaluation Context
//
// type Config struct {
// Timeout int
// Retries int
// }
//
// baseConfig := Config{Timeout: 30, Retries: 3}
// applyToBase := Read(baseConfig)
//
// // Apply different transformations to the same base config
// withLongTimeout := applyToBase(func(c Config) Config {
// c.Timeout = 60
// return c
// })
//
// withMoreRetries := applyToBase(func(c Config) Config {
// c.Retries = 5
// return c
// })
//
// # Example - Testing Different Transformations
//
// // Useful for testing multiple transformations on the same input
// testValue := "hello"
// applyToTest := Read(testValue)
//
// upperCase := applyToTest(strings.ToUpper) // "HELLO"
// withSuffix := applyToTest(func(s string) string {
// return s + " world"
// }) // "hello world"
//
// # Use Cases
//
// 1. **Testing**: Apply multiple transformations to the same test value
// 2. **Configuration**: Create variations of a base configuration
// 3. **Data Processing**: Evaluate different processing pipelines on the same data
// 4. **Benchmarking**: Compare different endomorphisms on the same input
// 5. **Functional Composition**: Build evaluation contexts for composed operations
//
// # Relationship to Other Functions
//
// Read is complementary to other endomorphism operations:
// - Build applies an endomorphism to the zero value
// - Read applies endomorphisms to a specific captured value
// - Reduce applies multiple endomorphisms sequentially
// - ConcatAll composes multiple endomorphisms
//
//go:inline
func Read[A any](a A) func(Endomorphism[A]) A {
return func(f Endomorphism[A]) A {
return f(a)
}
}

View File

@@ -1071,3 +1071,226 @@ func TestReduceWithBuild(t *testing.T) {
assert.NotEqual(t, reduceResult, buildResult, "Reduce and Build(ConcatAll) produce different results due to execution order")
}
// TestRead tests the Read function
func TestRead(t *testing.T) {
t.Run("applies endomorphism to captured value", func(t *testing.T) {
applyTo5 := Read(5)
result := applyTo5(double)
assert.Equal(t, 10, result, "Read should apply double to captured value 5")
result2 := applyTo5(increment)
assert.Equal(t, 6, result2, "Read should apply increment to captured value 5")
result3 := applyTo5(square)
assert.Equal(t, 25, result3, "Read should apply square to captured value 5")
})
t.Run("captures value for reuse", func(t *testing.T) {
applyTo10 := Read(10)
// Apply multiple different endomorphisms to the same captured value
doubled := applyTo10(double)
incremented := applyTo10(increment)
negated := applyTo10(negate)
assert.Equal(t, 20, doubled, "Should double 10")
assert.Equal(t, 11, incremented, "Should increment 10")
assert.Equal(t, -10, negated, "Should negate 10")
})
t.Run("works with identity", func(t *testing.T) {
applyTo42 := Read(42)
result := applyTo42(Identity[int]())
assert.Equal(t, 42, result, "Read with identity should return original value")
})
t.Run("works with composed endomorphisms", func(t *testing.T) {
applyTo5 := Read(5)
// Compose: double then increment (RIGHT-TO-LEFT)
composed := MonadCompose(increment, double)
result := applyTo5(composed)
assert.Equal(t, 11, result, "Read should work with composed endomorphisms: (5 * 2) + 1 = 11")
})
t.Run("works with chained endomorphisms", func(t *testing.T) {
applyTo5 := Read(5)
// Chain: double then increment (LEFT-TO-RIGHT)
chained := MonadChain(double, increment)
result := applyTo5(chained)
assert.Equal(t, 11, result, "Read should work with chained endomorphisms: (5 * 2) + 1 = 11")
})
t.Run("works with ConcatAll", func(t *testing.T) {
applyTo5 := Read(5)
// ConcatAll composes RIGHT-TO-LEFT
combined := ConcatAll([]Endomorphism[int]{double, increment, square})
result := applyTo5(combined)
// Execution: square(5) = 25, increment(25) = 26, double(26) = 52
assert.Equal(t, 52, result, "Read should work with ConcatAll")
})
t.Run("works with different types", func(t *testing.T) {
// Test with string
applyToHello := Read("hello")
toUpper := func(s string) string { return s + " WORLD" }
result := applyToHello(toUpper)
assert.Equal(t, "hello WORLD", result, "Read should work with strings")
// Test with struct
type Point struct {
X, Y int
}
applyToPoint := Read(Point{X: 3, Y: 4})
scaleX := func(p Point) Point {
p.X *= 2
return p
}
result2 := applyToPoint(scaleX)
assert.Equal(t, Point{X: 6, Y: 4}, result2, "Read should work with structs")
})
t.Run("creates independent evaluation contexts", func(t *testing.T) {
applyTo5 := Read(5)
applyTo10 := Read(10)
// Same endomorphism, different contexts
result5 := applyTo5(double)
result10 := applyTo10(double)
assert.Equal(t, 10, result5, "First context should double 5")
assert.Equal(t, 20, result10, "Second context should double 10")
})
t.Run("useful for testing transformations", func(t *testing.T) {
testValue := 100
applyToTest := Read(testValue)
// Test multiple transformations on the same value
transformations := []struct {
name string
endo Endomorphism[int]
expected int
}{
{"double", double, 200},
{"increment", increment, 101},
{"negate", negate, -100},
{"square", square, 10000},
}
for _, tt := range transformations {
t.Run(tt.name, func(t *testing.T) {
result := applyToTest(tt.endo)
assert.Equal(t, tt.expected, result)
})
}
})
t.Run("works with monoid operations", func(t *testing.T) {
applyTo5 := Read(5)
// Use monoid to combine endomorphisms
combined := M.ConcatAll(Monoid[int]())([]Endomorphism[int]{
double,
increment,
})
result := applyTo5(combined)
// RIGHT-TO-LEFT: increment(5) = 6, double(6) = 12
assert.Equal(t, 12, result, "Read should work with monoid operations")
})
t.Run("configuration example", func(t *testing.T) {
type Config struct {
Timeout int
Retries int
}
baseConfig := Config{Timeout: 30, Retries: 3}
applyToBase := Read(baseConfig)
withLongTimeout := func(c Config) Config {
c.Timeout = 60
return c
}
withMoreRetries := func(c Config) Config {
c.Retries = 5
return c
}
result1 := applyToBase(withLongTimeout)
assert.Equal(t, Config{Timeout: 60, Retries: 3}, result1)
result2 := applyToBase(withMoreRetries)
assert.Equal(t, Config{Timeout: 30, Retries: 5}, result2)
// Original is unchanged
result3 := applyToBase(Identity[Config]())
assert.Equal(t, baseConfig, result3)
})
}
// TestReadWithBuild tests the relationship between Read and Build
func TestReadWithBuild(t *testing.T) {
t.Run("Read applies to specific value, Build to zero value", func(t *testing.T) {
endo := double
// Build applies to zero value
builtResult := Build(endo)
assert.Equal(t, 0, builtResult, "Build should apply to zero value: 0 * 2 = 0")
// Read applies to specific value
readResult := Read(5)(endo)
assert.Equal(t, 10, readResult, "Read should apply to captured value: 5 * 2 = 10")
})
t.Run("Read can evaluate Build results", func(t *testing.T) {
// Build an endomorphism
builder := ConcatAll([]Endomorphism[int]{double, increment})
// Apply it to zero value
builtValue := Build(builder)
// RIGHT-TO-LEFT: increment(0) = 1, double(1) = 2
assert.Equal(t, 2, builtValue)
// Now use Read to apply the same builder to a different value
readValue := Read(5)(builder)
// RIGHT-TO-LEFT: increment(5) = 6, double(6) = 12
assert.Equal(t, 12, readValue)
})
}
// BenchmarkRead benchmarks the Read function
func BenchmarkRead(b *testing.B) {
applyTo5 := Read(5)
b.Run("simple endomorphism", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = applyTo5(double)
}
})
b.Run("composed endomorphism", func(b *testing.B) {
composed := MonadCompose(double, increment)
for i := 0; i < b.N; i++ {
_ = applyTo5(composed)
}
})
b.Run("ConcatAll endomorphism", func(b *testing.B) {
combined := ConcatAll([]Endomorphism[int]{double, increment, square})
for i := 0; i < b.N; i++ {
_ = applyTo5(combined)
}
})
}

View File

@@ -144,8 +144,8 @@ func Semigroup[A any]() S.Semigroup[Endomorphism[A]] {
// square := func(x int) int { return x * x }
//
// // Combine multiple endomorphisms (RIGHT-TO-LEFT execution)
// combined := M.ConcatAll(monoid)(double, increment, square)
// result := combined(5) // square(increment(double(5))) = square(increment(10)) = square(11) = 121
// combined := M.ConcatAll(monoid)([]Endomorphism[int]{double, increment, square})
// result := combined(5) // double(increment(square(5))) = double(increment(25)) = double(26) = 52
func Monoid[A any]() M.Monoid[Endomorphism[A]] {
return M.MakeMonoid(MonadCompose[A], Identity[A]())
}

View File

@@ -41,20 +41,22 @@ type (
// It's a function from A to Endomorphism[A], used for composing endomorphic operations.
Kleisli[A any] = func(A) Endomorphism[A]
// Operator represents a transformation from one endomorphism to another.
// Operator represents a higher-order transformation on endomorphisms of the same type.
//
// An Operator takes an endomorphism on type A and produces an endomorphism on type B.
// This is useful for lifting operations or transforming endomorphisms in a generic way.
// An Operator takes an endomorphism on type A and produces another endomorphism on type A.
// Since Operator[A] = Endomorphism[Endomorphism[A]] = func(func(A)A) func(A)A,
// both the input and output endomorphisms operate on the same type A.
//
// This is the return type of curried operations such as Compose, Map, and Chain.
//
// Example:
//
// // An operator that converts an int endomorphism to a string endomorphism
// intToString := func(f endomorphism.Endomorphism[int]) endomorphism.Endomorphism[string] {
// return func(s string) string {
// n, _ := strconv.Atoi(s)
// result := f(n)
// return strconv.Itoa(result)
// }
// // An operator that applies any endomorphism twice
// var applyTwice endomorphism.Operator[int] = func(f endomorphism.Endomorphism[int]) endomorphism.Endomorphism[int] {
// return func(x int) int { return f(f(x)) }
// }
// double := N.Mul(2)
// result := applyTwice(double) // double ∘ double
// // result(5) = double(double(5)) = double(10) = 20
Operator[A any] = Endomorphism[Endomorphism[A]]
)

View File

@@ -0,0 +1,544 @@
// 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 record
import (
"fmt"
"testing"
O "github.com/IBM/fp-go/v2/option"
P "github.com/IBM/fp-go/v2/pair"
SG "github.com/IBM/fp-go/v2/semigroup"
S "github.com/IBM/fp-go/v2/string"
"github.com/stretchr/testify/assert"
)
// TestNilMap_IsEmpty verifies that IsEmpty handles nil maps correctly
func TestNilMap_IsEmpty(t *testing.T) {
var nilMap Record[string, int]
assert.True(t, IsEmpty(nilMap), "nil map should be empty")
}
// TestNilMap_IsNonEmpty verifies that IsNonEmpty handles nil maps correctly
func TestNilMap_IsNonEmpty(t *testing.T) {
var nilMap Record[string, int]
assert.False(t, IsNonEmpty(nilMap), "nil map should not be non-empty")
}
// TestNilMap_Keys verifies that Keys handles nil maps correctly
func TestNilMap_Keys(t *testing.T) {
var nilMap Record[string, int]
keys := Keys(nilMap)
assert.NotNil(t, keys, "Keys should return non-nil slice")
assert.Equal(t, 0, len(keys), "Keys should return empty slice for nil map")
}
// TestNilMap_Values verifies that Values handles nil maps correctly
func TestNilMap_Values(t *testing.T) {
var nilMap Record[string, int]
values := Values(nilMap)
assert.NotNil(t, values, "Values should return non-nil slice")
assert.Equal(t, 0, len(values), "Values should return empty slice for nil map")
}
// TestNilMap_Collect verifies that Collect handles nil maps correctly
func TestNilMap_Collect(t *testing.T) {
var nilMap Record[string, int]
collector := Collect(func(k string, v int) string {
return fmt.Sprintf("%s=%d", k, v)
})
result := collector(nilMap)
assert.NotNil(t, result, "Collect should return non-nil slice")
assert.Equal(t, 0, len(result), "Collect should return empty slice for nil map")
}
// TestNilMap_Reduce verifies that Reduce handles nil maps correctly
func TestNilMap_Reduce(t *testing.T) {
var nilMap Record[string, int]
reducer := Reduce[string](func(acc int, v int) int {
return acc + v
}, 10)
result := reducer(nilMap)
assert.Equal(t, 10, result, "Reduce should return initial value for nil map")
}
// TestNilMap_ReduceWithIndex verifies that ReduceWithIndex handles nil maps correctly
func TestNilMap_ReduceWithIndex(t *testing.T) {
var nilMap Record[string, int]
reducer := ReduceWithIndex(func(k string, acc int, v int) int {
return acc + v
}, 10)
result := reducer(nilMap)
assert.Equal(t, 10, result, "ReduceWithIndex should return initial value for nil map")
}
// TestNilMap_ReduceRef verifies that ReduceRef handles nil maps correctly
func TestNilMap_ReduceRef(t *testing.T) {
var nilMap Record[string, int]
reducer := ReduceRef[string](func(acc int, v *int) int {
return acc + *v
}, 10)
result := reducer(nilMap)
assert.Equal(t, 10, result, "ReduceRef should return initial value for nil map")
}
// TestNilMap_ReduceRefWithIndex verifies that ReduceRefWithIndex handles nil maps correctly
func TestNilMap_ReduceRefWithIndex(t *testing.T) {
var nilMap Record[string, int]
reducer := ReduceRefWithIndex(func(k string, acc int, v *int) int {
return acc + *v
}, 10)
result := reducer(nilMap)
assert.Equal(t, 10, result, "ReduceRefWithIndex should return initial value for nil map")
}
// TestNilMap_MonadMap verifies that MonadMap handles nil maps correctly
func TestNilMap_MonadMap(t *testing.T) {
var nilMap Record[string, int]
result := MonadMap(nilMap, func(v int) string {
return fmt.Sprintf("%d", v)
})
assert.NotNil(t, result, "MonadMap should return non-nil map")
assert.Equal(t, 0, len(result), "MonadMap should return empty map for nil input")
}
// TestNilMap_MonadMapWithIndex verifies that MonadMapWithIndex handles nil maps correctly
func TestNilMap_MonadMapWithIndex(t *testing.T) {
var nilMap Record[string, int]
result := MonadMapWithIndex(nilMap, func(k string, v int) string {
return fmt.Sprintf("%s=%d", k, v)
})
assert.NotNil(t, result, "MonadMapWithIndex should return non-nil map")
assert.Equal(t, 0, len(result), "MonadMapWithIndex should return empty map for nil input")
}
// TestNilMap_MonadMapRefWithIndex verifies that MonadMapRefWithIndex handles nil maps correctly
func TestNilMap_MonadMapRefWithIndex(t *testing.T) {
var nilMap Record[string, int]
result := MonadMapRefWithIndex(nilMap, func(k string, v *int) string {
return fmt.Sprintf("%s=%d", k, *v)
})
assert.NotNil(t, result, "MonadMapRefWithIndex should return non-nil map")
assert.Equal(t, 0, len(result), "MonadMapRefWithIndex should return empty map for nil input")
}
// TestNilMap_MonadMapRef verifies that MonadMapRef handles nil maps correctly
func TestNilMap_MonadMapRef(t *testing.T) {
var nilMap Record[string, int]
result := MonadMapRef(nilMap, func(v *int) string {
return fmt.Sprintf("%d", *v)
})
assert.NotNil(t, result, "MonadMapRef should return non-nil map")
assert.Equal(t, 0, len(result), "MonadMapRef should return empty map for nil input")
}
// TestNilMap_Map verifies that Map handles nil maps correctly
func TestNilMap_Map(t *testing.T) {
var nilMap Record[string, int]
mapper := Map[string](func(v int) string {
return fmt.Sprintf("%d", v)
})
result := mapper(nilMap)
assert.NotNil(t, result, "Map should return non-nil map")
assert.Equal(t, 0, len(result), "Map should return empty map for nil input")
}
// TestNilMap_MapRef verifies that MapRef handles nil maps correctly
func TestNilMap_MapRef(t *testing.T) {
var nilMap Record[string, int]
mapper := MapRef[string](func(v *int) string {
return fmt.Sprintf("%d", *v)
})
result := mapper(nilMap)
assert.NotNil(t, result, "MapRef should return non-nil map")
assert.Equal(t, 0, len(result), "MapRef should return empty map for nil input")
}
// TestNilMap_MapWithIndex verifies that MapWithIndex handles nil maps correctly
func TestNilMap_MapWithIndex(t *testing.T) {
var nilMap Record[string, int]
mapper := MapWithIndex[string](func(k string, v int) string {
return fmt.Sprintf("%s=%d", k, v)
})
result := mapper(nilMap)
assert.NotNil(t, result, "MapWithIndex should return non-nil map")
assert.Equal(t, 0, len(result), "MapWithIndex should return empty map for nil input")
}
// TestNilMap_MapRefWithIndex verifies that MapRefWithIndex handles nil maps correctly
func TestNilMap_MapRefWithIndex(t *testing.T) {
var nilMap Record[string, int]
mapper := MapRefWithIndex[string](func(k string, v *int) string {
return fmt.Sprintf("%s=%d", k, *v)
})
result := mapper(nilMap)
assert.NotNil(t, result, "MapRefWithIndex should return non-nil map")
assert.Equal(t, 0, len(result), "MapRefWithIndex should return empty map for nil input")
}
// TestNilMap_Lookup verifies that Lookup handles nil maps correctly
func TestNilMap_Lookup(t *testing.T) {
var nilMap Record[string, int]
lookup := Lookup[int]("key")
result := lookup(nilMap)
assert.True(t, O.IsNone(result), "Lookup should return None for nil map")
}
// TestNilMap_MonadLookup verifies that MonadLookup handles nil maps correctly
func TestNilMap_MonadLookup(t *testing.T) {
var nilMap Record[string, int]
result := MonadLookup(nilMap, "key")
assert.True(t, O.IsNone(result), "MonadLookup should return None for nil map")
}
// TestNilMap_Has verifies that Has handles nil maps correctly
func TestNilMap_Has(t *testing.T) {
var nilMap Record[string, int]
result := Has("key", nilMap)
assert.False(t, result, "Has should return false for nil map")
}
// TestNilMap_Union verifies that Union handles nil maps correctly
func TestNilMap_Union(t *testing.T) {
var nilMap Record[string, int]
nonNilMap := Record[string, int]{"a": 1, "b": 2}
semigroup := SG.Last[int]()
union := Union[string](semigroup)
// nil union non-nil
result1 := union(nonNilMap)(nilMap)
assert.Equal(t, nonNilMap, result1, "nil union non-nil should return non-nil map")
// non-nil union nil
result2 := union(nilMap)(nonNilMap)
assert.Equal(t, nonNilMap, result2, "non-nil union nil should return non-nil map")
// nil union nil - returns nil when both inputs are nil (optimization)
result3 := union(nilMap)(nilMap)
assert.Nil(t, result3, "nil union nil returns nil")
}
// TestNilMap_Merge verifies that Merge handles nil maps correctly
func TestNilMap_Merge(t *testing.T) {
var nilMap Record[string, int]
nonNilMap := Record[string, int]{"a": 1, "b": 2}
// nil merge non-nil
result1 := Merge(nonNilMap)(nilMap)
assert.Equal(t, nonNilMap, result1, "nil merge non-nil should return non-nil map")
// non-nil merge nil
result2 := Merge(nilMap)(nonNilMap)
assert.Equal(t, nonNilMap, result2, "non-nil merge nil should return non-nil map")
// nil merge nil - returns nil when both inputs are nil (optimization)
result3 := Merge(nilMap)(nilMap)
assert.Nil(t, result3, "nil merge nil returns nil")
}
// TestNilMap_Size verifies that Size handles nil maps correctly
func TestNilMap_Size(t *testing.T) {
var nilMap Record[string, int]
result := Size(nilMap)
assert.Equal(t, 0, result, "Size should return 0 for nil map")
}
// TestNilMap_ToArray verifies that ToArray handles nil maps correctly
func TestNilMap_ToArray(t *testing.T) {
var nilMap Record[string, int]
result := ToArray(nilMap)
assert.NotNil(t, result, "ToArray should return non-nil slice")
assert.Equal(t, 0, len(result), "ToArray should return empty slice for nil map")
}
// TestNilMap_ToEntries verifies that ToEntries handles nil maps correctly
func TestNilMap_ToEntries(t *testing.T) {
var nilMap Record[string, int]
result := ToEntries(nilMap)
assert.NotNil(t, result, "ToEntries should return non-nil slice")
assert.Equal(t, 0, len(result), "ToEntries should return empty slice for nil map")
}
// TestNilMap_UpsertAt verifies that UpsertAt handles nil maps correctly
func TestNilMap_UpsertAt(t *testing.T) {
var nilMap Record[string, int]
upsert := UpsertAt("key", 42)
result := upsert(nilMap)
assert.NotNil(t, result, "UpsertAt should return non-nil map")
assert.Equal(t, 1, len(result), "UpsertAt should create map with one entry")
assert.Equal(t, 42, result["key"], "UpsertAt should insert value correctly")
}
// TestNilMap_DeleteAt verifies that DeleteAt handles nil maps correctly
func TestNilMap_DeleteAt(t *testing.T) {
var nilMap Record[string, int]
deleteFunc := DeleteAt[string, int]("key")
result := deleteFunc(nilMap)
assert.NotNil(t, result, "DeleteAt should return non-nil map")
assert.Equal(t, 0, len(result), "DeleteAt should return empty map for nil input")
}
// TestNilMap_Filter verifies that Filter handles nil maps correctly
func TestNilMap_Filter(t *testing.T) {
var nilMap Record[string, int]
filter := Filter[string, int](func(k string) bool {
return true
})
result := filter(nilMap)
assert.NotNil(t, result, "Filter should return non-nil map")
assert.Equal(t, 0, len(result), "Filter should return empty map for nil input")
}
// TestNilMap_FilterWithIndex verifies that FilterWithIndex handles nil maps correctly
func TestNilMap_FilterWithIndex(t *testing.T) {
var nilMap Record[string, int]
filter := FilterWithIndex[string, int](func(k string, v int) bool {
return true
})
result := filter(nilMap)
assert.NotNil(t, result, "FilterWithIndex should return non-nil map")
assert.Equal(t, 0, len(result), "FilterWithIndex should return empty map for nil input")
}
// TestNilMap_IsNil verifies that IsNil handles nil maps correctly
func TestNilMap_IsNil(t *testing.T) {
var nilMap Record[string, int]
assert.True(t, IsNil(nilMap), "IsNil should return true for nil map")
nonNilMap := Record[string, int]{}
assert.False(t, IsNil(nonNilMap), "IsNil should return false for non-nil empty map")
}
// TestNilMap_IsNonNil verifies that IsNonNil handles nil maps correctly
func TestNilMap_IsNonNil(t *testing.T) {
var nilMap Record[string, int]
assert.False(t, IsNonNil(nilMap), "IsNonNil should return false for nil map")
nonNilMap := Record[string, int]{}
assert.True(t, IsNonNil(nonNilMap), "IsNonNil should return true for non-nil empty map")
}
// TestNilMap_MonadChainWithIndex verifies that MonadChainWithIndex handles nil maps correctly
func TestNilMap_MonadChainWithIndex(t *testing.T) {
var nilMap Record[string, int]
monoid := MergeMonoid[string, string]()
result := MonadChainWithIndex(monoid, nilMap, func(k string, v int) Record[string, string] {
return Record[string, string]{k: fmt.Sprintf("%d", v)}
})
assert.NotNil(t, result, "MonadChainWithIndex should return non-nil map")
assert.Equal(t, 0, len(result), "MonadChainWithIndex should return empty map for nil input")
}
// TestNilMap_MonadChain verifies that MonadChain handles nil maps correctly
func TestNilMap_MonadChain(t *testing.T) {
var nilMap Record[string, int]
monoid := MergeMonoid[string, string]()
result := MonadChain(monoid, nilMap, func(v int) Record[string, string] {
return Record[string, string]{"key": fmt.Sprintf("%d", v)}
})
assert.NotNil(t, result, "MonadChain should return non-nil map")
assert.Equal(t, 0, len(result), "MonadChain should return empty map for nil input")
}
// TestNilMap_ChainWithIndex verifies that ChainWithIndex handles nil maps correctly
func TestNilMap_ChainWithIndex(t *testing.T) {
var nilMap Record[string, int]
monoid := MergeMonoid[string, string]()
chain := ChainWithIndex[int, string](monoid)(func(k string, v int) Record[string, string] {
return Record[string, string]{k: fmt.Sprintf("%d", v)}
})
result := chain(nilMap)
assert.NotNil(t, result, "ChainWithIndex should return non-nil map")
assert.Equal(t, 0, len(result), "ChainWithIndex should return empty map for nil input")
}
// TestNilMap_Chain verifies that Chain handles nil maps correctly
func TestNilMap_Chain(t *testing.T) {
var nilMap Record[string, int]
monoid := MergeMonoid[string, string]()
chain := Chain[int, string](monoid)(func(v int) Record[string, string] {
return Record[string, string]{"key": fmt.Sprintf("%d", v)}
})
result := chain(nilMap)
assert.NotNil(t, result, "Chain should return non-nil map")
assert.Equal(t, 0, len(result), "Chain should return empty map for nil input")
}
// TestNilMap_Flatten verifies that Flatten handles nil maps correctly
func TestNilMap_Flatten(t *testing.T) {
var nilMap Record[string, Record[string, int]]
monoid := MergeMonoid[string, int]()
flatten := Flatten[string, int](monoid)
result := flatten(nilMap)
assert.NotNil(t, result, "Flatten should return non-nil map")
assert.Equal(t, 0, len(result), "Flatten should return empty map for nil input")
}
// TestNilMap_Copy verifies that Copy handles nil maps correctly
func TestNilMap_Copy(t *testing.T) {
var nilMap Record[string, int]
result := Copy(nilMap)
assert.NotNil(t, result, "Copy should return non-nil map")
assert.Equal(t, 0, len(result), "Copy should return empty map for nil input")
}
// TestNilMap_Clone verifies that Clone handles nil maps correctly
func TestNilMap_Clone(t *testing.T) {
var nilMap Record[string, int]
clone := Clone[string, int](func(v int) int { return v * 2 })
result := clone(nilMap)
assert.NotNil(t, result, "Clone should return non-nil map")
assert.Equal(t, 0, len(result), "Clone should return empty map for nil input")
}
// TestNilMap_FromArray verifies that FromArray handles nil/empty arrays correctly
func TestNilMap_FromArray(t *testing.T) {
semigroup := SG.Last[int]()
fromArray := FromArray[string, int](semigroup)
// Test with nil slice
var nilSlice Entries[string, int]
result1 := fromArray(nilSlice)
assert.NotNil(t, result1, "FromArray should return non-nil map for nil slice")
assert.Equal(t, 0, len(result1), "FromArray should return empty map for nil slice")
// Test with empty slice
emptySlice := Entries[string, int]{}
result2 := fromArray(emptySlice)
assert.NotNil(t, result2, "FromArray should return non-nil map for empty slice")
assert.Equal(t, 0, len(result2), "FromArray should return empty map for empty slice")
}
// TestNilMap_MonadAp verifies that MonadAp handles nil maps correctly
func TestNilMap_MonadAp(t *testing.T) {
var nilFab Record[string, func(int) string]
var nilFa Record[string, int]
monoid := MergeMonoid[string, string]()
// nil functions, nil values
result1 := MonadAp(monoid, nilFab, nilFa)
assert.NotNil(t, result1, "MonadAp should return non-nil map")
assert.Equal(t, 0, len(result1), "MonadAp should return empty map for nil inputs")
// nil functions, non-nil values
nonNilFa := Record[string, int]{"a": 1}
result2 := MonadAp(monoid, nilFab, nonNilFa)
assert.NotNil(t, result2, "MonadAp should return non-nil map")
assert.Equal(t, 0, len(result2), "MonadAp should return empty map when functions are nil")
// non-nil functions, nil values
nonNilFab := Record[string, func(int) string]{"a": func(v int) string { return fmt.Sprintf("%d", v) }}
result3 := MonadAp(monoid, nonNilFab, nilFa)
assert.NotNil(t, result3, "MonadAp should return non-nil map")
assert.Equal(t, 0, len(result3), "MonadAp should return empty map when values are nil")
}
// TestNilMap_Of verifies that Of creates a proper singleton map
func TestNilMap_Of(t *testing.T) {
result := Of("key", 42)
assert.NotNil(t, result, "Of should return non-nil map")
assert.Equal(t, 1, len(result), "Of should create map with one entry")
assert.Equal(t, 42, result["key"], "Of should set value correctly")
}
// TestNilMap_FromEntries verifies that FromEntries handles nil/empty slices correctly
func TestNilMap_FromEntries(t *testing.T) {
// Test with nil slice
var nilSlice Entries[string, int]
result1 := FromEntries(nilSlice)
assert.NotNil(t, result1, "FromEntries should return non-nil map for nil slice")
assert.Equal(t, 0, len(result1), "FromEntries should return empty map for nil slice")
// Test with empty slice
emptySlice := Entries[string, int]{}
result2 := FromEntries(emptySlice)
assert.NotNil(t, result2, "FromEntries should return non-nil map for empty slice")
assert.Equal(t, 0, len(result2), "FromEntries should return empty map for empty slice")
// Test with actual entries
entries := Entries[string, int]{
P.MakePair("a", 1),
P.MakePair("b", 2),
}
result3 := FromEntries(entries)
assert.NotNil(t, result3, "FromEntries should return non-nil map")
assert.Equal(t, 2, len(result3), "FromEntries should create map with correct size")
assert.Equal(t, 1, result3["a"], "FromEntries should set values correctly")
assert.Equal(t, 2, result3["b"], "FromEntries should set values correctly")
}
// TestNilMap_Singleton verifies that Singleton creates a proper singleton map
func TestNilMap_Singleton(t *testing.T) {
result := Singleton("key", 42)
assert.NotNil(t, result, "Singleton should return non-nil map")
assert.Equal(t, 1, len(result), "Singleton should create map with one entry")
assert.Equal(t, 42, result["key"], "Singleton should set value correctly")
}
// TestNilMap_Empty verifies that Empty creates an empty non-nil map
func TestNilMap_Empty(t *testing.T) {
result := Empty[string, int]()
assert.NotNil(t, result, "Empty should return non-nil map")
assert.Equal(t, 0, len(result), "Empty should return empty map")
assert.False(t, IsNil(result), "Empty should not return nil map")
}
// TestNilMap_ConstNil verifies that ConstNil returns a nil map
func TestNilMap_ConstNil(t *testing.T) {
result := ConstNil[string, int]()
assert.Nil(t, result, "ConstNil should return nil map")
assert.True(t, IsNil(result), "ConstNil should return nil map")
}
// TestNilMap_FoldMap verifies that FoldMap handles nil maps correctly
func TestNilMap_FoldMap(t *testing.T) {
var nilMap Record[string, int]
monoid := S.Monoid
foldMap := FoldMap[string, int, string](monoid)(func(v int) string {
return fmt.Sprintf("%d", v)
})
result := foldMap(nilMap)
assert.Equal(t, "", result, "FoldMap should return empty value for nil map")
}
// TestNilMap_FoldMapWithIndex verifies that FoldMapWithIndex handles nil maps correctly
func TestNilMap_FoldMapWithIndex(t *testing.T) {
var nilMap Record[string, int]
monoid := S.Monoid
foldMap := FoldMapWithIndex[string, int, string](monoid)(func(k string, v int) string {
return fmt.Sprintf("%s=%d", k, v)
})
result := foldMap(nilMap)
assert.Equal(t, "", result, "FoldMapWithIndex should return empty value for nil map")
}
// TestNilMap_Fold verifies that Fold handles nil maps correctly
func TestNilMap_Fold(t *testing.T) {
var nilMap Record[string, string]
monoid := S.Monoid
fold := Fold[string](monoid)
result := fold(nilMap)
assert.Equal(t, "", result, "Fold should return empty value for nil map")
}
// TestNilMap_MonadFlap verifies that MonadFlap handles nil maps correctly
func TestNilMap_MonadFlap(t *testing.T) {
var nilMap Record[string, func(int) string]
result := MonadFlap(nilMap, 42)
assert.NotNil(t, result, "MonadFlap should return non-nil map")
assert.Equal(t, 0, len(result), "MonadFlap should return empty map for nil input")
}