Fork me on GitHub
#clojure
<
2022-07-08
>
dpsutton20:07:08

Question about core.cache and errors thrown by functions which are cached. Details in thread

dpsutton20:07:13

demo=> (defn is-odd? [x]
         (println "computing for " x)
         (if (odd? x) true (throw (ex-info "boom" {}))))
#'demo/is-odd?
demo=> (def modd (memoize/ttl is-odd? :ttl/threshold 30000))
#'demo/modd
demo=> (modd 3)
computing for  3
true
demo=> (modd 5)
computing for  5
true
demo=> (memoize/snapshot modd)
{[3] true, [5] true}
demo=> (modd 2)
computing for  2
Execution error (ExceptionInfo) at demo/is-odd? (REPL:181).
boom
demo=> (memoize/snapshot modd) ;; this blows up with the error from above
computing for  2
Execution error (ExceptionInfo) at demo/is-odd? (REPL:181).
boom

dpsutton20:07:06

define a function that can throw an error (in the real case network access for http requests). I’m confused why (memoize/snapshot modd) would throw here. I dont see any documentation about what the defined behavior is for errors

dpsutton20:07:42

It seems to not cache when the underlying function throws an error, but i’m surprised that the snapshot also throws an error

hiredman20:07:20

it's a delay

hiredman20:07:01

https://github.com/clojure/core.memoize/blob/master/src/main/clojure/clojure/core/memoize.clj#L121-L129 just goes through and reads the delays, if a delay threw an exception when it ran, it will throw it everytime it is deref'ed

dpsutton20:07:07

This feels inconsistent to me. If i repeatedly call (modd 2) I get each time the message “recomputing for 2” and it treats it as not cached. But if I look at the cache it treats it as if it is cached (and prevents me from seeing the actual cache)

hiredman20:07:53

it actually isn't a real delay, so it is actually trying again each time

👍 1
dpsutton20:07:14

Perhaps i’m looking at it wrong but it does seem to be both cached and not cached (and in the wrong direction for my purposes on each side)

hiredman20:07:34

but it is true that the cache contains an entry for whatever throws the exception, the entry just throws an exception when you try and read it

dpsutton20:07:45

fair enough. thanks @U0NCTKEV8

hiredman20:07:53

seems like that would be bad for storage, why store things if it is going to throw

dpsutton20:07:27

Yeah i’m looking at error handling with a ttl cache in general. We put a token check behind a ttl cache and it floods our logs with errors when there’s no network access. Ideally I could cache an error for a different time than the successful request but that doesn’t seem to be an option. And in the case of errors it reverts back to un-cached so quite a bit of unnecessary work

Claudiu Babin20:07:31

Hey! I'm trying to use atom but for some reason I can't get it working when passing it to other functions. For example, the expected output of this is 10 but it outputs 0. Am I doing something wrong?

(defn f [at val]
  (swap! at (partial + val)))

(let [at (atom 0)
      something (map #(f at %) (range 5))]
  (println @at))

phronmophobic20:07:56

map is lazy. try run! instead

phronmophobic20:07:14

or mapv or wrap the call to map with doall

phronmophobic20:07:01

if you don't care about the return value, run! should be preferred. if you do care about the return value, then I would recommend mapv

Claudiu Babin20:07:59

@U7RJTCH6J thank you for pointing that out! is there any way to do something similar to pmap, but so that it resolves only after everything from the collection is consumed?

Claudiu Babin20:07:49

would wrapping the call with doall be the solution?

👍 1
phronmophobic20:07:09

there's also dorun if you don't care about the return value

👍 1
p-himik20:07:51

When you think of pmap specifically with waiting for all the results, you should probably just use a thread pool executor.

Ben Sless03:07:15

Are you using an atom yo accumulate values concurrently?