2023-08-31 10:27:32 +02:00
|
|
|
// Copyright (c) 2023 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 generic
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
L "github.com/IBM/fp-go/internal/lazy"
|
|
|
|
)
|
|
|
|
|
2023-09-10 21:29:01 +02:00
|
|
|
// Memoize converts a unary function into a unary function that caches the value depending on the parameter
|
|
|
|
func Memoize[F ~func(K) T, K comparable, T any](f F) F {
|
|
|
|
return ContramapMemoize[F](func(k K) K { return k })(f)
|
2023-08-31 10:27:32 +02:00
|
|
|
}
|
|
|
|
|
2023-09-10 21:29:01 +02:00
|
|
|
// ContramapMemoize converts a unary function into a unary function that caches the value depending on the parameter
|
|
|
|
func ContramapMemoize[F ~func(A) T, KF func(A) K, A any, K comparable, T any](kf KF) func(F) F {
|
2023-12-21 14:10:46 +01:00
|
|
|
return CacheCallback[func(F) F, func() func() T](kf, getOrCreate[K, T]())
|
2023-08-31 10:27:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// getOrCreate is a naive implementation of a cache, without bounds
|
|
|
|
func getOrCreate[K comparable, T any]() func(K, func() func() T) func() T {
|
|
|
|
cache := make(map[K]func() T)
|
|
|
|
var l sync.Mutex
|
|
|
|
|
|
|
|
return func(k K, cb func() func() T) func() T {
|
|
|
|
// only lock to access a lazy accessor to the value
|
|
|
|
l.Lock()
|
|
|
|
existing, ok := cache[k]
|
|
|
|
if !ok {
|
|
|
|
existing = cb()
|
|
|
|
cache[k] = existing
|
|
|
|
}
|
|
|
|
l.Unlock()
|
|
|
|
// compute the value outside of the lock
|
|
|
|
return existing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-21 14:10:46 +01:00
|
|
|
// SingleElementCache is a cache with a capacity of a single element
|
|
|
|
func SingleElementCache[
|
|
|
|
LLT ~func() LT, // generator of the generator
|
|
|
|
K comparable, // key into the cache
|
|
|
|
LT ~func() T, // generator of a value
|
|
|
|
T any, // the cached data type
|
|
|
|
]() func(K, LLT) LT {
|
|
|
|
var l sync.Mutex
|
|
|
|
|
|
|
|
var key K
|
|
|
|
var value LT
|
|
|
|
hasKey := false
|
|
|
|
|
|
|
|
return func(k K, gen LLT) LT {
|
|
|
|
l.Lock()
|
|
|
|
|
|
|
|
existing := value
|
|
|
|
if !hasKey || key != k {
|
|
|
|
existing = gen()
|
|
|
|
// update state
|
|
|
|
key = k
|
|
|
|
value = existing
|
|
|
|
hasKey = true
|
|
|
|
}
|
|
|
|
|
|
|
|
l.Unlock()
|
|
|
|
|
|
|
|
return existing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 10:27:32 +02:00
|
|
|
// CacheCallback converts a unary function into a unary function that caches the value depending on the parameter
|
2023-12-21 14:10:46 +01:00
|
|
|
func CacheCallback[
|
|
|
|
EM ~func(F) F, // endomorphism of the function
|
|
|
|
LLT ~func() LT, // generator of the generator
|
|
|
|
LT ~func() T, // generator of a value
|
|
|
|
F ~func(A) T, // function to actually cache
|
|
|
|
KF func(A) K, // extracts the cache key from the input
|
|
|
|
C ~func(K, LLT) LT, // the cache callback function
|
|
|
|
A any, K comparable, T any](kf KF, getOrCreate C) EM {
|
2023-08-31 10:27:32 +02:00
|
|
|
return func(f F) F {
|
|
|
|
return func(a A) T {
|
|
|
|
// cache entry
|
2023-12-21 14:10:46 +01:00
|
|
|
return getOrCreate(kf(a), func() LT {
|
|
|
|
return L.Memoize[LT](func() T {
|
2023-08-31 10:27:32 +02:00
|
|
|
return f(a)
|
|
|
|
})
|
|
|
|
})()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|