mirror of
https://github.com/IBM/fp-go.git
synced 2025-11-23 22:14:53 +02:00
232 lines
7.4 KiB
Go
232 lines
7.4 KiB
Go
// 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 (
|
|
"time"
|
|
|
|
"github.com/IBM/fp-go/v2/io"
|
|
)
|
|
|
|
// Of creates a lazy computation that returns the given value.
|
|
// This is the most basic way to lift a value into the Lazy context.
|
|
//
|
|
// The computation is pure and will always return the same value when evaluated.
|
|
//
|
|
// Example:
|
|
//
|
|
// computation := lazy.Of(42)
|
|
// result := computation() // 42
|
|
func Of[A any](a A) Lazy[A] {
|
|
return io.Of(a)
|
|
}
|
|
|
|
// FromLazy creates a lazy computation from another lazy computation.
|
|
// This is an identity function that can be useful for type conversions or
|
|
// making the intent explicit in code.
|
|
//
|
|
// Example:
|
|
//
|
|
// original := func() int { return 42 }
|
|
// wrapped := lazy.FromLazy(original)
|
|
// result := wrapped() // 42
|
|
func FromLazy[A any](a Lazy[A]) Lazy[A] {
|
|
return io.FromIO(a)
|
|
}
|
|
|
|
// FromImpure converts a side effect without a return value into a side effect that returns any
|
|
func FromImpure(f func()) Lazy[any] {
|
|
return io.FromImpure(f)
|
|
}
|
|
|
|
// MonadOf creates a lazy computation that returns the given value.
|
|
// This is an alias for Of, provided for consistency with monadic naming conventions.
|
|
//
|
|
// Example:
|
|
//
|
|
// computation := lazy.MonadOf(42)
|
|
// result := computation() // 42
|
|
func MonadOf[A any](a A) Lazy[A] {
|
|
return io.MonadOf(a)
|
|
}
|
|
|
|
// MonadMap transforms the value inside a lazy computation using the provided function.
|
|
// The transformation is not applied until the lazy computation is evaluated.
|
|
//
|
|
// This is the monadic version of Map, taking the lazy computation as the first parameter.
|
|
//
|
|
// Example:
|
|
//
|
|
// computation := lazy.Of(5)
|
|
// 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)
|
|
}
|
|
|
|
// Map transforms the value inside a lazy computation using the provided function.
|
|
// Returns a function that can be applied to a lazy computation.
|
|
//
|
|
// This is the curried version of MonadMap, useful for function composition.
|
|
//
|
|
// Example:
|
|
//
|
|
// double := lazy.Map(N.Mul(2))
|
|
// computation := lazy.Of(5)
|
|
// result := double(computation)() // 10
|
|
//
|
|
// // Or with pipe:
|
|
// result := F.Pipe1(lazy.Of(5), double)() // 10
|
|
func Map[A, B any](f func(A) B) func(fa Lazy[A]) Lazy[B] {
|
|
return io.Map(f)
|
|
}
|
|
|
|
// MonadMapTo replaces the value inside a lazy computation with a constant value.
|
|
// The original computation is still evaluated, but its result is discarded.
|
|
//
|
|
// This is useful when you want to sequence computations but only care about
|
|
// the side effects (though Lazy should represent pure computations).
|
|
//
|
|
// Example:
|
|
//
|
|
// computation := lazy.Of("ignored")
|
|
// replaced := lazy.MonadMapTo(computation, 42)
|
|
// result := replaced() // 42
|
|
func MonadMapTo[A, B any](fa Lazy[A], b B) Lazy[B] {
|
|
return io.MonadMapTo(fa, b)
|
|
}
|
|
|
|
// MapTo replaces the value inside a lazy computation with a constant value.
|
|
// Returns a function that can be applied to a lazy computation.
|
|
//
|
|
// This is the curried version of MonadMapTo.
|
|
//
|
|
// Example:
|
|
//
|
|
// replaceWith42 := lazy.MapTo[string](42)
|
|
// computation := lazy.Of("ignored")
|
|
// result := replaceWith42(computation)() // 42
|
|
func MapTo[A, B any](b B) Kleisli[Lazy[A], B] {
|
|
return io.MapTo[A](b)
|
|
}
|
|
|
|
// MonadChain composes computations in sequence, using the return value of one computation to determine the next computation.
|
|
func MonadChain[A, B any](fa Lazy[A], f Kleisli[A, B]) Lazy[B] {
|
|
return io.MonadChain(fa, f)
|
|
}
|
|
|
|
// Chain composes computations in sequence, using the return value of one computation to determine the next computation.
|
|
func Chain[A, B any](f Kleisli[A, B]) Kleisli[Lazy[A], B] {
|
|
return io.Chain(f)
|
|
}
|
|
|
|
// MonadAp applies a lazy function to a lazy value.
|
|
// Both the function and the value are evaluated when the result is evaluated.
|
|
//
|
|
// This is the applicative functor operation, allowing you to apply functions
|
|
// that are themselves wrapped in a lazy context.
|
|
//
|
|
// Example:
|
|
//
|
|
// 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] {
|
|
return io.MonadApSeq(mab, ma)
|
|
}
|
|
|
|
// Ap applies a lazy function to a lazy value.
|
|
// Returns a function that takes a lazy function and returns a lazy result.
|
|
//
|
|
// This is the curried version of MonadAp, useful for function composition.
|
|
//
|
|
// Example:
|
|
//
|
|
// lazyValue := lazy.Of(5)
|
|
// applyTo5 := lazy.Ap[int](lazyValue)
|
|
// 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)
|
|
}
|
|
|
|
func Flatten[A any](mma Lazy[Lazy[A]]) Lazy[A] {
|
|
return io.Flatten(mma)
|
|
}
|
|
|
|
// Memoize computes the value of the provided [Lazy] monad lazily but exactly once
|
|
func Memoize[A any](ma Lazy[A]) Lazy[A] {
|
|
return io.Memoize(ma)
|
|
}
|
|
|
|
// MonadChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
|
// keeping only the result of the first.
|
|
func MonadChainFirst[A, B any](fa Lazy[A], f Kleisli[A, B]) Lazy[A] {
|
|
return io.MonadChainFirst(fa, f)
|
|
}
|
|
|
|
// ChainFirst composes computations in sequence, using the return value of one computation to determine the next computation and
|
|
// keeping only the result of the first.
|
|
func ChainFirst[A, B any](f Kleisli[A, B]) Kleisli[Lazy[A], A] {
|
|
return io.ChainFirst(f)
|
|
}
|
|
|
|
// MonadApFirst combines two effectful actions, keeping only the result of the first.
|
|
func MonadApFirst[A, B any](first Lazy[A], second Lazy[B]) Lazy[A] {
|
|
return io.MonadApFirst(first, second)
|
|
}
|
|
|
|
// ApFirst combines two effectful actions, keeping only the result of the first.
|
|
func ApFirst[A, B any](second Lazy[B]) Kleisli[Lazy[A], A] {
|
|
return io.ApFirst[A](second)
|
|
}
|
|
|
|
// MonadApSecond combines two effectful actions, keeping only the result of the second.
|
|
func MonadApSecond[A, B any](first Lazy[A], second Lazy[B]) Lazy[B] {
|
|
return io.MonadApSecond(first, second)
|
|
}
|
|
|
|
// ApSecond combines two effectful actions, keeping only the result of the second.
|
|
func ApSecond[A, B any](second Lazy[B]) Kleisli[Lazy[A], B] {
|
|
return io.ApSecond[A](second)
|
|
}
|
|
|
|
// MonadChainTo composes computations in sequence, ignoring the return value of the first computation
|
|
func MonadChainTo[A, B any](fa Lazy[A], fb Lazy[B]) Lazy[B] {
|
|
return io.MonadChainTo(fa, fb)
|
|
}
|
|
|
|
// ChainTo composes computations in sequence, ignoring the return value of the first computation
|
|
func ChainTo[A, B any](fb Lazy[B]) Kleisli[Lazy[A], B] {
|
|
return io.ChainTo[A](fb)
|
|
}
|
|
|
|
// Now is a lazy computation that returns the current timestamp when evaluated.
|
|
// Each evaluation will return the current time at the moment of evaluation.
|
|
//
|
|
// Example:
|
|
//
|
|
// time1 := lazy.Now()
|
|
// // ... some time passes ...
|
|
// time2 := lazy.Now()
|
|
// // time1 and time2 will be different
|
|
var Now Lazy[time.Time] = io.Now
|
|
|
|
// Defer creates an IO by creating a brand new IO via a generator function, each time
|
|
func Defer[A any](gen func() Lazy[A]) Lazy[A] {
|
|
return io.Defer(gen)
|
|
}
|