Fork me on GitHub
#clojure
<
2022-08-23
>
opqdonut08:08:40

Does any memoize/cache library implement a ttl cache that always returns fast and updates the value on the background? I mean something like this:

(defn get-value []
  (let [result @cache]
    (when (old? result)
      (future (reset! cache (expesive-computation))))
    result))

opqdonut08:08:01

a normal ttl cache does something like

(if-not (old? result)
  result
  (do (reset! cache (expensive-computation))
      @cache))

opqdonut08:08:49

It's easy enough to implement something like this, and I've done it for a previous project, but I assume others would need the same thing

amithgeorge08:08:10

Guava cache does it with refreshAfterWrite https://github.com/google/guava/wiki/CachesExplained#refresh

💯 1
opqdonut08:08:18

Thanks for the pointer! I was wondering what to call this feature.

amithgeorge08:08:42

The HTTP caching spec calls it stale-while-revalidate - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#stale-while-revalidate

👍 1
💯 1
Cora (she/her)14:08:46

user=> (vec #{1 2 3})
[1 3 2]

Cora (she/her)14:08:36

or that 😂

delaguardo14:08:47

it depends on the use case. if array needed for interop it is better to use into-array

djm15:08:53

Let’s say I have this macro:

(defmacro m1 [n x]
        `(def ~n ~x))
It works fine: if I run (m1 foo1 1), I get back #'user/foo1, and the value of foo1 is 1. So far so good. But if I have this macro:
(defmacro m2 [n x]
        `(when-not (resolve '~n)
           (def ~n ~x)))
(m2 foo2), gives back nil, and foo2 gives me #object[clojure.lang.Var$Unbound 0x65b01bf5 "Unbound: #'user/foo2"]. If I change it to (when-not false ...) it works like m1, so the when-not isn’t the problem. macroexpand didn’t give me any clues - everything looks fine there. Does anyone have any idea what’s going wrong?

djm15:08:37

Trying again with a new var, to show the the before and after:

user> foo3
Syntax error compiling at (*cider-repl src/tmp:localhost:33461(clj)*:1:8275).
Unable to resolve symbol: foo3 in this context
user> (m2 foo3 3)
nil
user> foo3
#object[clojure.lang.Var$Unbound 0x70deeb77 "Unbound: #'user/foo3"]

p-himik15:08:03

The var is interned when the (def ...) expression is parsed - regardless whether it's in (when false ...) or not.

p-himik15:08:42

Also '~n won't work because it'll return (clojure.core/unquote n).

p-himik15:08:42

Instead, try ~(list 'quote n) or something like that.

djm15:08:03

Do you mean (resolve ~(list 'quote n)) ?

djm15:08:50

How can I get round the issue with def? I think I tried something with bound? previously, but it didn’t seem to make any difference (perhaps because of the quoting issue).

p-himik15:08:57

> Do you mean (resolve ~(list 'quote n)) ? Yes. > How can I get round the issue with def? What exactly are you trying to achieve?

djm15:08:48

This is the use-case I have: I often use a macro like this: (defmacro d [n x] (do (def n x) ~x))`, that I can wrap round expressions to store them for poking at (so a little like #spy/p from spyscope, but for def rather than printing). But sometimes I want to capture the result from multiple runs, so I want to def once, and then conj the other times.

bronsa15:08:48

you can use intern instead of def

djm15:08:01

I know I could just create a list/atom/whatever outside of the function/macro, but I wanted to get it working with “def it, unless it already exists”, if you see what I mean.

djm15:08:33

@U060FKQPN I’ll give that a go

djm15:08:35

Thank you for your help, both. This seems to work for the “def unless it exists part”:

(defmacro m4 [n x]
        `(when-not (resolve ~(list 'quote n))
           (intern ~(list 'quote 'user) ~(list 'quote n) ~x)))
although I’m not sure why I need 'user and not just user in ~(list 'quote 'user) (without he extra ' I get No namespace: user/user found)

bronsa15:08:42

you're within an unquote, so without the quote it would be evaluating the symbol user

thanks3 1
p-himik15:08:35

Also, ~(list 'quote 'user) could be written as ~''user or '~'user. :)

djm16:08:42

Thank you again, both.

Ed16:08:54

(defmacro d [n x] (do (def n x) ~x))` has the problem that it will evaluate x twice (including side effects). You could always write something like this instead:

(defmacro d [n x] `(deref (def ~n ~x)))

djm16:08:11

Good point, I hadn’t thought of that.