:sig
function kv_cache(key : string, regenerate : function() dval, ttl_seconds : u64 = 3600) dval

:params
key : the entry key
regenerate : a function that produces the value when the cache has none
ttl_seconds : how long a regenerated value is kept

:returns
the cached value, or the freshly regenerated one

:content
`kv_cache()` is the read-through form: it returns the cached value for `key` when there is one, and otherwise calls `regenerate`, stores its result for `ttl_seconds`, and returns it. The common pattern of "look it up, and if it is missing compute it and remember it" becomes one call.

Choose the time to live by how stale a value may be: a user's account row can live for hours when every write to it calls `kv_delete()`, a list of recent posts for a minute.

:example capy render
kv_delete("doc-expensive")
var calls := { n: 0 }
var compute := function() dval { calls.n = s64(calls.n) + 1; -> { answer: 42 } }
var first := kv_cache("doc-expensive", compute, 60)
var second := kv_cache("doc-expensive", compute, 60)
print(s64(first.answer), " ", s64(second.answer), " ", s64(calls.n), "\n")

:output
42 42 1

:see-group
kv
