//// The cache is based on the [javascript_mutable_reference](https://github.com/lpil/javascript-mutable-reference) package. import gleam/dict.{type Dict} import javascript/mutable_reference.{type MutableReference} pub type Cache(k, v) = MutableReference(Dict(k, v)) /// Make a new memoization cache that will be used for the rest of the inner scope /// of the provided function. /// This is best used with a `use` expression: /// ```gleam /// use cache <- create() /// f(a, b, c, cache) /// ``` /// pub fn create(apply fun: fn(Cache(a, b)) -> c) -> c { dict.new() |> mutable_reference.new |> fun } /// Manually add a key-value pair to the memoization cache. /// Useful if you need to pre-seed the cache with a starting value, for example. pub fn set(in cache: Cache(k, v), for key: k, insert value: v) -> Nil { mutable_reference.update(cache, fn(d) { dict.insert(d, key, value) }) Nil } /// Manually look up a value from the memoization cache for a given key. /// Useful if you want to also return intermediate results as well as a final result, for example. pub fn get(from cache: Cache(k, v), fetch key: k) -> Result(v, Nil) { cache |> mutable_reference.get |> dict.get(key) } /// Look up the value associated with the given key in the memoization cache, /// and return it if it exists. If it doesn't exist, evaluate the callback function /// update the cache with the key and the corresponding value the callback returned, /// and return that value. /// /// This works well with a `use` expression: /// ```gleam /// fn f(a, b, c, cache) { /// use <- memoize(cache, #(a, b, c)) /// // function body goes here /// } /// ``` /// pub fn memoize(with cache: Cache(k, v), this key: k, apply fun: fn() -> v) -> v { case get(from: cache, fetch: key) { Ok(value) -> value Error(Nil) -> { let result = fun() set(cache, key, result) result } } }