// 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