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

fix: modernize codebase

Signed-off-by: Dr. Carsten Leue <carsten.leue@de.ibm.com>
This commit is contained in:
Dr. Carsten Leue
2025-11-15 17:00:22 +01:00
parent ab868315d4
commit ed108812d6
51 changed files with 896 additions and 374 deletions

View File

@@ -69,7 +69,7 @@ func main() {
none := option.None[int]()
// Map over values
doubled := option.Map(func(x int) int { return x * 2 })(some)
doubled := option.Map(N.Mul(2))(some)
fmt.Println(option.GetOrElse(0)(doubled)) // Output: 84
// Chain operations
@@ -187,7 +187,7 @@ Monadic operations for `Pair` now operate on the **second argument** to align wi
```go
// Operations on first element
pair := MakePair(1, "hello")
result := Map(func(x int) int { return x * 2 })(pair) // Pair(2, "hello")
result := Map(N.Mul(2))(pair) // Pair(2, "hello")
```
**V2:**
@@ -204,7 +204,7 @@ The `Compose` function for endomorphisms now follows **mathematical function com
**V1:**
```go
// Compose executed left-to-right
double := func(x int) int { return x * 2 }
double := N.Mul(2)
increment := func(x int) int { return x + 1 }
composed := Compose(double, increment)
result := composed(5) // (5 * 2) + 1 = 11
@@ -213,7 +213,7 @@ result := composed(5) // (5 * 2) + 1 = 11
**V2:**
```go
// Compose executes RIGHT-TO-LEFT (mathematical composition)
double := func(x int) int { return x * 2 }
double := N.Mul(2)
increment := func(x int) int { return x + 1 }
composed := Compose(double, increment)
result := composed(5) // (5 + 1) * 2 = 12
@@ -368,7 +368,7 @@ If you're using `Pair`, update operations to work on the second element:
```go
pair := MakePair(42, "data")
// Map operates on first element
result := Map(func(x int) int { return x * 2 })(pair)
result := Map(N.Mul(2))(pair)
```
**After (V2):**

View File

@@ -76,7 +76,7 @@ func MapWithIndex[A, B any](f func(int, A) B) Operator[A, B] {
//
// Example:
//
// double := array.Map(func(x int) int { return x * 2 })
// double := array.Map(N.Mul(2))
// result := double([]int{1, 2, 3}) // [2, 4, 6]
//
//go:inline

View File

@@ -36,7 +36,7 @@
// generated := array.MakeBy(5, func(i int) int { return i * 2 })
//
// // Transforming arrays
// doubled := array.Map(func(x int) int { return x * 2 })(arr)
// doubled := array.Map(N.Mul(2))(arr)
// filtered := array.Filter(func(x int) bool { return x > 2 })(arr)
//
// // Combining arrays
@@ -50,7 +50,7 @@
// numbers := []int{1, 2, 3, 4, 5}
//
// // Map transforms each element
// doubled := array.Map(func(x int) int { return x * 2 })(numbers)
// doubled := array.Map(N.Mul(2))(numbers)
// // Result: [2, 4, 6, 8, 10]
//
// // Filter keeps elements matching a predicate

View File

@@ -18,6 +18,7 @@ package array
import (
"testing"
N "github.com/IBM/fp-go/v2/number"
"github.com/stretchr/testify/assert"
)
@@ -243,7 +244,7 @@ func TestSliceComposition(t *testing.T) {
t.Run("slice then map", func(t *testing.T) {
sliced := Slice[int](2, 5)(data)
mapped := Map(func(x int) int { return x * 2 })(sliced)
mapped := Map(N.Mul(2))(sliced)
assert.Equal(t, []int{4, 6, 8}, mapped)
})

View File

@@ -382,7 +382,7 @@ func BenchmarkToString(b *testing.B) {
data := []byte("Hello, World!")
b.Run("small", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = ToString(data)
}
})
@@ -393,7 +393,7 @@ func BenchmarkToString(b *testing.B) {
large[i] = byte(i % 256)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = ToString(large)
}
})
@@ -402,7 +402,7 @@ func BenchmarkToString(b *testing.B) {
func BenchmarkSize(b *testing.B) {
data := []byte("Hello, World!")
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Size(data)
}
}
@@ -412,7 +412,7 @@ func BenchmarkMonoidConcat(b *testing.B) {
c := []byte(" World")
b.Run("small slices", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Monoid.Concat(a, c)
}
})
@@ -421,7 +421,7 @@ func BenchmarkMonoidConcat(b *testing.B) {
large1 := make([]byte, 10000)
large2 := make([]byte, 10000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Monoid.Concat(large1, large2)
}
})
@@ -436,7 +436,7 @@ func BenchmarkConcatAll(b *testing.B) {
}
b.Run("few slices", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = ConcatAll(slices...)
}
})
@@ -447,7 +447,7 @@ func BenchmarkConcatAll(b *testing.B) {
many[i] = []byte{byte(i)}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = ConcatAll(many...)
}
})
@@ -458,13 +458,13 @@ func BenchmarkOrdCompare(b *testing.B) {
c := []byte("abd")
b.Run("equal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Ord.Compare(a, a)
}
})
b.Run("different", func(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Ord.Compare(a, c)
}
})
@@ -474,7 +474,7 @@ func BenchmarkOrdCompare(b *testing.B) {
large2 := make([]byte, 10000)
large2[9999] = 1
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Ord.Compare(large1, large2)
}
})

View File

@@ -24,6 +24,7 @@ import (
E "github.com/IBM/fp-go/v2/either"
F "github.com/IBM/fp-go/v2/function"
IOE "github.com/IBM/fp-go/v2/ioeither"
N "github.com/IBM/fp-go/v2/number"
)
var (
@@ -37,21 +38,21 @@ var (
// Benchmark core constructors
func BenchmarkLeft(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Left[int](benchErr)
}
}
func BenchmarkRight(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Right(42)
}
}
func BenchmarkOf(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Of(42)
}
}
@@ -60,7 +61,7 @@ func BenchmarkFromEither_Right(b *testing.B) {
either := E.Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = FromEither(either)
}
}
@@ -69,7 +70,7 @@ func BenchmarkFromEither_Left(b *testing.B) {
either := E.Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = FromEither(either)
}
}
@@ -77,7 +78,7 @@ func BenchmarkFromEither_Left(b *testing.B) {
func BenchmarkFromIO(b *testing.B) {
io := func() int { return 42 }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = FromIO(io)
}
}
@@ -85,7 +86,7 @@ func BenchmarkFromIO(b *testing.B) {
func BenchmarkFromIOEither_Right(b *testing.B) {
ioe := IOE.Of[error](42)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = FromIOEither(ioe)
}
}
@@ -93,7 +94,7 @@ func BenchmarkFromIOEither_Right(b *testing.B) {
func BenchmarkFromIOEither_Left(b *testing.B) {
ioe := IOE.Left[int](benchErr)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = FromIOEither(ioe)
}
}
@@ -103,7 +104,7 @@ func BenchmarkExecute_Right(b *testing.B) {
rioe := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -112,7 +113,7 @@ func BenchmarkExecute_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -123,7 +124,7 @@ func BenchmarkExecute_WithContext(b *testing.B) {
defer cancel()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(ctx)()
}
}
@@ -131,40 +132,40 @@ func BenchmarkExecute_WithContext(b *testing.B) {
// Benchmark functor operations
func BenchmarkMonadMap_Right(b *testing.B) {
rioe := Right(42)
mapper := func(a int) int { return a * 2 }
mapper := N.Mul(2)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadMap(rioe, mapper)
}
}
func BenchmarkMonadMap_Left(b *testing.B) {
rioe := Left[int](benchErr)
mapper := func(a int) int { return a * 2 }
mapper := N.Mul(2)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadMap(rioe, mapper)
}
}
func BenchmarkMap_Right(b *testing.B) {
rioe := Right(42)
mapper := Map(func(a int) int { return a * 2 })
mapper := Map(N.Mul(2))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = mapper(rioe)
}
}
func BenchmarkMap_Left(b *testing.B) {
rioe := Left[int](benchErr)
mapper := Map(func(a int) int { return a * 2 })
mapper := Map(N.Mul(2))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = mapper(rioe)
}
}
@@ -174,7 +175,7 @@ func BenchmarkMapTo_Right(b *testing.B) {
mapper := MapTo[int](99)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = mapper(rioe)
}
}
@@ -185,7 +186,7 @@ func BenchmarkMonadChain_Right(b *testing.B) {
chainer := func(a int) ReaderIOResult[int] { return Right(a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadChain(rioe, chainer)
}
}
@@ -195,7 +196,7 @@ func BenchmarkMonadChain_Left(b *testing.B) {
chainer := func(a int) ReaderIOResult[int] { return Right(a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadChain(rioe, chainer)
}
}
@@ -205,7 +206,7 @@ func BenchmarkChain_Right(b *testing.B) {
chainer := Chain(func(a int) ReaderIOResult[int] { return Right(a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -215,7 +216,7 @@ func BenchmarkChain_Left(b *testing.B) {
chainer := Chain(func(a int) ReaderIOResult[int] { return Right(a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -225,7 +226,7 @@ func BenchmarkChainFirst_Right(b *testing.B) {
chainer := ChainFirst(func(a int) ReaderIOResult[string] { return Right("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -235,7 +236,7 @@ func BenchmarkChainFirst_Left(b *testing.B) {
chainer := ChainFirst(func(a int) ReaderIOResult[string] { return Right("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -244,7 +245,7 @@ func BenchmarkFlatten_Right(b *testing.B) {
nested := Right(Right(42))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Flatten(nested)
}
}
@@ -253,28 +254,28 @@ func BenchmarkFlatten_Left(b *testing.B) {
nested := Left[ReaderIOResult[int]](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Flatten(nested)
}
}
// Benchmark applicative operations
func BenchmarkMonadApSeq_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fab := Right(N.Mul(2))
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadApSeq(fab, fa)
}
}
func BenchmarkMonadApSeq_RightLeft(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fab := Right(N.Mul(2))
fa := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadApSeq(fab, fa)
}
}
@@ -284,27 +285,27 @@ func BenchmarkMonadApSeq_LeftRight(b *testing.B) {
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadApSeq(fab, fa)
}
}
func BenchmarkMonadApPar_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fab := Right(N.Mul(2))
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadApPar(fab, fa)
}
}
func BenchmarkMonadApPar_RightLeft(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fab := Right(N.Mul(2))
fa := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadApPar(fab, fa)
}
}
@@ -314,30 +315,30 @@ func BenchmarkMonadApPar_LeftRight(b *testing.B) {
fa := Right(42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = MonadApPar(fab, fa)
}
}
// Benchmark execution of applicative operations
func BenchmarkExecuteApSeq_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fab := Right(N.Mul(2))
fa := Right(42)
rioe := MonadApSeq(fab, fa)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
func BenchmarkExecuteApPar_RightRight(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fab := Right(N.Mul(2))
fa := Right(42)
rioe := MonadApPar(fab, fa)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -348,7 +349,7 @@ func BenchmarkAlt_RightRight(b *testing.B) {
alternative := Alt(func() ReaderIOResult[int] { return Right(99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = alternative(rioe)
}
}
@@ -358,7 +359,7 @@ func BenchmarkAlt_LeftRight(b *testing.B) {
alternative := Alt(func() ReaderIOResult[int] { return Right(99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = alternative(rioe)
}
}
@@ -368,7 +369,7 @@ func BenchmarkOrElse_Right(b *testing.B) {
recover := OrElse(func(e error) ReaderIOResult[int] { return Right(0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = recover(rioe)
}
}
@@ -378,7 +379,7 @@ func BenchmarkOrElse_Left(b *testing.B) {
recover := OrElse(func(e error) ReaderIOResult[int] { return Right(0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = recover(rioe)
}
}
@@ -389,7 +390,7 @@ func BenchmarkChainEitherK_Right(b *testing.B) {
chainer := ChainEitherK(func(a int) Either[int] { return E.Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -399,7 +400,7 @@ func BenchmarkChainEitherK_Left(b *testing.B) {
chainer := ChainEitherK(func(a int) Either[int] { return E.Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -409,7 +410,7 @@ func BenchmarkChainIOK_Right(b *testing.B) {
chainer := ChainIOK(func(a int) func() int { return func() int { return a * 2 } })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -419,7 +420,7 @@ func BenchmarkChainIOK_Left(b *testing.B) {
chainer := ChainIOK(func(a int) func() int { return func() int { return a * 2 } })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -429,7 +430,7 @@ func BenchmarkChainIOEitherK_Right(b *testing.B) {
chainer := ChainIOEitherK(func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -439,7 +440,7 @@ func BenchmarkChainIOEitherK_Left(b *testing.B) {
chainer := ChainIOEitherK(func(a int) IOEither[int] { return IOE.Of[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = chainer(rioe)
}
}
@@ -447,7 +448,7 @@ func BenchmarkChainIOEitherK_Left(b *testing.B) {
// Benchmark context operations
func BenchmarkAsk(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Ask()
}
}
@@ -455,7 +456,7 @@ func BenchmarkAsk(b *testing.B) {
func BenchmarkDefer(b *testing.B) {
gen := func() ReaderIOResult[int] { return Right(42) }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Defer(gen)
}
}
@@ -463,7 +464,7 @@ func BenchmarkDefer(b *testing.B) {
func BenchmarkMemoize(b *testing.B) {
rioe := Right(42)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Memoize(rioe)
}
}
@@ -472,14 +473,14 @@ func BenchmarkMemoize(b *testing.B) {
func BenchmarkDelay_Construction(b *testing.B) {
rioe := Right(42)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = Delay[int](time.Millisecond)(rioe)
}
}
func BenchmarkTimer_Construction(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Timer(time.Millisecond)
}
}
@@ -490,7 +491,7 @@ func BenchmarkTryCatch_Success(b *testing.B) {
return func() (int, error) { return 42, nil }
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = TryCatch(f)
}
}
@@ -500,7 +501,7 @@ func BenchmarkTryCatch_Error(b *testing.B) {
return func() (int, error) { return 0, benchErr }
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = TryCatch(f)
}
}
@@ -512,7 +513,7 @@ func BenchmarkExecuteTryCatch_Success(b *testing.B) {
rioe := TryCatch(f)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -524,7 +525,7 @@ func BenchmarkExecuteTryCatch_Error(b *testing.B) {
rioe := TryCatch(f)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -534,10 +535,10 @@ func BenchmarkPipeline_Map_Right(b *testing.B) {
rioe := Right(21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = F.Pipe1(
rioe,
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
)
}
}
@@ -546,10 +547,10 @@ func BenchmarkPipeline_Map_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = F.Pipe1(
rioe,
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
)
}
}
@@ -558,7 +559,7 @@ func BenchmarkPipeline_Chain_Right(b *testing.B) {
rioe := Right(21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = F.Pipe1(
rioe,
Chain(func(x int) ReaderIOResult[int] { return Right(x * 2) }),
@@ -570,7 +571,7 @@ func BenchmarkPipeline_Chain_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = F.Pipe1(
rioe,
Chain(func(x int) ReaderIOResult[int] { return Right(x * 2) }),
@@ -582,12 +583,12 @@ func BenchmarkPipeline_Complex_Right(b *testing.B) {
rioe := Right(10)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = F.Pipe3(
rioe,
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
Chain(func(x int) ReaderIOResult[int] { return Right(x + 1) }),
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
)
}
}
@@ -596,12 +597,12 @@ func BenchmarkPipeline_Complex_Left(b *testing.B) {
rioe := Left[int](benchErr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchRIOE = F.Pipe3(
rioe,
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
Chain(func(x int) ReaderIOResult[int] { return Right(x + 1) }),
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
)
}
}
@@ -609,13 +610,13 @@ func BenchmarkPipeline_Complex_Left(b *testing.B) {
func BenchmarkExecutePipeline_Complex_Right(b *testing.B) {
rioe := F.Pipe3(
Right(10),
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
Chain(func(x int) ReaderIOResult[int] { return Right(x + 1) }),
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -624,7 +625,7 @@ func BenchmarkExecutePipeline_Complex_Right(b *testing.B) {
func BenchmarkDo(b *testing.B) {
type State struct{ value int }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Do(State{})
}
}
@@ -642,7 +643,7 @@ func BenchmarkBind_Right(b *testing.B) {
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = binder(initial)
}
}
@@ -658,7 +659,7 @@ func BenchmarkLet_Right(b *testing.B) {
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = letter(initial)
}
}
@@ -674,7 +675,7 @@ func BenchmarkApS_Right(b *testing.B) {
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = aps(initial)
}
}
@@ -687,7 +688,7 @@ func BenchmarkTraverseArray_Empty(b *testing.B) {
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = traverser(arr)
}
}
@@ -699,7 +700,7 @@ func BenchmarkTraverseArray_Small(b *testing.B) {
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = traverser(arr)
}
}
@@ -714,7 +715,7 @@ func BenchmarkTraverseArray_Medium(b *testing.B) {
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = traverser(arr)
}
}
@@ -726,7 +727,7 @@ func BenchmarkTraverseArraySeq_Small(b *testing.B) {
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = traverser(arr)
}
}
@@ -738,7 +739,7 @@ func BenchmarkTraverseArrayPar_Small(b *testing.B) {
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = traverser(arr)
}
}
@@ -751,7 +752,7 @@ func BenchmarkSequenceArray_Small(b *testing.B) {
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = SequenceArray(arr)
}
}
@@ -763,7 +764,7 @@ func BenchmarkExecuteTraverseArray_Small(b *testing.B) {
})(arr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = rioe(benchCtx)()
}
}
@@ -775,7 +776,7 @@ func BenchmarkExecuteTraverseArraySeq_Small(b *testing.B) {
})(arr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = rioe(benchCtx)()
}
}
@@ -787,7 +788,7 @@ func BenchmarkExecuteTraverseArrayPar_Small(b *testing.B) {
})(arr)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = rioe(benchCtx)()
}
}
@@ -800,7 +801,7 @@ func BenchmarkTraverseRecord_Small(b *testing.B) {
})
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = traverser(rec)
}
}
@@ -813,7 +814,7 @@ func BenchmarkSequenceRecord_Small(b *testing.B) {
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = SequenceRecord(rec)
}
}
@@ -826,7 +827,7 @@ func BenchmarkWithResource_Success(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = WithResource[int](acquire, release)(body)
}
}
@@ -839,7 +840,7 @@ func BenchmarkExecuteWithResource_Success(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -852,7 +853,7 @@ func BenchmarkExecuteWithResource_ErrorInBody(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(benchCtx)()
}
}
@@ -865,13 +866,13 @@ func BenchmarkExecute_CanceledContext(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(ctx)()
}
}
func BenchmarkExecuteApPar_CanceledContext(b *testing.B) {
fab := Right(func(a int) int { return a * 2 })
fab := Right(N.Mul(2))
fa := Right(42)
rioe := MonadApPar(fab, fa)
ctx, cancel := context.WithCancel(benchCtx)
@@ -879,7 +880,7 @@ func BenchmarkExecuteApPar_CanceledContext(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = rioe(ctx)()
}
}

View File

@@ -26,6 +26,7 @@ import (
IOG "github.com/IBM/fp-go/v2/io"
IOE "github.com/IBM/fp-go/v2/ioeither"
M "github.com/IBM/fp-go/v2/monoid"
N "github.com/IBM/fp-go/v2/number"
O "github.com/IBM/fp-go/v2/option"
R "github.com/IBM/fp-go/v2/reader"
"github.com/stretchr/testify/assert"
@@ -77,27 +78,27 @@ func TestOf(t *testing.T) {
func TestMonadMap(t *testing.T) {
t.Run("Map over Right", func(t *testing.T) {
result := MonadMap(Of(5), func(x int) int { return x * 2 })
result := MonadMap(Of(5), N.Mul(2))
assert.Equal(t, E.Right[error](10), result(context.Background())())
})
t.Run("Map over Left", func(t *testing.T) {
err := errors.New("test error")
result := MonadMap(Left[int](err), func(x int) int { return x * 2 })
result := MonadMap(Left[int](err), N.Mul(2))
assert.Equal(t, E.Left[int](err), result(context.Background())())
})
}
func TestMap(t *testing.T) {
t.Run("Map with success", func(t *testing.T) {
mapper := Map(func(x int) int { return x * 2 })
mapper := Map(N.Mul(2))
result := mapper(Of(5))
assert.Equal(t, E.Right[error](10), result(context.Background())())
})
t.Run("Map with error", func(t *testing.T) {
err := errors.New("test error")
mapper := Map(func(x int) int { return x * 2 })
mapper := Map(N.Mul(2))
result := mapper(Left[int](err))
assert.Equal(t, E.Left[int](err), result(context.Background())())
})
@@ -182,7 +183,7 @@ func TestChainFirst(t *testing.T) {
func TestMonadApSeq(t *testing.T) {
t.Run("ApSeq with success", func(t *testing.T) {
fab := Of(func(x int) int { return x * 2 })
fab := Of(N.Mul(2))
fa := Of(5)
result := MonadApSeq(fab, fa)
assert.Equal(t, E.Right[error](10), result(context.Background())())
@@ -198,7 +199,7 @@ func TestMonadApSeq(t *testing.T) {
t.Run("ApSeq with error in value", func(t *testing.T) {
err := errors.New("test error")
fab := Of(func(x int) int { return x * 2 })
fab := Of(N.Mul(2))
fa := Left[int](err)
result := MonadApSeq(fab, fa)
assert.Equal(t, E.Left[int](err), result(context.Background())())
@@ -207,7 +208,7 @@ func TestMonadApSeq(t *testing.T) {
func TestApSeq(t *testing.T) {
fa := Of(5)
fab := Of(func(x int) int { return x * 2 })
fab := Of(N.Mul(2))
result := MonadApSeq(fab, fa)
assert.Equal(t, E.Right[error](10), result(context.Background())())
}
@@ -215,7 +216,7 @@ func TestApSeq(t *testing.T) {
func TestApPar(t *testing.T) {
t.Run("ApPar with success", func(t *testing.T) {
fa := Of(5)
fab := Of(func(x int) int { return x * 2 })
fab := Of(N.Mul(2))
result := MonadApPar(fab, fa)
assert.Equal(t, E.Right[error](10), result(context.Background())())
})
@@ -224,7 +225,7 @@ func TestApPar(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
fa := Of(5)
fab := Of(func(x int) int { return x * 2 })
fab := Of(N.Mul(2))
result := MonadApPar(fab, fa)
res := result(ctx)()
assert.True(t, E.IsLeft(res))
@@ -587,14 +588,14 @@ func TestFlatten(t *testing.T) {
}
func TestMonadFlap(t *testing.T) {
fab := Of(func(x int) int { return x * 2 })
fab := Of(N.Mul(2))
result := MonadFlap(fab, 5)
assert.Equal(t, E.Right[error](10), result(context.Background())())
}
func TestFlap(t *testing.T) {
flapper := Flap[int](5)
result := flapper(Of(func(x int) int { return x * 2 }))
result := flapper(Of(N.Mul(2)))
assert.Equal(t, E.Right[error](10), result(context.Background())())
}

View File

@@ -249,7 +249,7 @@ func TestMultiTokenStringRepresentation(t *testing.T) {
// Benchmark tests
func BenchmarkMakeToken(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
MakeToken[int]("BenchToken")
}
}
@@ -259,13 +259,13 @@ func BenchmarkTokenUnerase(b *testing.B) {
value := any(42)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
token.Unerase(value)
}
}
func BenchmarkMakeMultiToken(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
MakeMultiToken[int]("BenchMulti")
}
}

View File

@@ -4,16 +4,17 @@ import (
"fmt"
"testing"
A "github.com/IBM/fp-go/v2/array"
TST "github.com/IBM/fp-go/v2/internal/testing"
"github.com/stretchr/testify/assert"
)
func TestCompactArray(t *testing.T) {
ar := []Either[string, string]{
ar := A.From(
Of[string]("ok"),
Left[string]("err"),
Of[string]("ok"),
}
)
res := CompactArray(ar)
assert.Equal(t, 2, len(res))

View File

@@ -58,7 +58,7 @@ func FromIO[E any, IO ~func() A, A any](f IO) Either[E, A] {
//
// Example:
//
// fab := either.Right[error](func(x int) int { return x * 2 })
// fab := either.Right[error](N.Mul(2))
// fa := either.Right[error](21)
// result := either.MonadAp(fab, fa) // Right(42)
func MonadAp[B, E, A any](fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] {
@@ -81,7 +81,7 @@ func Ap[B, E, A any](fa Either[E, A]) Operator[E, func(A) B, B] {
//
// result := either.MonadMap(
// either.Right[error](21),
// func(x int) int { return x * 2 },
// N.Mul(2),
// ) // Right(42)
//
//go:inline

View File

@@ -20,6 +20,7 @@ import (
"testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
)
var (
@@ -33,21 +34,21 @@ var (
// Benchmark core constructors
func BenchmarkLeft(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = Left[int](errBench)
}
}
func BenchmarkRight(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = Right[error](42)
}
}
func BenchmarkOf(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = Of[error](42)
}
}
@@ -57,7 +58,7 @@ func BenchmarkIsLeft(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchBool = IsLeft(left)
}
}
@@ -66,7 +67,7 @@ func BenchmarkIsRight(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchBool = IsRight(right)
}
}
@@ -75,10 +76,10 @@ func BenchmarkIsRight(b *testing.B) {
func BenchmarkMonadFold_Right(b *testing.B) {
right := Right[error](42)
onLeft := func(e error) int { return 0 }
onRight := func(a int) int { return a * 2 }
onRight := N.Mul(2)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt = MonadFold(right, onLeft, onRight)
}
}
@@ -86,10 +87,10 @@ func BenchmarkMonadFold_Right(b *testing.B) {
func BenchmarkMonadFold_Left(b *testing.B) {
left := Left[int](errBench)
onLeft := func(e error) int { return 0 }
onRight := func(a int) int { return a * 2 }
onRight := N.Mul(2)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt = MonadFold(left, onLeft, onRight)
}
}
@@ -98,11 +99,11 @@ func BenchmarkFold_Right(b *testing.B) {
right := Right[error](42)
folder := Fold(
func(e error) int { return 0 },
func(a int) int { return a * 2 },
N.Mul(2),
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt = folder(right)
}
}
@@ -111,11 +112,11 @@ func BenchmarkFold_Left(b *testing.B) {
left := Left[int](errBench)
folder := Fold(
func(e error) int { return 0 },
func(a int) int { return a * 2 },
N.Mul(2),
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt = folder(left)
}
}
@@ -125,7 +126,7 @@ func BenchmarkUnwrap_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt, _ = Unwrap(right)
}
}
@@ -134,7 +135,7 @@ func BenchmarkUnwrap_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt, _ = Unwrap(left)
}
}
@@ -143,7 +144,7 @@ func BenchmarkUnwrapError_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt, _ = UnwrapError(right)
}
}
@@ -152,7 +153,7 @@ func BenchmarkUnwrapError_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt, _ = UnwrapError(left)
}
}
@@ -160,40 +161,40 @@ func BenchmarkUnwrapError_Left(b *testing.B) {
// Benchmark functor operations
func BenchmarkMonadMap_Right(b *testing.B) {
right := Right[error](42)
mapper := func(a int) int { return a * 2 }
mapper := N.Mul(2)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadMap(right, mapper)
}
}
func BenchmarkMonadMap_Left(b *testing.B) {
left := Left[int](errBench)
mapper := func(a int) int { return a * 2 }
mapper := N.Mul(2)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadMap(left, mapper)
}
}
func BenchmarkMap_Right(b *testing.B) {
right := Right[error](42)
mapper := Map[error](func(a int) int { return a * 2 })
mapper := Map[error](N.Mul(2))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = mapper(right)
}
}
func BenchmarkMap_Left(b *testing.B) {
left := Left[int](errBench)
mapper := Map[error](func(a int) int { return a * 2 })
mapper := Map[error](N.Mul(2))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = mapper(left)
}
}
@@ -203,7 +204,7 @@ func BenchmarkMapLeft_Right(b *testing.B) {
mapper := MapLeft[int](error.Error)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = mapper(right)
}
}
@@ -213,7 +214,7 @@ func BenchmarkMapLeft_Left(b *testing.B) {
mapper := MapLeft[int](error.Error)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = mapper(left)
}
}
@@ -226,7 +227,7 @@ func BenchmarkBiMap_Right(b *testing.B) {
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = mapper(right)
}
}
@@ -239,7 +240,7 @@ func BenchmarkBiMap_Left(b *testing.B) {
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = mapper(left)
}
}
@@ -250,7 +251,7 @@ func BenchmarkMonadChain_Right(b *testing.B) {
chainer := func(a int) Either[error, int] { return Right[error](a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadChain(right, chainer)
}
}
@@ -260,7 +261,7 @@ func BenchmarkMonadChain_Left(b *testing.B) {
chainer := func(a int) Either[error, int] { return Right[error](a * 2) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadChain(left, chainer)
}
}
@@ -270,7 +271,7 @@ func BenchmarkChain_Right(b *testing.B) {
chainer := Chain(func(a int) Either[error, int] { return Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = chainer(right)
}
}
@@ -280,7 +281,7 @@ func BenchmarkChain_Left(b *testing.B) {
chainer := Chain(func(a int) Either[error, int] { return Right[error](a * 2) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = chainer(left)
}
}
@@ -290,7 +291,7 @@ func BenchmarkChainFirst_Right(b *testing.B) {
chainer := ChainFirst(func(a int) Either[error, string] { return Right[error]("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = chainer(right)
}
}
@@ -300,7 +301,7 @@ func BenchmarkChainFirst_Left(b *testing.B) {
chainer := ChainFirst(func(a int) Either[error, string] { return Right[error]("logged") })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = chainer(left)
}
}
@@ -309,7 +310,7 @@ func BenchmarkFlatten_Right(b *testing.B) {
nested := Right[error](Right[error](42))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = Flatten(nested)
}
}
@@ -318,28 +319,28 @@ func BenchmarkFlatten_Left(b *testing.B) {
nested := Left[Either[error, int]](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = Flatten(nested)
}
}
// Benchmark applicative operations
func BenchmarkMonadAp_RightRight(b *testing.B) {
fab := Right[error](func(a int) int { return a * 2 })
fab := Right[error](N.Mul(2))
fa := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadAp(fab, fa)
}
}
func BenchmarkMonadAp_RightLeft(b *testing.B) {
fab := Right[error](func(a int) int { return a * 2 })
fab := Right[error](N.Mul(2))
fa := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadAp(fab, fa)
}
}
@@ -349,18 +350,18 @@ func BenchmarkMonadAp_LeftRight(b *testing.B) {
fa := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadAp(fab, fa)
}
}
func BenchmarkAp_RightRight(b *testing.B) {
fab := Right[error](func(a int) int { return a * 2 })
fab := Right[error](N.Mul(2))
fa := Right[error](42)
ap := Ap[int](fa)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = ap(fab)
}
}
@@ -371,7 +372,7 @@ func BenchmarkAlt_RightRight(b *testing.B) {
alternative := Alt(func() Either[error, int] { return Right[error](99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = alternative(right)
}
}
@@ -381,7 +382,7 @@ func BenchmarkAlt_LeftRight(b *testing.B) {
alternative := Alt(func() Either[error, int] { return Right[error](99) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = alternative(left)
}
}
@@ -391,7 +392,7 @@ func BenchmarkOrElse_Right(b *testing.B) {
recover := OrElse(func(e error) Either[error, int] { return Right[error](0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = recover(right)
}
}
@@ -401,7 +402,7 @@ func BenchmarkOrElse_Left(b *testing.B) {
recover := OrElse(func(e error) Either[error, int] { return Right[error](0) })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = recover(left)
}
}
@@ -410,7 +411,7 @@ func BenchmarkOrElse_Left(b *testing.B) {
func BenchmarkTryCatch_Success(b *testing.B) {
onThrow := func(err error) error { return err }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = TryCatch(42, nil, onThrow)
}
}
@@ -418,21 +419,21 @@ func BenchmarkTryCatch_Success(b *testing.B) {
func BenchmarkTryCatch_Error(b *testing.B) {
onThrow := func(err error) error { return err }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = TryCatch(0, errBench, onThrow)
}
}
func BenchmarkTryCatchError_Success(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = TryCatchError(42, nil)
}
}
func BenchmarkTryCatchError_Error(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = TryCatchError(0, errBench)
}
}
@@ -441,7 +442,7 @@ func BenchmarkSwap_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Swap(right)
}
}
@@ -450,7 +451,7 @@ func BenchmarkSwap_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Swap(left)
}
}
@@ -460,7 +461,7 @@ func BenchmarkGetOrElse_Right(b *testing.B) {
getter := GetOrElse(func(e error) int { return 0 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt = getter(right)
}
}
@@ -470,7 +471,7 @@ func BenchmarkGetOrElse_Left(b *testing.B) {
getter := GetOrElse(func(e error) int { return 0 })
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchInt = getter(left)
}
}
@@ -480,10 +481,10 @@ func BenchmarkPipeline_Map_Right(b *testing.B) {
right := Right[error](21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = F.Pipe1(
right,
Map[error](func(x int) int { return x * 2 }),
Map[error](N.Mul(2)),
)
}
}
@@ -492,10 +493,10 @@ func BenchmarkPipeline_Map_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = F.Pipe1(
left,
Map[error](func(x int) int { return x * 2 }),
Map[error](N.Mul(2)),
)
}
}
@@ -504,7 +505,7 @@ func BenchmarkPipeline_Chain_Right(b *testing.B) {
right := Right[error](21)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = F.Pipe1(
right,
Chain(func(x int) Either[error, int] { return Right[error](x * 2) }),
@@ -516,7 +517,7 @@ func BenchmarkPipeline_Chain_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = F.Pipe1(
left,
Chain(func(x int) Either[error, int] { return Right[error](x * 2) }),
@@ -528,12 +529,12 @@ func BenchmarkPipeline_Complex_Right(b *testing.B) {
right := Right[error](10)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = F.Pipe3(
right,
Map[error](func(x int) int { return x * 2 }),
Map[error](N.Mul(2)),
Chain(func(x int) Either[error, int] { return Right[error](x + 1) }),
Map[error](func(x int) int { return x * 2 }),
Map[error](N.Mul(2)),
)
}
}
@@ -542,12 +543,12 @@ func BenchmarkPipeline_Complex_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = F.Pipe3(
left,
Map[error](func(x int) int { return x * 2 }),
Map[error](N.Mul(2)),
Chain(func(x int) Either[error, int] { return Right[error](x + 1) }),
Map[error](func(x int) int { return x * 2 }),
Map[error](N.Mul(2)),
)
}
}
@@ -559,7 +560,7 @@ func BenchmarkMonadSequence2_RightRight(b *testing.B) {
f := func(a, b int) Either[error, int] { return Right[error](a + b) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadSequence2(e1, e2, f)
}
}
@@ -570,7 +571,7 @@ func BenchmarkMonadSequence2_LeftRight(b *testing.B) {
f := func(a, b int) Either[error, int] { return Right[error](a + b) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadSequence2(e1, e2, f)
}
}
@@ -582,7 +583,7 @@ func BenchmarkMonadSequence3_RightRightRight(b *testing.B) {
f := func(a, b, c int) Either[error, int] { return Right[error](a + b + c) }
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchResult = MonadSequence3(e1, e2, e3, f)
}
}
@@ -591,7 +592,7 @@ func BenchmarkMonadSequence3_RightRightRight(b *testing.B) {
func BenchmarkDo(b *testing.B) {
type State struct{ value int }
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = Do[error](State{})
}
}
@@ -609,7 +610,7 @@ func BenchmarkBind_Right(b *testing.B) {
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = binder(initial)
}
}
@@ -625,7 +626,7 @@ func BenchmarkLet_Right(b *testing.B) {
)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = letter(initial)
}
}
@@ -635,7 +636,7 @@ func BenchmarkString_Right(b *testing.B) {
right := Right[error](42)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchString = right.String()
}
}
@@ -644,7 +645,7 @@ func BenchmarkString_Left(b *testing.B) {
left := Left[int](errBench)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
benchString = left.String()
}
}

View File

@@ -66,7 +66,7 @@ func TestUnwrapError(t *testing.T) {
func TestReduce(t *testing.T) {
s := S.Semigroup()
s := S.Semigroup
assert.Equal(t, "foobar", F.Pipe1(Right[string]("bar"), Reduce[string](s.Concat, "foo")))
assert.Equal(t, "foo", F.Pipe1(Left[string]("bar"), Reduce[string](s.Concat, "foo")))

View File

@@ -46,7 +46,7 @@ func _log[E, A any](left func(string, ...any), right func(string, ...any), prefi
// result := F.Pipe2(
// either.Right[error](42),
// logger("Processing"),
// either.Map(func(x int) int { return x * 2 }),
// either.Map(N.Mul(2)),
// )
// // Logs: "Processing: 42"
// // result is Right(84)

142
v2/either/validation.go Normal file
View File

@@ -0,0 +1,142 @@
// 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 either
import (
F "github.com/IBM/fp-go/v2/function"
S "github.com/IBM/fp-go/v2/semigroup"
)
// MonadApV is the applicative validation functor that combines errors using a semigroup.
//
// Unlike the standard [MonadAp] which short-circuits on the first Left (error),
// MonadApV accumulates all errors using the provided semigroup's Concat operation.
// This is particularly useful for validation scenarios where you want to collect
// all validation errors rather than stopping at the first one.
//
// The function takes a semigroup for combining errors and returns a function that
// applies a wrapped function to a wrapped value, accumulating errors if both are Left.
//
// Behavior:
// - If both fab and fa are Left, combines their errors using sg.Concat
// - If only fab is Left, returns Left with fab's error
// - If only fa is Left, returns Left with fa's error
// - If both are Right, applies the function and returns Right with the result
//
// Type Parameters:
// - B: The result type after applying the function
// - E: The error type (must support the semigroup operation)
// - A: The input type to the function
//
// Parameters:
// - sg: A semigroup that defines how to combine two error values
//
// Returns:
// - A function that takes a wrapped function and a wrapped value, returning
// Either[E, B] with accumulated errors or the computed result
//
// Example:
//
// // Define a semigroup that concatenates error messages
// errorSemigroup := semigroup.MakeSemigroup(func(e1, e2 string) string {
// return e1 + "; " + e2
// })
//
// // Create the validation applicative
// applyV := either.MonadApV[int](errorSemigroup)
//
// // Both are errors - errors get combined
// fab := either.Left[func(int) int]("error1")
// fa := either.Left[int]("error2")
// result := applyV(fab, fa) // Left("error1; error2")
//
// // One error - returns that error
// fab2 := either.Right[string](N.Mul(2))
// fa2 := either.Left[int]("validation failed")
// result2 := applyV(fab2, fa2) // Left("validation failed")
//
// // Both success - applies function
// fab3 := either.Right[string](N.Mul(2))
// fa3 := either.Right[string](21)
// result3 := applyV(fab3, fa3) // Right(42)
func MonadApV[B, E, A any](sg S.Semigroup[E]) func(fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] {
c := F.Bind2of2(sg.Concat)
return func(fab Either[E, func(a A) B], fa Either[E, A]) Either[E, B] {
return MonadFold(fab, func(eab E) Either[E, B] {
return MonadFold(fa, F.Flow2(c(eab), Left[B]), F.Constant1[A](Left[B](eab)))
}, func(ab func(A) B) Either[E, B] {
return MonadFold(fa, Left[B, E], F.Flow2(ab, Right[E, B]))
})
}
}
// ApV is the curried version of [MonadApV] that combines errors using a semigroup.
//
// This function provides a more convenient API for validation scenarios by currying
// the arguments. It first takes the value to validate, then returns a function that
// takes the validation function. This allows for a more natural composition style.
//
// Like [MonadApV], this accumulates all errors using the provided semigroup instead
// of short-circuiting on the first error. This is the key difference from the
// standard [Ap] function.
//
// Type Parameters:
// - B: The result type after applying the function
// - E: The error type (must support the semigroup operation)
// - A: The input type to the function
//
// Parameters:
// - sg: A semigroup that defines how to combine two error values
//
// Returns:
// - A function that takes a value Either[E, A] and returns an Operator that
// applies validation functions while accumulating errors
//
// Example:
//
// // Define a semigroup for combining validation errors
// type ValidationError struct {
// Errors []string
// }
// errorSemigroup := semigroup.MakeSemigroup(func(e1, e2 ValidationError) ValidationError {
// return ValidationError{Errors: append(e1.Errors, e2.Errors...)}
// })
//
// // Create validators
// validatePositive := func(x int) either.Either[ValidationError, int] {
// if x > 0 {
// return either.Right[ValidationError](x)
// }
// return either.Left[int](ValidationError{Errors: []string{"must be positive"}})
// }
//
// // Use ApV for validation
// applyValidation := either.ApV[int](errorSemigroup)
// value := either.Left[int](ValidationError{Errors: []string{"invalid input"}})
// validator := either.Left[func(int) int](ValidationError{Errors: []string{"invalid validator"}})
//
// result := applyValidation(value)(validator)
// // Left(ValidationError{Errors: []string{"invalid validator", "invalid input"}})
func ApV[B, E, A any](sg S.Semigroup[E]) func(fa Either[E, A]) Operator[E, func(A) B, B] {
c := F.Bind2of2(sg.Concat)
return func(fa Either[E, A]) Operator[E, func(A) B, B] {
return Fold(func(eab E) Either[E, B] {
return MonadFold(fa, F.Flow2(c(eab), Left[B]), F.Constant1[A](Left[B](eab)))
}, func(ab func(A) B) Either[E, B] {
return MonadFold(fa, Left[B, E], F.Flow2(ab, Right[E, B]))
})
}
}

View File

@@ -0,0 +1,381 @@
// 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 either
import (
"testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
S "github.com/IBM/fp-go/v2/semigroup"
"github.com/stretchr/testify/assert"
)
// TestMonadApV_BothRight tests MonadApV when both function and value are Right
func TestMonadApV_BothRight(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := MonadApV[int, string, int](sg)
// Both are Right - should apply function
fab := Right[string](N.Mul(2))
fa := Right[string](21)
result := applyV(fab, fa)
assert.True(t, IsRight(result))
assert.Equal(t, Right[string](42), result)
}
// TestMonadApV_BothLeft tests MonadApV when both function and value are Left
func TestMonadApV_BothLeft(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := MonadApV[int, string, int](sg)
// Both are Left - should combine errors
fab := Left[func(int) int]("error1")
fa := Left[int]("error2")
result := applyV(fab, fa)
assert.True(t, IsLeft(result))
// When both are Left, errors are combined as: fa error + fab error
assert.Equal(t, Left[int]("error2; error1"), result)
}
// TestMonadApV_LeftFunction tests MonadApV when function is Left and value is Right
func TestMonadApV_LeftFunction(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := MonadApV[int, string, int](sg)
// Function is Left, value is Right - should return function's error
fab := Left[func(int) int]("function error")
fa := Right[string](21)
result := applyV(fab, fa)
assert.True(t, IsLeft(result))
assert.Equal(t, Left[int]("function error"), result)
}
// TestMonadApV_LeftValue tests MonadApV when function is Right and value is Left
func TestMonadApV_LeftValue(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := MonadApV[int, string, int](sg)
// Function is Right, value is Left - should return value's error
fab := Right[string](N.Mul(2))
fa := Left[int]("value error")
result := applyV(fab, fa)
assert.True(t, IsLeft(result))
assert.Equal(t, Left[int]("value error"), result)
}
// TestMonadApV_WithSliceSemigroup tests MonadApV with a slice-based semigroup
func TestMonadApV_WithSliceSemigroup(t *testing.T) {
// Create a semigroup that concatenates slices
sg := S.MakeSemigroup(func(a, b []string) []string {
return append(a, b...)
})
// Create the validation applicative
applyV := MonadApV[string, []string, string](sg)
// Both are Left with slice errors
fab := Left[func(string) string]([]string{"error1", "error2"})
fa := Left[string]([]string{"error3", "error4"})
result := applyV(fab, fa)
assert.True(t, IsLeft(result))
// When both are Left, errors are combined as: fa errors + fab errors
expected := Left[string]([]string{"error3", "error4", "error1", "error2"})
assert.Equal(t, expected, result)
}
// TestMonadApV_ComplexFunction tests MonadApV with a more complex function
func TestMonadApV_ComplexFunction(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + " | " + b
})
// Create the validation applicative
applyV := MonadApV[string, string, int](sg)
// Test with a function that transforms the value
fab := Right[string](func(x int) string {
if x > 0 {
return "positive"
}
return "non-positive"
})
fa := Right[string](42)
result := applyV(fab, fa)
assert.True(t, IsRight(result))
assert.Equal(t, Right[string]("positive"), result)
}
// TestApV_BothRight tests ApV when both function and value are Right
func TestApV_BothRight(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := ApV[int, string, int](sg)
// Both are Right - should apply function
fa := Right[string](21)
fab := Right[string](N.Mul(2))
result := applyV(fa)(fab)
assert.True(t, IsRight(result))
assert.Equal(t, Right[string](42), result)
}
// TestApV_BothLeft tests ApV when both function and value are Left
func TestApV_BothLeft(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := ApV[int, string, int](sg)
// Both are Left - should combine errors
fa := Left[int]("error2")
fab := Left[func(int) int]("error1")
result := applyV(fa)(fab)
assert.True(t, IsLeft(result))
// When both are Left, errors are combined as: fa error + fab error
assert.Equal(t, Left[int]("error2; error1"), result)
}
// TestApV_LeftFunction tests ApV when function is Left and value is Right
func TestApV_LeftFunction(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := ApV[int, string, int](sg)
// Function is Left, value is Right - should return function's error
fa := Right[string](21)
fab := Left[func(int) int]("function error")
result := applyV(fa)(fab)
assert.True(t, IsLeft(result))
assert.Equal(t, Left[int]("function error"), result)
}
// TestApV_LeftValue tests ApV when function is Right and value is Left
func TestApV_LeftValue(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + "; " + b
})
// Create the validation applicative
applyV := ApV[int, string, int](sg)
// Function is Right, value is Left - should return value's error
fa := Left[int]("value error")
fab := Right[string](N.Mul(2))
result := applyV(fa)(fab)
assert.True(t, IsLeft(result))
assert.Equal(t, Left[int]("value error"), result)
}
// TestApV_Composition tests ApV with function composition
func TestApV_Composition(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + " & " + b
})
// Create the validation applicative
applyV := ApV[string, string, int](sg)
// Test composition with pipe
fa := Right[string](10)
fab := Right[string](func(x int) string {
return F.Pipe1(x, func(n int) string {
if n >= 10 {
return "large"
}
return "small"
})
})
result := F.Pipe1(fa, applyV)(fab)
assert.True(t, IsRight(result))
assert.Equal(t, Right[string]("large"), result)
}
// TestApV_WithStructSemigroup tests ApV with a custom struct semigroup
func TestApV_WithStructSemigroup(t *testing.T) {
type ValidationErrors struct {
Errors []string
}
// Create a semigroup that combines validation errors
sg := S.MakeSemigroup(func(a, b ValidationErrors) ValidationErrors {
return ValidationErrors{
Errors: append(append([]string{}, a.Errors...), b.Errors...),
}
})
// Create the validation applicative
applyV := ApV[int, ValidationErrors, int](sg)
// Both are Left with validation errors
fa := Left[int](ValidationErrors{Errors: []string{"field1: required"}})
fab := Left[func(int) int](ValidationErrors{Errors: []string{"field2: invalid"}})
result := applyV(fa)(fab)
assert.True(t, IsLeft(result))
// When both are Left, errors are combined as: fa errors + fab errors
expected := Left[int](ValidationErrors{
Errors: []string{"field1: required", "field2: invalid"},
})
assert.Equal(t, expected, result)
}
// TestApV_MultipleValidations tests ApV with multiple validation steps
func TestApV_MultipleValidations(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + ", " + b
})
// Create the validation applicative
applyV := ApV[int, string, int](sg)
// Simulate multiple validation failures
validation1 := Left[int]("age must be positive")
validation2 := Left[func(int) int]("name is required")
result := applyV(validation1)(validation2)
assert.True(t, IsLeft(result))
// When both are Left, errors are combined as: validation1 error + validation2 error
assert.Equal(t, Left[int]("age must be positive, name is required"), result)
}
// TestMonadApV_DifferentTypes tests MonadApV with different input and output types
func TestMonadApV_DifferentTypes(t *testing.T) {
// Create a semigroup for string concatenation
sg := S.MakeSemigroup(func(a, b string) string {
return a + " + " + b
})
// Create the validation applicative
applyV := MonadApV[string, string, int](sg)
// Function converts int to string
fab := Right[string](func(x int) string {
return F.Pipe1(x, func(n int) string {
if n == 0 {
return "zero"
} else if n > 0 {
return "positive"
}
return "negative"
})
})
fa := Right[string](-5)
result := applyV(fab, fa)
assert.True(t, IsRight(result))
assert.Equal(t, Right[string]("negative"), result)
}
// TestApV_FirstSemigroup tests ApV with First semigroup (always returns first error)
func TestApV_FirstSemigroup(t *testing.T) {
// Use First semigroup which always returns the first value
sg := S.First[string]()
// Create the validation applicative
applyV := ApV[int, string, int](sg)
// Both are Left - should return first error
fa := Left[int]("error2")
fab := Left[func(int) int]("error1")
result := applyV(fa)(fab)
assert.True(t, IsLeft(result))
// First semigroup returns the first value, which is fa's error
assert.Equal(t, Left[int]("error2"), result)
}
// TestApV_LastSemigroup tests ApV with Last semigroup (always returns last error)
func TestApV_LastSemigroup(t *testing.T) {
// Use Last semigroup which always returns the last value
sg := S.Last[string]()
// Create the validation applicative
applyV := ApV[int, string, int](sg)
// Both are Left - should return last error
fa := Left[int]("error2")
fab := Left[func(int) int]("error1")
result := applyV(fa)(fab)
assert.True(t, IsLeft(result))
// Last semigroup returns the last value, which is fab's error
assert.Equal(t, Left[int]("error1"), result)
}
// Made with Bob

View File

@@ -36,7 +36,7 @@
// )
//
// // Define some endomorphisms
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
//
// // Compose them (RIGHT-TO-LEFT execution)
@@ -62,7 +62,7 @@
//
// // Combine multiple endomorphisms (RIGHT-TO-LEFT execution)
// combined := M.ConcatAll(monoid)(
// func(x int) int { return x * 2 }, // applied third
// N.Mul(2), // applied third
// func(x int) int { return x + 1 }, // applied second
// func(x int) int { return x * 3 }, // applied first
// )
@@ -74,7 +74,7 @@
// MonadChain executes LEFT-TO-RIGHT, unlike Compose:
//
// // Chain allows sequencing of endomorphisms (LEFT-TO-RIGHT)
// f := func(x int) int { return x * 2 }
// f := N.Mul(2)
// g := func(x int) int { return x + 1 }
// chained := endomorphism.MonadChain(f, g) // f first, then g
// result := chained(5) // (5 * 2) + 1 = 11
@@ -83,7 +83,7 @@
//
// The key difference between Compose and Chain/MonadChain is execution order:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
//
// // Compose: RIGHT-TO-LEFT (mathematical composition)

View File

@@ -37,7 +37,7 @@ import (
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
// result := endomorphism.MonadAp(double, increment) // Composes: double ∘ increment
// // result(5) = double(increment(5)) = double(6) = 12
@@ -64,7 +64,7 @@ func MonadAp[A any](fab Endomorphism[A], fa Endomorphism[A]) Endomorphism[A] {
//
// increment := func(x int) int { return x + 1 }
// applyIncrement := endomorphism.Ap(increment)
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// composed := applyIncrement(double) // double ∘ increment
// // composed(5) = double(increment(5)) = double(6) = 12
func Ap[A any](fa Endomorphism[A]) Operator[A] {
@@ -91,7 +91,7 @@ func Ap[A any](fa Endomorphism[A]) Operator[A] {
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
//
// // MonadCompose executes RIGHT-TO-LEFT: increment first, then double
@@ -123,7 +123,7 @@ func MonadCompose[A any](f, g Endomorphism[A]) Endomorphism[A] {
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
// mapped := endomorphism.MonadMap(double, increment)
// // mapped(5) = double(increment(5)) = double(6) = 12
@@ -153,7 +153,7 @@ func MonadMap[A any](f, g Endomorphism[A]) Endomorphism[A] {
//
// increment := func(x int) int { return x + 1 }
// composeWithIncrement := endomorphism.Compose(increment)
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
//
// // Composes double with increment (RIGHT-TO-LEFT: increment first, then double)
// composed := composeWithIncrement(double)
@@ -186,7 +186,7 @@ func Compose[A any](g Endomorphism[A]) Operator[A] {
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// mapDouble := endomorphism.Map(double)
// increment := func(x int) int { return x + 1 }
// mapped := mapDouble(increment)
@@ -215,7 +215,7 @@ func Map[A any](f Endomorphism[A]) Operator[A] {
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
//
// // MonadChain executes LEFT-TO-RIGHT: double first, then increment
@@ -243,7 +243,7 @@ func MonadChain[A any](ma Endomorphism[A], f Endomorphism[A]) Endomorphism[A] {
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// log := func(x int) int { fmt.Println(x); return x }
// chained := endomorphism.MonadChainFirst(double, log)
// result := chained(5) // Prints 10, returns 10
@@ -269,7 +269,7 @@ func MonadChainFirst[A any](ma Endomorphism[A], f Endomorphism[A]) Endomorphism[
//
// log := func(x int) int { fmt.Println(x); return x }
// chainLog := endomorphism.ChainFirst(log)
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// chained := chainLog(double)
// result := chained(5) // Prints 10, returns 10
func ChainFirst[A any](f Endomorphism[A]) Operator[A] {
@@ -296,7 +296,7 @@ func ChainFirst[A any](f Endomorphism[A]) Operator[A] {
//
// increment := func(x int) int { return x + 1 }
// chainWithIncrement := endomorphism.Chain(increment)
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
//
// // Chains double (first) with increment (second)
// chained := chainWithIncrement(double)

View File

@@ -19,6 +19,7 @@ import (
"testing"
M "github.com/IBM/fp-go/v2/monoid"
N "github.com/IBM/fp-go/v2/number"
S "github.com/IBM/fp-go/v2/semigroup"
"github.com/stretchr/testify/assert"
)
@@ -204,7 +205,7 @@ func TestCompose(t *testing.T) {
// TestMonadComposeVsCompose demonstrates the relationship between MonadCompose and Compose
func TestMonadComposeVsCompose(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
increment := func(x int) int { return x + 1 }
// MonadCompose takes both functions at once
@@ -448,7 +449,7 @@ func TestOperatorType(t *testing.T) {
func BenchmarkCompose(b *testing.B) {
composed := MonadCompose(double, increment)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = composed(5)
}
}
@@ -456,7 +457,7 @@ func BenchmarkCompose(b *testing.B) {
// BenchmarkMonoidConcatAll benchmarks ConcatAll with monoid
// TestComposeVsChain demonstrates the key difference between Compose and Chain
func TestComposeVsChain(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
increment := func(x int) int { return x + 1 }
// Compose executes RIGHT-TO-LEFT
@@ -499,7 +500,7 @@ func BenchmarkMonoidConcatAll(b *testing.B) {
monoid := Monoid[int]()
combined := M.ConcatAll(monoid)([]Endomorphism[int]{double, increment, square})
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = combined(5)
}
}
@@ -509,7 +510,7 @@ func BenchmarkChain(b *testing.B) {
chainWithIncrement := Chain(increment)
chained := chainWithIncrement(double)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = chained(5)
}
}
@@ -704,7 +705,7 @@ func TestApEqualsCompose(t *testing.T) {
// TestChainFirst tests the ChainFirst operation
func TestChainFirst(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
// Track side effect
var sideEffect int

View File

@@ -35,7 +35,7 @@ import (
//
// Example:
//
// myFunc := func(x int) int { return x * 2 }
// myFunc := N.Mul(2)
// endo := endomorphism.Of(myFunc)
func Of[F ~func(A) A, A any](f F) Endomorphism[A] {
return f
@@ -75,7 +75,7 @@ func Unwrap[F ~func(A) A, A any](f Endomorphism[A]) F {
// result := id(42) // Returns: 42
//
// // Identity is neutral for composition
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// composed := endomorphism.Compose(id, double)
// // composed behaves exactly like double
func Identity[A any]() Endomorphism[A] {
@@ -103,7 +103,7 @@ func Identity[A any]() Endomorphism[A] {
// import S "github.com/IBM/fp-go/v2/semigroup"
//
// sg := endomorphism.Semigroup[int]()
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
//
// // Combine using the semigroup (RIGHT-TO-LEFT execution)
@@ -139,7 +139,7 @@ func Semigroup[A any]() S.Semigroup[Endomorphism[A]] {
// import M "github.com/IBM/fp-go/v2/monoid"
//
// monoid := endomorphism.Monoid[int]()
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
// square := func(x int) int { return x * x }
//

View File

@@ -29,7 +29,7 @@ type (
// Example:
//
// // Simple endomorphisms on integers
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// increment := func(x int) int { return x + 1 }
//
// // Both are endomorphisms of type Endomorphism[int]

View File

@@ -23,6 +23,7 @@ import (
E "github.com/IBM/fp-go/v2/either"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
"github.com/stretchr/testify/assert"
)
@@ -266,7 +267,7 @@ func TestEither(t *testing.T) {
erased := Erase(42)
result := F.Pipe1(
SafeUnerase[int](erased),
E.Map[error](func(x int) int { return x * 2 }),
E.Map[error](N.Mul(2)),
)
assert.True(t, E.IsRight(result))

View File

@@ -23,6 +23,7 @@ import (
F "github.com/IBM/fp-go/v2/function"
M "github.com/IBM/fp-go/v2/monoid"
N "github.com/IBM/fp-go/v2/number"
S "github.com/IBM/fp-go/v2/semigroup"
T "github.com/IBM/fp-go/v2/tuple"
"github.com/stretchr/testify/assert"
@@ -55,14 +56,14 @@ func TestMapTo(t *testing.T) {
// Test MonadApSeq
func TestMonadApSeq(t *testing.T) {
f := Of(func(x int) int { return x * 2 })
f := Of(N.Mul(2))
result := MonadApSeq(f, Of(21))
assert.Equal(t, 42, result())
}
// Test ApPar
func TestApPar(t *testing.T) {
f := Of(func(x int) int { return x * 2 })
f := Of(N.Mul(2))
result := F.Pipe1(f, ApPar[int](Of(21)))
assert.Equal(t, 42, result())
}
@@ -128,14 +129,14 @@ func TestDefer(t *testing.T) {
// Test MonadFlap
func TestMonadFlap(t *testing.T) {
f := Of(func(x int) int { return x * 2 })
f := Of(N.Mul(2))
result := MonadFlap(f, 21)
assert.Equal(t, 42, result())
}
// Test Flap
func TestFlap(t *testing.T) {
f := Of(func(x int) int { return x * 2 })
f := Of(N.Mul(2))
result := F.Pipe1(f, Flap[int](21))
assert.Equal(t, 42, result())
}
@@ -355,7 +356,7 @@ func TestApplicativeTypeClass(t *testing.T) {
assert.Equal(t, 21, io1())
// Test Map
io2 := app.Map(func(x int) int { return x * 2 })(io1)
io2 := app.Map(N.Mul(2))(io1)
assert.Equal(t, 42, io2())
}

View File

@@ -33,7 +33,7 @@
//
// // Create a sequence and transform it
// seq := From(1, 2, 3, 4, 5)
// doubled := Map(func(x int) int { return x * 2 })(seq)
// doubled := Map(N.Mul(2))(seq)
//
// // Filter and reduce
// evens := Filter(func(x int) bool { return x%2 == 0 })(doubled)
@@ -83,7 +83,7 @@ func Of2[K, A any](k K, a A) Seq2[K, A] {
// Example:
//
// seq := From(1, 2, 3)
// result := MonadMap(seq, func(x int) int { return x * 2 })
// result := MonadMap(seq, N.Mul(2))
// // yields: 2, 4, 6
func MonadMap[A, B any](as Seq[A], f func(A) B) Seq[B] {
return func(yield Predicate[B]) {
@@ -100,7 +100,7 @@ func MonadMap[A, B any](as Seq[A], f func(A) B) Seq[B] {
//
// Example:
//
// double := Map(func(x int) int { return x * 2 })
// double := Map(N.Mul(2))
// seq := From(1, 2, 3)
// result := double(seq)
// // yields: 2, 4, 6
@@ -476,7 +476,7 @@ func Flatten[A any](mma Seq[Seq[A]]) Seq[A] {
//
// Example:
//
// fns := From(func(x int) int { return x * 2 }, func(x int) int { return x + 10 })
// fns := From(N.Mul(2), func(x int) int { return x + 10 })
// vals := From(5, 3)
// result := MonadAp(fns, vals)
// // yields: 10, 6, 15, 13 (each function applied to each value)
@@ -492,7 +492,7 @@ func MonadAp[B, A any](fab Seq[func(A) B], fa Seq[A]) Seq[B] {
// Example:
//
// applyTo5 := Ap(From(5))
// fns := From(func(x int) int { return x * 2 }, func(x int) int { return x + 10 })
// fns := From(N.Mul(2), func(x int) int { return x + 10 })
// result := applyTo5(fns)
// // yields: 10, 15
//
@@ -799,7 +799,7 @@ func FoldMapWithKey[K, A, B any](m M.Monoid[B]) func(func(K, A) B) func(Seq2[K,
//
// Example:
//
// fns := From(func(x int) int { return x * 2 }, func(x int) int { return x + 10 })
// fns := From(N.Mul(2), func(x int) int { return x + 10 })
// result := MonadFlap(fns, 5)
// // yields: 10, 15
//

View File

@@ -23,6 +23,7 @@ import (
"testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
O "github.com/IBM/fp-go/v2/option"
S "github.com/IBM/fp-go/v2/string"
"github.com/stretchr/testify/assert"
@@ -64,14 +65,14 @@ func TestEmpty(t *testing.T) {
func TestMonadMap(t *testing.T) {
seq := From(1, 2, 3)
doubled := MonadMap(seq, func(x int) int { return x * 2 })
doubled := MonadMap(seq, N.Mul(2))
result := toSlice(doubled)
assert.Equal(t, []int{2, 4, 6}, result)
}
func TestMap(t *testing.T) {
seq := From(1, 2, 3)
double := Map(func(x int) int { return x * 2 })
double := Map(N.Mul(2))
result := toSlice(double(seq))
assert.Equal(t, []int{2, 4, 6}, result)
}
@@ -249,7 +250,7 @@ func TestFlatten(t *testing.T) {
func TestMonadAp(t *testing.T) {
fns := From(
func(x int) int { return x * 2 },
N.Mul(2),
func(x int) int { return x + 10 },
)
vals := From(1, 2)
@@ -259,7 +260,7 @@ func TestMonadAp(t *testing.T) {
func TestAp(t *testing.T) {
fns := From(
func(x int) int { return x * 2 },
N.Mul(2),
func(x int) int { return x + 10 },
)
vals := From(1, 2)
@@ -423,7 +424,7 @@ func TestFoldMapWithKey(t *testing.T) {
func TestMonadFlap(t *testing.T) {
fns := From(
func(x int) int { return x * 2 },
N.Mul(2),
func(x int) int { return x + 10 },
)
result := MonadFlap(fns, 5)
@@ -432,7 +433,7 @@ func TestMonadFlap(t *testing.T) {
func TestFlap(t *testing.T) {
fns := From(
func(x int) int { return x * 2 },
N.Mul(2),
func(x int) int { return x + 10 },
)
flapper := Flap[int](5)

View File

@@ -58,7 +58,7 @@
// // Transform it
// doubled := F.Pipe1(
// computation,
// lazy.Map(func(x int) int { return x * 2 }),
// lazy.Map(N.Mul(2)),
// )
//
// // Evaluate when needed

View File

@@ -71,7 +71,7 @@ func MonadOf[A any](a A) Lazy[A] {
// Example:
//
// computation := lazy.Of(5)
// doubled := lazy.MonadMap(computation, func(x int) int { return x * 2 })
// doubled := lazy.MonadMap(computation, N.Mul(2))
// result := doubled() // 10
func MonadMap[A, B any](fa Lazy[A], f func(A) B) Lazy[B] {
return io.MonadMap(fa, f)
@@ -84,7 +84,7 @@ func MonadMap[A, B any](fa Lazy[A], f func(A) B) Lazy[B] {
//
// Example:
//
// double := lazy.Map(func(x int) int { return x * 2 })
// double := lazy.Map(N.Mul(2))
// computation := lazy.Of(5)
// result := double(computation)() // 10
//
@@ -141,7 +141,7 @@ func Chain[A, B any](f Kleisli[A, B]) Kleisli[Lazy[A], B] {
//
// Example:
//
// lazyFunc := lazy.Of(func(x int) int { return x * 2 })
// lazyFunc := lazy.Of(N.Mul(2))
// lazyValue := lazy.Of(5)
// result := lazy.MonadAp(lazyFunc, lazyValue)() // 10
func MonadAp[B, A any](mab Lazy[func(A) B], ma Lazy[A]) Lazy[B] {
@@ -157,7 +157,7 @@ func MonadAp[B, A any](mab Lazy[func(A) B], ma Lazy[A]) Lazy[B] {
//
// lazyValue := lazy.Of(5)
// applyTo5 := lazy.Ap[int](lazyValue)
// lazyFunc := lazy.Of(func(x int) int { return x * 2 })
// lazyFunc := lazy.Of(N.Mul(2))
// result := applyTo5(lazyFunc)() // 10
func Ap[B, A any](ma Lazy[A]) func(Lazy[func(A) B]) Lazy[B] {
return io.ApSeq[B](ma)

View File

@@ -23,6 +23,7 @@ import (
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/utils"
M "github.com/IBM/fp-go/v2/monoid"
N "github.com/IBM/fp-go/v2/number"
L "github.com/IBM/fp-go/v2/optics/lens"
"github.com/stretchr/testify/assert"
)
@@ -54,7 +55,7 @@ func TestMonadOf(t *testing.T) {
}
func TestMonadMap(t *testing.T) {
result := MonadMap(Of(5), func(x int) int { return x * 2 })
result := MonadMap(Of(5), N.Mul(2))
assert.Equal(t, 10, result())
}
@@ -103,7 +104,7 @@ func TestChainTo(t *testing.T) {
}
func TestMonadAp(t *testing.T) {
lazyFunc := Of(func(x int) int { return x * 2 })
lazyFunc := Of(N.Mul(2))
lazyValue := Of(5)
result := MonadAp(lazyFunc, lazyValue)
assert.Equal(t, 10, result())
@@ -494,7 +495,7 @@ func TestMapComposition(t *testing.T) {
// Test mapping multiple transformations
result := F.Pipe3(
Of(5),
Map(func(x int) int { return x * 2 }),
Map(N.Mul(2)),
Map(func(x int) int { return x + 10 }),
Map(func(x int) int { return x }),
)

View File

@@ -17,7 +17,7 @@ type (
// computation := lazy.Of(42)
//
// // Transform it (not evaluated yet)
// doubled := lazy.Map(func(x int) int { return x * 2 })(computation)
// doubled := lazy.Map(N.Mul(2))(computation)
//
// // Evaluate when needed
// result := doubled() // 84
@@ -52,7 +52,7 @@ type (
// Example:
//
// // An operator that doubles the value in a lazy computation
// doubleOp := lazy.Map(func(x int) int { return x * 2 })
// doubleOp := lazy.Map(N.Mul(2))
//
// // Apply it to a lazy computation
// result := doubleOp(lazy.Of(5))() // 10

View File

@@ -247,7 +247,7 @@ func TestLoggingCallbacks_ConsecutiveCalls(t *testing.T) {
// BenchmarkLoggingCallbacks_NoLoggers benchmarks the no-logger case.
func BenchmarkLoggingCallbacks_NoLoggers(b *testing.B) {
for i := 0; i < b.N; i++ {
for b.Loop() {
LoggingCallbacks()
}
}
@@ -258,7 +258,7 @@ func BenchmarkLoggingCallbacks_OneLogger(b *testing.B) {
logger := log.New(&buf, "", 0)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
LoggingCallbacks(logger)
}
}
@@ -270,7 +270,7 @@ func BenchmarkLoggingCallbacks_TwoLoggers(b *testing.B) {
logger2 := log.New(&buf2, "", 0)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
LoggingCallbacks(logger1, logger2)
}
}

View File

@@ -690,7 +690,7 @@ func BenchmarkConcatAll(b *testing.B) {
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = concatAll(numbers)
}
}
@@ -710,7 +710,7 @@ func BenchmarkFunctionMonoid(b *testing.B) {
combined := funcMonoid.Concat(f1, f2)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_ = combined("benchmark")
}
}

View File

@@ -19,6 +19,7 @@ import (
"testing"
"time"
A "github.com/IBM/fp-go/v2/array"
"github.com/stretchr/testify/assert"
)
@@ -93,31 +94,31 @@ func TestLines(t *testing.T) {
iso := Lines()
t.Run("Get joins lines with newline", func(t *testing.T) {
lines := []string{"line1", "line2", "line3"}
lines := A.From("line1", "line2", "line3")
result := iso.Get(lines)
assert.Equal(t, "line1\nline2\nline3", result)
})
t.Run("Get handles single line", func(t *testing.T) {
lines := []string{"single line"}
lines := A.Of("single line")
result := iso.Get(lines)
assert.Equal(t, "single line", result)
})
t.Run("Get handles empty slice", func(t *testing.T) {
lines := []string{}
lines := A.Empty[string]()
result := iso.Get(lines)
assert.Equal(t, "", result)
})
t.Run("Get handles empty strings in slice", func(t *testing.T) {
lines := []string{"a", "", "b"}
lines := A.From("a", "", "b")
result := iso.Get(lines)
assert.Equal(t, "a\n\nb", result)
})
t.Run("Get handles slice with only empty strings", func(t *testing.T) {
lines := []string{"", "", ""}
lines := A.From("", "", "")
result := iso.Get(lines)
assert.Equal(t, "\n\n", result)
})
@@ -340,20 +341,20 @@ func TestLinesRoundTripLaws(t *testing.T) {
t.Run("Law 1: Empty slice special case", func(t *testing.T) {
// Empty slice becomes "" which splits to [""]
// This is expected behavior of strings.Split
original := []string{}
original := A.Empty[string]()
text := iso.Get(original) // ""
result := iso.ReverseGet(text) // [""]
assert.Equal(t, []string{""}, result)
})
t.Run("Law 2: Get(ReverseGet(str)) == str", func(t *testing.T) {
testCases := []string{
testCases := A.From(
"line1\nline2",
"single",
"",
"a\n\nb",
"\n\n",
}
)
for _, original := range testCases {
result := iso.Get(iso.ReverseGet(original))
@@ -367,13 +368,13 @@ func TestUnixMilliRoundTripLaws(t *testing.T) {
iso := UnixMilli()
t.Run("Law 1: ReverseGet(Get(millis)) == millis", func(t *testing.T) {
testCases := []int64{
testCases := A.From(
0,
1609459200000,
-86400000,
1234567890000,
time.Now().UnixMilli(),
}
)
for _, original := range testCases {
result := iso.ReverseGet(iso.Get(original))
@@ -382,12 +383,12 @@ func TestUnixMilliRoundTripLaws(t *testing.T) {
})
t.Run("Law 2: Get(ReverseGet(time)) == time (with millisecond precision)", func(t *testing.T) {
testCases := []time.Time{
testCases := A.From(
time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
time.Unix(0, 0).UTC(),
time.Date(1969, 12, 31, 0, 0, 0, 0, time.UTC),
time.Now().Truncate(time.Millisecond),
}
)
for _, original := range testCases {
result := iso.Get(iso.ReverseGet(original))
@@ -410,7 +411,7 @@ func TestIsosComposition(t *testing.T) {
assert.Equal(t, []string{"line1", "line2", "line3"}, lines)
// Reverse: lines to string to bytes
originalLines := []string{"a", "b", "c"}
originalLines := A.From("a", "b", "c")
text := linesIso.Get(originalLines)
resultBytes := utf8Iso.ReverseGet(text)
assert.Equal(t, []byte("a\nb\nc"), resultBytes)

View File

@@ -19,6 +19,7 @@ import (
"testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
"github.com/IBM/fp-go/v2/optics/iso"
O "github.com/IBM/fp-go/v2/option"
"github.com/stretchr/testify/assert"
@@ -211,7 +212,7 @@ func TestFromZeroWithModify(t *testing.T) {
t.Run("Modify applies transformation to non-zero value", func(t *testing.T) {
double := func(opt O.Option[int]) O.Option[int] {
return O.MonadMap(opt, func(x int) int { return x * 2 })
return O.MonadMap(opt, N.Mul(2))
}
result := iso.Modify[int](double)(isoInt)(5)
@@ -220,7 +221,7 @@ func TestFromZeroWithModify(t *testing.T) {
t.Run("Modify preserves zero value", func(t *testing.T) {
double := func(opt O.Option[int]) O.Option[int] {
return O.MonadMap(opt, func(x int) int { return x * 2 })
return O.MonadMap(opt, N.Mul(2))
}
result := iso.Modify[int](double)(isoInt)(0)
@@ -235,7 +236,7 @@ func TestFromZeroWithCompose(t *testing.T) {
// Create an isomorphism that doubles/halves values
doubleIso := iso.MakeIso(
func(opt O.Option[int]) O.Option[int] {
return O.MonadMap(opt, func(x int) int { return x * 2 })
return O.MonadMap(opt, N.Mul(2))
},
func(opt O.Option[int]) O.Option[int] {
return O.MonadMap(opt, func(x int) int { return x / 2 })

View File

@@ -20,6 +20,7 @@ import (
EQ "github.com/IBM/fp-go/v2/eq"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
"github.com/stretchr/testify/assert"
)
@@ -555,7 +556,7 @@ func TestModifyLaws(t *testing.T) {
// Modify composition: Modify(f ∘ g) = Modify(f) ∘ Modify(g)
t.Run("ModifyComposition", func(t *testing.T) {
f := func(x int) int { return x * 2 }
f := N.Mul(2)
g := func(x int) int { return x + 3 }
// Modify(f ∘ g)

View File

@@ -20,6 +20,7 @@ import (
EQT "github.com/IBM/fp-go/v2/eq/testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
ISO "github.com/IBM/fp-go/v2/optics/iso"
L "github.com/IBM/fp-go/v2/optics/lens"
LT "github.com/IBM/fp-go/v2/optics/lens/testing"
@@ -281,7 +282,7 @@ func TestFromIsoModify(t *testing.T) {
t.Run("ModifySomeValue", func(t *testing.T) {
config := Config{timeout: 30, retries: 3}
// Double the timeout value
modified := L.Modify[Config](O.Map(func(x int) int { return x * 2 }))(optTimeoutLens)(config)
modified := L.Modify[Config](O.Map(N.Mul(2)))(optTimeoutLens)(config)
assert.Equal(t, 60, modified.timeout)
})

View File

@@ -48,7 +48,7 @@ func _log[A any](left func(string, ...any), right func(string, ...any), prefix s
// result := F.Pipe2(
// Some(42),
// logger("step1"), // logs "step1: 42"
// Map(func(x int) int { return x * 2 }),
// Map(N.Mul(2)),
// ) // Some(84)
//
// result := F.Pipe1(

View File

@@ -93,7 +93,7 @@ func FromValidation[A, B any](f func(A) (B, bool)) Kleisli[A, B] {
//
// Example:
//
// fab := Some(func(x int) int { return x * 2 })
// fab := Some(N.Mul(2))
// fa := Some(5)
// result := MonadAp(fab, fa) // Some(10)
func MonadAp[B, A any](fab Option[func(A) B], fa Option[A]) Option[B] {
@@ -109,7 +109,7 @@ func MonadAp[B, A any](fab Option[func(A) B], fa Option[A]) Option[B] {
//
// fa := Some(5)
// applyTo5 := Ap[int](fa)
// fab := Some(func(x int) int { return x * 2 })
// fab := Some(N.Mul(2))
// result := applyTo5(fab) // Some(10)
func Ap[B, A any](fa Option[A]) Operator[func(A) B, B] {
return F.Bind2nd(MonadAp[B, A], fa)
@@ -121,7 +121,7 @@ func Ap[B, A any](fa Option[A]) Operator[func(A) B, B] {
// Example:
//
// fa := Some(5)
// result := MonadMap(fa, func(x int) int { return x * 2 }) // Some(10)
// result := MonadMap(fa, N.Mul(2)) // Some(10)
func MonadMap[A, B any](fa Option[A], f func(A) B) Option[B] {
return MonadChain(fa, F.Flow2(f, Some[B]))
}
@@ -131,7 +131,7 @@ func MonadMap[A, B any](fa Option[A], f func(A) B) Option[B] {
//
// Example:
//
// double := Map(func(x int) int { return x * 2 })
// double := Map(N.Mul(2))
// result := double(Some(5)) // Some(10)
// result := double(None[int]()) // None
func Map[A, B any](f func(a A) B) Operator[A, B] {
@@ -387,7 +387,7 @@ func Filter[A any](pred func(A) bool) Kleisli[Option[A], A] {
//
// Example:
//
// fab := Some(func(x int) int { return x * 2 })
// fab := Some(N.Mul(2))
// result := MonadFlap(fab, 5) // Some(10)
func MonadFlap[B, A any](fab Option[func(A) B], a A) Option[B] {
return FC.MonadFlap(MonadMap[func(A) B, B], fab, a)
@@ -398,7 +398,7 @@ func MonadFlap[B, A any](fab Option[func(A) B], a A) Option[B] {
// Example:
//
// applyFive := Flap[int](5)
// fab := Some(func(x int) int { return x * 2 })
// fab := Some(N.Mul(2))
// result := applyFive(fab) // Some(10)
func Flap[B, A any](a A) Operator[func(A) B, B] {
return FC.Flap(Map[func(A) B, B], a)

View File

@@ -22,6 +22,7 @@ import (
F "github.com/IBM/fp-go/v2/function"
M "github.com/IBM/fp-go/v2/monoid"
N "github.com/IBM/fp-go/v2/number"
P "github.com/IBM/fp-go/v2/pair"
S "github.com/IBM/fp-go/v2/semigroup"
T "github.com/IBM/fp-go/v2/tuple"
@@ -58,7 +59,7 @@ func TestFromValidation(t *testing.T) {
// Test MonadAp
func TestMonadAp(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
assert.Equal(t, Some(10), MonadAp(Some(double), Some(5)))
assert.Equal(t, None[int](), MonadAp(Some(double), None[int]()))
@@ -68,7 +69,7 @@ func TestMonadAp(t *testing.T) {
// Test MonadMap
func TestMonadMap(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
assert.Equal(t, Some(10), MonadMap(Some(5), double))
assert.Equal(t, None[int](), MonadMap(None[int](), double))
@@ -190,7 +191,7 @@ func TestFilter(t *testing.T) {
// Test MonadFlap
func TestMonadFlap(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
assert.Equal(t, Some(10), MonadFlap(Some(double), 5))
assert.Equal(t, None[int](), MonadFlap(None[func(int) int](), 5))
@@ -199,7 +200,7 @@ func TestMonadFlap(t *testing.T) {
// Test Flap
func TestFlap(t *testing.T) {
applyFive := Flap[int](5)
double := func(x int) int { return x * 2 }
double := N.Mul(2)
assert.Equal(t, Some(10), applyFive(Some(double)))
assert.Equal(t, None[int](), applyFive(None[func(int) int]()))

View File

@@ -17,12 +17,12 @@ package pair
import (
"fmt"
"strconv"
"testing"
EQ "github.com/IBM/fp-go/v2/eq"
M "github.com/IBM/fp-go/v2/monoid"
N "github.com/IBM/fp-go/v2/number"
SG "github.com/IBM/fp-go/v2/semigroup"
S "github.com/IBM/fp-go/v2/string"
"github.com/IBM/fp-go/v2/tuple"
"github.com/stretchr/testify/assert"
)
@@ -67,9 +67,7 @@ func TestFirstAndSecond(t *testing.T) {
func TestMonadMapHead(t *testing.T) {
p := MakePair(5, "hello")
p2 := MonadMapHead(p, func(n int) string {
return fmt.Sprintf("%d", n)
})
p2 := MonadMapHead(p, strconv.Itoa)
assert.Equal(t, "5", Head(p2))
assert.Equal(t, "hello", Tail(p2))
}
@@ -85,9 +83,7 @@ func TestMonadMapTail(t *testing.T) {
func TestMonadMap(t *testing.T) {
p := MakePair(10, "test")
p2 := MonadMap(p, func(n int) string {
return fmt.Sprintf("value: %d", n)
})
p2 := MonadMap(p, S.Format[int]("value: %d"))
assert.Equal(t, "value: 10", Head(p2))
assert.Equal(t, "test", Tail(p2))
}
@@ -95,17 +91,15 @@ func TestMonadMap(t *testing.T) {
func TestMonadBiMap(t *testing.T) {
p := MakePair(5, "hello")
p2 := MonadBiMap(p,
func(n int) string { return fmt.Sprintf("%d", n) },
func(s string) int { return len(s) },
strconv.Itoa,
S.Size,
)
assert.Equal(t, "5", Head(p2))
assert.Equal(t, 5, Tail(p2))
}
func TestMapHead(t *testing.T) {
mapper := MapHead[string](func(n int) string {
return fmt.Sprintf("%d", n)
})
mapper := MapHead[string](strconv.Itoa)
p := MakePair(42, "world")
p2 := mapper(p)
assert.Equal(t, "42", Head(p2))
@@ -134,8 +128,8 @@ func TestMap(t *testing.T) {
func TestBiMap(t *testing.T) {
mapper := BiMap(
func(n int) string { return fmt.Sprintf("n=%d", n) },
func(s string) int { return len(s) },
S.Format[int]("n=%d"),
S.Size,
)
p := MakePair(7, "hello")
p2 := mapper(p)
@@ -151,7 +145,7 @@ func TestSwap(t *testing.T) {
}
func TestMonadChainHead(t *testing.T) {
strConcat := SG.MakeSemigroup(func(a, b string) string { return a + b })
strConcat := S.Semigroup
p := MakePair(5, "hello")
p2 := MonadChainHead(strConcat, p, func(n int) Pair[string, string] {
return MakePair(fmt.Sprintf("%d", n), "!")
@@ -181,7 +175,7 @@ func TestMonadChain(t *testing.T) {
}
func TestChainHead(t *testing.T) {
strConcat := SG.MakeSemigroup(func(a, b string) string { return a + b })
strConcat := S.Semigroup
chain := ChainHead(strConcat, func(n int) Pair[string, string] {
return MakePair(fmt.Sprintf("%d", n), "!")
})
@@ -214,8 +208,8 @@ func TestChain(t *testing.T) {
}
func TestMonadApHead(t *testing.T) {
strConcat := SG.MakeSemigroup(func(a, b string) string { return a + b })
pf := MakePair(func(n int) string { return fmt.Sprintf("%d", n) }, "!")
strConcat := S.Semigroup
pf := MakePair(strconv.Itoa, "!")
pv := MakePair(42, "hello")
result := MonadApHead(strConcat, pf, pv)
assert.Equal(t, "42", Head(result))
@@ -224,7 +218,7 @@ func TestMonadApHead(t *testing.T) {
func TestMonadApTail(t *testing.T) {
intSum := N.SemigroupSum[int]()
pf := MakePair(10, func(s string) int { return len(s) })
pf := MakePair(10, S.Size)
pv := MakePair(5, "hello")
result := MonadApTail(intSum, pf, pv)
assert.Equal(t, 15, Head(result)) // 5 + 10
@@ -241,7 +235,7 @@ func TestMonadAp(t *testing.T) {
}
func TestApHead(t *testing.T) {
strConcat := SG.MakeSemigroup(func(a, b string) string { return a + b })
strConcat := S.Semigroup
pv := MakePair(100, "world")
ap := ApHead[string, int, string](strConcat, pv)
pf := MakePair(func(n int) string { return fmt.Sprintf("num=%d", n) }, "!")
@@ -254,7 +248,7 @@ func TestApTail(t *testing.T) {
intSum := N.SemigroupSum[int]()
pv := MakePair(20, "hello")
ap := ApTail[int, string, int](intSum, pv)
pf := MakePair(5, func(s string) int { return len(s) })
pf := MakePair(5, S.Size)
result := ap(pf)
assert.Equal(t, 25, Head(result)) // 20 + 5
assert.Equal(t, 5, Tail(result))
@@ -287,9 +281,7 @@ func TestUnpaired(t *testing.T) {
}
func TestMerge(t *testing.T) {
add := func(b int) func(a int) int {
return func(a int) int { return a + b }
}
add := N.Add[int]
merge := Merge(add)
result := merge(MakePair(3, 4))
assert.Equal(t, 7, result)
@@ -337,10 +329,7 @@ func TestFormat(t *testing.T) {
}
func TestMonadHead(t *testing.T) {
stringMonoid := M.MakeMonoid(
func(a, b string) string { return a + b },
"",
)
stringMonoid := S.Monoid
monad := MonadHead[int, string, string](stringMonoid)
// Test Of
@@ -349,7 +338,7 @@ func TestMonadHead(t *testing.T) {
assert.Equal(t, "", Tail(p))
// Test Map
mapper := monad.Map(func(n int) string { return fmt.Sprintf("%d", n) })
mapper := monad.Map(strconv.Itoa)
p2 := mapper(MakePair(100, "!"))
assert.Equal(t, "100", Head(p2))
assert.Equal(t, "!", Tail(p2))
@@ -372,10 +361,7 @@ func TestMonadHead(t *testing.T) {
}
func TestPointedHead(t *testing.T) {
stringMonoid := M.MakeMonoid(
func(a, b string) string { return a + b },
"",
)
stringMonoid := S.Monoid
pointed := PointedHead[int](stringMonoid)
p := pointed.Of(42)
assert.Equal(t, 42, Head(p))
@@ -392,10 +378,7 @@ func TestFunctorHead(t *testing.T) {
}
func TestApplicativeHead(t *testing.T) {
stringMonoid := M.MakeMonoid(
func(a, b string) string { return a + b },
"",
)
stringMonoid := S.Monoid
applicative := ApplicativeHead[int, string, string](stringMonoid)
// Test Of
@@ -404,7 +387,7 @@ func TestApplicativeHead(t *testing.T) {
assert.Equal(t, "", Tail(p))
// Test Map
mapper := applicative.Map(func(n int) string { return fmt.Sprintf("%d", n) })
mapper := applicative.Map(strconv.Itoa)
p2 := mapper(MakePair(42, "!"))
assert.Equal(t, "42", Head(p2))
assert.Equal(t, "!", Tail(p2))
@@ -428,7 +411,7 @@ func TestMonadTail(t *testing.T) {
assert.Equal(t, "hello", Tail(p))
// Test Map
mapper := monad.Map(func(s string) int { return len(s) })
mapper := monad.Map(S.Size)
p2 := mapper(MakePair(5, "world"))
assert.Equal(t, 5, Head(p2))
assert.Equal(t, 5, Tail(p2))
@@ -444,7 +427,7 @@ func TestMonadTail(t *testing.T) {
// Test Ap
pv := MakePair(5, "hello")
ap := monad.Ap(pv)
pf := MakePair(10, func(s string) int { return len(s) })
pf := MakePair(10, S.Size)
p4 := ap(pf)
assert.Equal(t, 15, Head(p4)) // 5 + 10
assert.Equal(t, 5, Tail(p4))
@@ -477,7 +460,7 @@ func TestApplicativeTail(t *testing.T) {
assert.Equal(t, "world", Tail(p))
// Test Map
mapper := applicative.Map(func(s string) int { return len(s) })
mapper := applicative.Map(S.Size)
p2 := mapper(MakePair(5, "test"))
assert.Equal(t, 5, Head(p2))
assert.Equal(t, 4, Tail(p2))
@@ -511,7 +494,7 @@ func TestPointed(t *testing.T) {
func TestFunctor(t *testing.T) {
functor := Functor[string, int, int]()
mapper := functor.Map(func(s string) int { return len(s) })
mapper := functor.Map(S.Size)
p := MakePair(7, "world")
p2 := mapper(p)
assert.Equal(t, 7, Head(p2))

View File

@@ -212,7 +212,7 @@ func Compose[C, R, B any](ab Reader[R, B]) func(Reader[B, C]) Reader[R, C] {
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// r := reader.First[int, int, string](double)
// result := r(tuple.MakeTuple2(5, "hello")) // (10, "hello")
func First[A, B, C any](pab Reader[A, B]) Reader[T.Tuple2[A, C], T.Tuple2[B, C]] {
@@ -226,7 +226,7 @@ func First[A, B, C any](pab Reader[A, B]) Reader[T.Tuple2[A, C], T.Tuple2[B, C]]
//
// Example:
//
// double := func(x int) int { return x * 2 }
// double := N.Mul(2)
// r := reader.Second[string, int, int](double)
// result := r(tuple.MakeTuple2("hello", 5)) // ("hello", 10)
func Second[A, B, C any](pbc Reader[B, C]) Reader[T.Tuple2[A, B], T.Tuple2[A, C]] {

View File

@@ -21,6 +21,7 @@ import (
"testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
T "github.com/IBM/fp-go/v2/tuple"
"github.com/stretchr/testify/assert"
@@ -134,14 +135,14 @@ func TestCompose(t *testing.T) {
}
func TestFirst(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
r := First[int, int, string](double)
result := r(T.MakeTuple2(5, "hello"))
assert.Equal(t, T.MakeTuple2(10, "hello"), result)
}
func TestSecond(t *testing.T) {
double := func(x int) int { return x * 2 }
double := N.Mul(2)
r := Second[string](double)
result := r(T.MakeTuple2("hello", 5))
assert.Equal(t, T.MakeTuple2("hello", 10), result)

View File

@@ -26,6 +26,7 @@ import (
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/io"
IOE "github.com/IBM/fp-go/v2/ioeither"
N "github.com/IBM/fp-go/v2/number"
O "github.com/IBM/fp-go/v2/option"
R "github.com/IBM/fp-go/v2/reader"
RE "github.com/IBM/fp-go/v2/readereither"
@@ -39,7 +40,7 @@ type testContext struct {
func TestMonadMap(t *testing.T) {
ctx := testContext{value: 10}
result := MonadMap(Of[testContext, error](5), func(x int) int { return x * 2 })
result := MonadMap(Of[testContext, error](5), N.Mul(2))
assert.Equal(t, E.Right[error](10), result(ctx)())
}
@@ -215,7 +216,7 @@ func TestChainOptionK(t *testing.T) {
func TestMonadApSeq(t *testing.T) {
ctx := testContext{value: 10}
fab := Of[testContext, error](func(x int) int { return x * 2 })
fab := Of[testContext, error](N.Mul(2))
fa := Of[testContext, error](5)
result := MonadApSeq(fab, fa)
assert.Equal(t, E.Right[error](10), result(ctx)())
@@ -223,7 +224,7 @@ func TestMonadApSeq(t *testing.T) {
func TestMonadApPar(t *testing.T) {
ctx := testContext{value: 10}
fab := Of[testContext, error](func(x int) int { return x * 2 })
fab := Of[testContext, error](N.Mul(2))
fa := Of[testContext, error](5)
result := MonadApPar(fab, fa)
assert.Equal(t, E.Right[error](10), result(ctx)())
@@ -550,7 +551,7 @@ func TestMemoize(t *testing.T) {
func TestMonadFlap(t *testing.T) {
ctx := testContext{value: 10}
fab := Of[testContext, error](func(x int) int { return x * 2 })
fab := Of[testContext, error](N.Mul(2))
result := MonadFlap(fab, 5)
assert.Equal(t, E.Right[error](10), result(ctx)())
}
@@ -558,7 +559,7 @@ func TestMonadFlap(t *testing.T) {
func TestFlap(t *testing.T) {
ctx := testContext{value: 10}
result := F.Pipe1(
Of[testContext, error](func(x int) int { return x * 2 }),
Of[testContext, error](N.Mul(2)),
Flap[testContext, error, int](5),
)
assert.Equal(t, E.Right[error](10), result(ctx)())

View File

@@ -84,7 +84,7 @@ type (
// - B: The output value type
//
// Example:
// var doubleOp Operator[Config, error, int, int] = Map(func(x int) int { return x * 2 })
// var doubleOp Operator[Config, error, int, int] = Map(N.Mul(2))
Operator[R, E, A, B any] = Kleisli[R, E, ReaderIOEither[R, E, A], B]
ReaderOption[R, A any] = readeroption.ReaderOption[R, A]

View File

@@ -25,6 +25,7 @@ import (
E "github.com/IBM/fp-go/v2/either"
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/ioresult"
N "github.com/IBM/fp-go/v2/number"
O "github.com/IBM/fp-go/v2/option"
R "github.com/IBM/fp-go/v2/reader"
RE "github.com/IBM/fp-go/v2/readereither"
@@ -39,7 +40,7 @@ type testContext struct {
func TestMonadMap(t *testing.T) {
ctx := testContext{value: 10}
res := MonadMap(Of[testContext](5), func(x int) int { return x * 2 })
res := MonadMap(Of[testContext](5), N.Mul(2))
assert.Equal(t, result.Of(10), res(ctx)())
}
@@ -215,7 +216,7 @@ func TestChainOptionK(t *testing.T) {
func TestMonadApSeq(t *testing.T) {
ctx := testContext{value: 10}
fab := Of[testContext](func(x int) int { return x * 2 })
fab := Of[testContext](N.Mul(2))
fa := Of[testContext](5)
res := MonadApSeq(fab, fa)
assert.Equal(t, result.Of(10), res(ctx)())
@@ -223,7 +224,7 @@ func TestMonadApSeq(t *testing.T) {
func TestMonadApPar(t *testing.T) {
ctx := testContext{value: 10}
fab := Of[testContext](func(x int) int { return x * 2 })
fab := Of[testContext](N.Mul(2))
fa := Of[testContext](5)
res := MonadApPar(fab, fa)
assert.Equal(t, result.Of(10), res(ctx)())
@@ -512,7 +513,7 @@ func TestMemoize(t *testing.T) {
func TestMonadFlap(t *testing.T) {
ctx := testContext{value: 10}
fab := Of[testContext](func(x int) int { return x * 2 })
fab := Of[testContext](N.Mul(2))
res := MonadFlap(fab, 5)
assert.Equal(t, result.Of(10), res(ctx)())
}
@@ -520,7 +521,7 @@ func TestMonadFlap(t *testing.T) {
func TestFlap(t *testing.T) {
ctx := testContext{value: 10}
res := F.Pipe1(
Of[testContext](func(x int) int { return x * 2 }),
Of[testContext](N.Mul(2)),
Flap[testContext, int](5),
)
assert.Equal(t, result.Of(10), res(ctx)())

View File

@@ -98,6 +98,6 @@ type (
// - B: The output value type
//
// Example:
// var doubleOp Operator[Config, error, int, int] = Map(func(x int) int { return x * 2 })
// var doubleOp Operator[Config, error, int, int] = Map(N.Mul(2))
Operator[R, A, B any] = Kleisli[R, ReaderIOResult[R, A], B]
)

View File

@@ -64,7 +64,7 @@ func SomeReader[E, A any](r Reader[E, A]) ReaderOption[E, A] {
// Example:
//
// ro := readeroption.Of[Config](42)
// doubled := readeroption.MonadMap(ro, func(x int) int { return x * 2 })
// doubled := readeroption.MonadMap(ro, N.Mul(2))
//
//go:inline
func MonadMap[E, A, B any](fa ReaderOption[E, A], f func(A) B) ReaderOption[E, B] {
@@ -78,7 +78,7 @@ func MonadMap[E, A, B any](fa ReaderOption[E, A], f func(A) B) ReaderOption[E, B
//
// doubled := F.Pipe1(
// readeroption.Of[Config](42),
// readeroption.Map[Config](func(x int) int { return x * 2 }),
// readeroption.Map[Config](N.Mul(2)),
// )
//
//go:inline

View File

@@ -23,7 +23,7 @@ import (
)
func TestUnionMonoid(t *testing.T) {
m := UnionMonoid[string](S.Semigroup())
m := UnionMonoid[string](S.Semigroup)
e := Empty[string, string]()

View File

@@ -56,7 +56,7 @@ func FromIO[IO ~func() A, A any](f IO) Result[A] {
//
// Example:
//
// fab := either.Right[error](func(x int) int { return x * 2 })
// fab := either.Right[error](N.Mul(2))
// fa := either.Right[error](21)
// result := either.MonadAp(fab, fa) // Right(42)
//
@@ -81,7 +81,7 @@ func Ap[B, A any](fa Result[A]) Operator[func(A) B, B] {
//
// result := either.MonadMap(
// either.Right[error](21),
// func(x int) int { return x * 2 },
// N.Mul(2),
// ) // Right(42)
//
//go:inline

View File

@@ -66,7 +66,7 @@ func TestUnwrapError(t *testing.T) {
func TestReduce(t *testing.T) {
s := S.Semigroup()
s := S.Semigroup
assert.Equal(t, "foobar", F.Pipe1(Right("bar"), Reduce(s.Concat, "foo")))
assert.Equal(t, "foo", F.Pipe1(Left[string](errors.New("bar")), Reduce(s.Concat, "foo")))

View File

@@ -33,7 +33,7 @@ import (
// result := F.Pipe2(
// either.Right[error](42),
// logger("Processing"),
// either.Map(func(x int) int { return x * 2 }),
// either.Map(N.Mul(2)),
// )
// // Logs: "Processing: 42"
// // result is Right(84)

View File

@@ -393,21 +393,21 @@ func TestMapSemigroup(t *testing.T) {
// Benchmark tests
func BenchmarkFirst(b *testing.B) {
first := First[int]()
for i := 0; i < b.N; i++ {
for b.Loop() {
first.Concat(1, 2)
}
}
func BenchmarkLast(b *testing.B) {
last := Last[int]()
for i := 0; i < b.N; i++ {
for b.Loop() {
last.Concat(1, 2)
}
}
func BenchmarkMakeSemigroupAdd(b *testing.B) {
add := MakeSemigroup(func(a, b int) int { return a + b })
for i := 0; i < b.N; i++ {
for b.Loop() {
add.Concat(1, 2)
}
}
@@ -415,7 +415,7 @@ func BenchmarkMakeSemigroupAdd(b *testing.B) {
func BenchmarkReverse(b *testing.B) {
sub := MakeSemigroup(func(a, b int) int { return a - b })
reversed := Reverse(sub)
for i := 0; i < b.N; i++ {
for b.Loop() {
reversed.Concat(10, 3)
}
}
@@ -426,7 +426,7 @@ func BenchmarkConcatAll(b *testing.B) {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
concatAll(0)(arr)
}
}
@@ -440,7 +440,7 @@ func BenchmarkFunctionSemigroup(b *testing.B) {
combined := funcSG.Concat(f, g)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
combined("hello")
}
}

View File

@@ -25,6 +25,4 @@ func concat(left string, right string) string {
return fmt.Sprintf("%s%s", left, right)
}
func Semigroup() S.Semigroup[string] {
return S.MakeSemigroup(concat)
}
var Semigroup = S.MakeSemigroup(concat)