Documentation
¶
Overview ¶
Package memo provides memoization primitives: lazy zero-arg evaluation, keyed function caching, and pluggable cache strategies. All primitives are concurrent-safe. From uses retry-on-panic semantics (matching stream's lazy evaluation). FnErr caches successes only — errors trigger retry on subsequent calls.
Index ¶
- func Fn[K comparable, V any](fn func(K) V) func(K) V
- func FnErr[K comparable, V any](fn func(K) (V, error)) func(K) (V, error)
- func FnErrWith[K comparable, V any](fn func(K) (V, error), cache Cache[K, V]) func(K) (V, error)
- func FnWith[K comparable, V any](fn func(K) V, cache Cache[K, V]) func(K) V
- func From[T any](fn func() T) func() T
- type Cache
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Fn ¶
func Fn[K comparable, V any](fn func(K) V) func(K) V
Fn wraps a single-arg function with an unbounded map cache. Cache-backed, not single-flight: concurrent misses for the same key may compute the value multiple times; the last store wins. Thread-safe. Panics if fn is nil.
Example ¶
package main
import (
"fmt"
"github.com/binaryphile/fluentfp/memo"
)
func main() {
calls := 0
// square caches results by input key.
square := memo.Fn(func(n int) int {
calls++
return n * n
})
fmt.Println(square(3))
fmt.Println(square(3)) // cached
fmt.Println(square(4)) // new key, computed
fmt.Println("calls:", calls)
}
Output: 9 9 16 calls: 2
func FnErr ¶
func FnErr[K comparable, V any](fn func(K) (V, error)) func(K) (V, error)
FnErr wraps a fallible single-arg function with an unbounded map cache. Only successful results are cached — errors trigger retry on subsequent calls. Cache-backed, not single-flight: concurrent misses for the same key may compute the value multiple times; the last store wins. Thread-safe. Panics if fn is nil.
Example ¶
package main
import (
"fmt"
"github.com/binaryphile/fluentfp/memo"
)
func main() {
calls := 0
// lookup caches successful results. Errors are not cached.
lookup := memo.FnErr(func(key string) (int, error) {
calls++
if key == "bad" {
return 0, fmt.Errorf("not found")
}
return len(key), nil
})
v, _ := lookup("hello")
fmt.Println(v)
v, _ = lookup("hello") // cached
fmt.Println(v)
_, err := lookup("bad") // not cached — will retry
fmt.Println(err)
fmt.Println("calls:", calls)
}
Output: 5 5 not found calls: 2
func FnErrWith ¶
func FnErrWith[K comparable, V any](fn func(K) (V, error), cache Cache[K, V]) func(K) (V, error)
FnErrWith wraps a fallible single-arg function with a caller-provided cache. Only successful results are cached — errors trigger retry on subsequent calls. Cache-backed, not single-flight: concurrent misses for the same key may compute the value multiple times; the last store wins. Thread-safe (cache must handle its own synchronization). Panics if fn or cache is nil.
func FnWith ¶
func FnWith[K comparable, V any](fn func(K) V, cache Cache[K, V]) func(K) V
FnWith wraps a single-arg function with a caller-provided cache. Cache-backed, not single-flight: concurrent misses for the same key may compute the value multiple times; the last store wins. Thread-safe (cache must handle its own synchronization). Panics if fn or cache is nil.
func From ¶ added in v0.60.0
func From[T any](fn func() T) func() T
From wraps a zero-arg function so it executes at most once on success. The result is cached and returned on subsequent calls. Thread-safe.
If fn panics, the cell resets to pending — subsequent calls retry the function. This differs from sync.Once, which poisons permanently on panic.
Reentrancy constraint: fn must not call the returned memoized function, directly or indirectly — this would deadlock on the internal mutex.
Panics if fn is nil.
Example ¶
package main
import (
"fmt"
"github.com/binaryphile/fluentfp/memo"
)
func main() {
calls := 0
// expensiveInit simulates a slow computation that runs once.
expensiveInit := memo.From(func() string {
calls++
return "initialized"
})
fmt.Println(expensiveInit())
fmt.Println(expensiveInit()) // cached — fn not called again
fmt.Println("calls:", calls)
}
Output: initialized initialized calls: 1
Types ¶
type Cache ¶
type Cache[K comparable, V any] interface { Load(key K) (V, bool) Store(key K, value V) }
Cache is a thread-safe key-value store for memoized results. Implementations must handle their own synchronization.
func NewLRU ¶
func NewLRU[K comparable, V any](capacity int) Cache[K, V]
NewLRU returns a concurrent-safe LRU cache that evicts the least recently used entry when capacity is exceeded. Panics if capacity <= 0.
Example ¶
package main
import (
"fmt"
"github.com/binaryphile/fluentfp/memo"
)
func main() {
// Bounded cache: evicts least-recently-used when full.
cache := memo.NewLRU[string, int](2) // capacity 2
square := memo.FnWith(func(s string) int { return len(s) }, cache)
square("ab")
square("cd")
square("ef") // evicts "ab"
// "ab" was evicted, so the cache won't have it.
// (Can't directly observe eviction in output, but the pattern shows usage.)
fmt.Println(square("ef")) // cached
fmt.Println(square("ab")) // recomputed
}
Output: 2 2
func NewMap ¶
func NewMap[K comparable, V any]() Cache[K, V]
NewMap returns an unbounded, concurrent-safe cache.