// 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" ) // 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) } // 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 { return CacheCallback[func(F) F, func() func() T](kf, getOrCreate[K, T]()) } // 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 } } // 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 } } // CacheCallback converts a unary function into a unary function that caches the value depending on the parameter 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 { return func(f F) F { return func(a A) T { // cache entry return getOrCreate(kf(a), func() LT { return L.Memoize[LT](func() T { return f(a) }) })() } } }