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

506 lines
11 KiB
Go
Raw Normal View History

// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lazy
import (
"testing"
"time"
EQ "github.com/IBM/fp-go/v2/eq"
F "github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/internal/utils"
M "github.com/IBM/fp-go/v2/monoid"
L "github.com/IBM/fp-go/v2/optics/lens"
"github.com/stretchr/testify/assert"
)
func TestOf(t *testing.T) {
result := Of(42)
assert.Equal(t, 42, result())
}
func TestFromLazy(t *testing.T) {
original := func() int { return 42 }
wrapped := FromLazy(original)
assert.Equal(t, 42, wrapped())
}
func TestFromImpure(t *testing.T) {
counter := 0
impure := func() {
counter++
}
lazy := FromImpure(impure)
lazy()
assert.Equal(t, 1, counter)
}
func TestMonadOf(t *testing.T) {
result := MonadOf(42)
assert.Equal(t, 42, result())
}
func TestMonadMap(t *testing.T) {
result := MonadMap(Of(5), func(x int) int { return x * 2 })
assert.Equal(t, 10, result())
}
func TestMonadMapTo(t *testing.T) {
result := MonadMapTo(Of("ignored"), 42)
assert.Equal(t, 42, result())
}
func TestMapTo(t *testing.T) {
mapper := MapTo[string](42)
result := mapper(Of("ignored"))
assert.Equal(t, 42, result())
}
func TestMonadChain(t *testing.T) {
result := MonadChain(Of(5), func(x int) Lazy[int] {
return Of(x * 2)
})
assert.Equal(t, 10, result())
}
func TestMonadChainFirst(t *testing.T) {
result := MonadChainFirst(Of(5), func(x int) Lazy[string] {
return Of("ignored")
})
assert.Equal(t, 5, result())
}
func TestChainFirst(t *testing.T) {
chainer := ChainFirst(func(x int) Lazy[string] {
return Of("ignored")
})
result := chainer(Of(5))
assert.Equal(t, 5, result())
}
func TestMonadChainTo(t *testing.T) {
result := MonadChainTo(Of(5), Of(10))
assert.Equal(t, 10, result())
}
func TestChainTo(t *testing.T) {
chainer := ChainTo[int](Of(10))
result := chainer(Of(5))
assert.Equal(t, 10, result())
}
func TestMonadAp(t *testing.T) {
lazyFunc := Of(func(x int) int { return x * 2 })
lazyValue := Of(5)
result := MonadAp(lazyFunc, lazyValue)
assert.Equal(t, 10, result())
}
func TestMonadApFirst(t *testing.T) {
result := MonadApFirst(Of(5), Of(10))
assert.Equal(t, 5, result())
}
func TestMonadApSecond(t *testing.T) {
result := MonadApSecond(Of(5), Of(10))
assert.Equal(t, 10, result())
}
func TestNow(t *testing.T) {
before := time.Now()
result := Now()
after := time.Now()
assert.True(t, result.After(before) || result.Equal(before))
assert.True(t, result.Before(after) || result.Equal(after))
}
func TestDefer(t *testing.T) {
counter := 0
deferred := Defer(func() Lazy[int] {
counter++
return Of(counter)
})
// First execution
result1 := deferred()
assert.Equal(t, 1, result1)
// Second execution should generate a new computation
result2 := deferred()
assert.Equal(t, 2, result2)
}
func TestDo(t *testing.T) {
type State struct {
Value int
}
result := Do(State{Value: 42})
assert.Equal(t, State{Value: 42}, result())
}
func TestLet(t *testing.T) {
type State struct {
Value int
}
result := F.Pipe2(
Do(State{}),
Let(
func(v int) func(State) State {
return func(s State) State { s.Value = v; return s }
},
func(s State) int { return 42 },
),
Map(func(s State) int { return s.Value }),
)
assert.Equal(t, 42, result())
}
func TestLetTo(t *testing.T) {
type State struct {
Value int
}
result := F.Pipe2(
Do(State{}),
LetTo(
func(v int) func(State) State {
return func(s State) State { s.Value = v; return s }
},
42,
),
Map(func(s State) int { return s.Value }),
)
assert.Equal(t, 42, result())
}
func TestBindTo(t *testing.T) {
type State struct {
Value int
}
result := F.Pipe2(
Of(42),
BindTo(func(v int) State { return State{Value: v} }),
Map(func(s State) int { return s.Value }),
)
assert.Equal(t, 42, result())
}
func TestBindL(t *testing.T) {
type Config struct {
Port int
}
type State struct {
Config Config
}
// Create a lens manually
configLens := L.MakeLens(
func(s State) Config { return s.Config },
func(s State, cfg Config) State { s.Config = cfg; return s },
)
result := F.Pipe2(
Do(State{Config: Config{Port: 8080}}),
BindL(configLens, func(cfg Config) Lazy[Config] {
return Of(Config{Port: cfg.Port + 1})
}),
Map(func(s State) int { return s.Config.Port }),
)
assert.Equal(t, 8081, result())
}
func TestLetL(t *testing.T) {
type Config struct {
Port int
}
type State struct {
Config Config
}
// Create a lens manually
configLens := L.MakeLens(
func(s State) Config { return s.Config },
func(s State, cfg Config) State { s.Config = cfg; return s },
)
result := F.Pipe2(
Do(State{Config: Config{Port: 8080}}),
LetL(configLens, func(cfg Config) Config {
return Config{Port: cfg.Port + 1}
}),
Map(func(s State) int { return s.Config.Port }),
)
assert.Equal(t, 8081, result())
}
func TestLetToL(t *testing.T) {
type Config struct {
Port int
}
type State struct {
Config Config
}
// Create a lens manually
configLens := L.MakeLens(
func(s State) Config { return s.Config },
func(s State, cfg Config) State { s.Config = cfg; return s },
)
result := F.Pipe2(
Do(State{}),
LetToL(configLens, Config{Port: 8080}),
Map(func(s State) int { return s.Config.Port }),
)
assert.Equal(t, 8080, result())
}
func TestApSL(t *testing.T) {
type Config struct {
Port int
}
type State struct {
Config Config
}
// Create a lens manually
configLens := L.MakeLens(
func(s State) Config { return s.Config },
func(s State, cfg Config) State { s.Config = cfg; return s },
)
result := F.Pipe2(
Do(State{}),
ApSL(configLens, Of(Config{Port: 8080})),
Map(func(s State) int { return s.Config.Port }),
)
assert.Equal(t, 8080, result())
}
func TestSequenceT1(t *testing.T) {
result := SequenceT1(Of(42))
tuple := result()
assert.Equal(t, 42, tuple.F1)
}
func TestSequenceT2(t *testing.T) {
result := SequenceT2(Of(42), Of("hello"))
tuple := result()
assert.Equal(t, 42, tuple.F1)
assert.Equal(t, "hello", tuple.F2)
}
func TestSequenceT3(t *testing.T) {
result := SequenceT3(Of(42), Of("hello"), Of(true))
tuple := result()
assert.Equal(t, 42, tuple.F1)
assert.Equal(t, "hello", tuple.F2)
assert.Equal(t, true, tuple.F3)
}
func TestSequenceT4(t *testing.T) {
result := SequenceT4(Of(42), Of("hello"), Of(true), Of(3.14))
tuple := result()
assert.Equal(t, 42, tuple.F1)
assert.Equal(t, "hello", tuple.F2)
assert.Equal(t, true, tuple.F3)
assert.Equal(t, 3.14, tuple.F4)
}
func TestTraverseArray(t *testing.T) {
numbers := []int{1, 2, 3}
result := F.Pipe1(
numbers,
TraverseArray(func(x int) Lazy[int] {
return Of(x * 2)
}),
)
assert.Equal(t, []int{2, 4, 6}, result())
}
func TestTraverseArrayWithIndex(t *testing.T) {
numbers := []int{10, 20, 30}
result := F.Pipe1(
numbers,
TraverseArrayWithIndex(func(i int, x int) Lazy[int] {
return Of(x + i)
}),
)
assert.Equal(t, []int{10, 21, 32}, result())
}
func TestSequenceArray(t *testing.T) {
lazies := []Lazy[int]{Of(1), Of(2), Of(3)}
result := SequenceArray(lazies)
assert.Equal(t, []int{1, 2, 3}, result())
}
func TestMonadTraverseArray(t *testing.T) {
numbers := []int{1, 2, 3}
result := MonadTraverseArray(numbers, func(x int) Lazy[int] {
return Of(x * 2)
})
assert.Equal(t, []int{2, 4, 6}, result())
}
func TestTraverseRecord(t *testing.T) {
record := map[string]int{"a": 1, "b": 2}
result := F.Pipe1(
record,
TraverseRecord[string](func(x int) Lazy[int] {
return Of(x * 2)
}),
)
resultMap := result()
assert.Equal(t, 2, resultMap["a"])
assert.Equal(t, 4, resultMap["b"])
}
func TestTraverseRecordWithIndex(t *testing.T) {
record := map[string]int{"a": 10, "b": 20}
result := F.Pipe1(
record,
TraverseRecordWithIndex(func(k string, x int) Lazy[int] {
if k == "a" {
return Of(x + 1)
}
return Of(x + 2)
}),
)
resultMap := result()
assert.Equal(t, 11, resultMap["a"])
assert.Equal(t, 22, resultMap["b"])
}
func TestSequenceRecord(t *testing.T) {
record := map[string]Lazy[int]{
"a": Of(1),
"b": Of(2),
}
result := SequenceRecord(record)
resultMap := result()
assert.Equal(t, 1, resultMap["a"])
assert.Equal(t, 2, resultMap["b"])
}
func TestMonadTraverseRecord(t *testing.T) {
record := map[string]int{"a": 1, "b": 2}
result := MonadTraverseRecord(record, func(x int) Lazy[int] {
return Of(x * 2)
})
resultMap := result()
assert.Equal(t, 2, resultMap["a"])
assert.Equal(t, 4, resultMap["b"])
}
func TestApplySemigroup(t *testing.T) {
sg := ApplySemigroup(M.MakeMonoid(
func(a, b int) int { return a + b },
0,
))
result := sg.Concat(Of(5), Of(10))
assert.Equal(t, 15, result())
}
func TestApplicativeMonoid(t *testing.T) {
mon := ApplicativeMonoid(M.MakeMonoid(
func(a, b int) int { return a + b },
0,
))
// Test Empty
empty := mon.Empty()
assert.Equal(t, 0, empty())
// Test Concat
result := mon.Concat(Of(5), Of(10))
assert.Equal(t, 15, result())
// Test identity laws
left := mon.Concat(mon.Empty(), Of(5))
assert.Equal(t, 5, left())
right := mon.Concat(Of(5), mon.Empty())
assert.Equal(t, 5, right())
}
func TestEq(t *testing.T) {
eq := Eq(EQ.FromEquals(func(a, b int) bool { return a == b }))
assert.True(t, eq.Equals(Of(42), Of(42)))
assert.False(t, eq.Equals(Of(42), Of(43)))
}
func TestComplexDoNotation(t *testing.T) {
// Test a more complex do-notation scenario
result := F.Pipe3(
Do(utils.Empty),
Bind(utils.SetLastName, func(s utils.Initial) Lazy[string] {
return Of("Doe")
}),
Bind(utils.SetGivenName, func(s utils.WithLastName) Lazy[string] {
return Of("John")
}),
Map(utils.GetFullName),
)
assert.Equal(t, "John Doe", result())
}
func TestChainComposition(t *testing.T) {
// Test chaining multiple operations
double := func(x int) Lazy[int] {
return Of(x * 2)
}
addTen := func(x int) Lazy[int] {
return Of(x + 10)
}
result := F.Pipe2(
Of(5),
Chain(double),
Chain(addTen),
)
assert.Equal(t, 20, result())
}
func TestMapComposition(t *testing.T) {
// Test mapping multiple transformations
result := F.Pipe3(
Of(5),
Map(func(x int) int { return x * 2 }),
Map(func(x int) int { return x + 10 }),
Map(func(x int) int { return x }),
)
assert.Equal(t, 20, result())
}
// Made with Bob