Re: Using an atom for a caching map

2014-09-02 Thread Daniel
If the values in the map don't change then why not just drop the map? Implement the map as a function and memoize it eg (def f (memoize (fn [k] (calc-value k On Saturday, August 30, 2014 12:27:05 AM UTC-5, Colin Fleming wrote: > > Hi all, > > I want to use a map to cache values based on a k

Re: Using an atom for a caching map

2014-09-02 Thread Marcus Magnusson
Michał, that's quite a bit more straightforward, nice :) Also made me realize that I should've used delays in synced-memoize. Den tisdagen den 2:e september 2014 kl. 10:09:47 UTC+2 skrev Michał Marczyk: > > java.util.concurrent.ConcurrentHashMap has a putIfAbsent method that > could be used wi

Re: Using an atom for a caching map

2014-09-02 Thread Michał Marczyk
java.util.concurrent.ConcurrentHashMap has a putIfAbsent method that could be used with delays to support this use case with a no recomputation guarantee: (def chm (java.util.concurrent.ConcurrentHashMap.)) ;; this will work as expected whether chm has a value for :foo or not (let [d (delay (+ 1

Re: Using an atom for a caching map

2014-09-01 Thread Marcus Magnusson
Forget about sleep, there's work to be done! I realized that the drawback I mentioned with my solution, where concurrent calls to the memoized function with different sets of arguments would be handled one-by-one, could easily be resolved by having the cache inside synced-memoize store futures f

Re: Using an atom for a caching map

2014-09-01 Thread Marcus Magnusson
Huh, I gave it some more thought - of course, the reason why memoize won't help us here is that there is no synchronization between simultaneous invocations with the same set of arguments. This is typically not a problem if the underlying function is fast, but in our case it would be neat with

Re: Using an atom for a caching map

2014-09-01 Thread Marcus Magnusson
I reckon if that worked, there would be no need for memoize anyway, but I don't think swap! will allow for it. I'm far from an expert on swap! or atoms, but several swap!s may be run simultaneously on a single atom (and swap! may re-run the swapping function if the atom has been changed since t

Re: Using an atom for a caching map

2014-09-01 Thread Alex Baranosky
I believe you can replace: (when-not (contains? @cache k) (swap! cache assoc k (calc-value* k with: (swap! cache (fn [cache'] (if (contains? cache' k) cache' (assoc cache' k (calc-value* k) Note: I haven't run this code On Mon, Sep 1, 2

Re: Using an atom for a caching map

2014-09-01 Thread Marcus Magnusson
On inspecting the source for memoize, it would actually seem (to my eyes, someone please correct me if I'm mistaken) to be using a similar approach to what has been suggested earlier in the discussion, with the risk of re-computing values at concurrent invocations with the same arguments. So us

Re: Using an atom for a caching map

2014-09-01 Thread Beau Fabry
Indeed you're right, I was confused. The dosync call was a naive attempt to avoid the CAS operation you were worried about earlier. I suppose another option would be to use compare-and-set! with a lazy seq. (compare-and-set! cache-atom (dissoc @cache-atom k) (assoc @cache-atom k (lazy-seq (cons

Re: Using an atom for a caching map

2014-09-01 Thread Thomas Heller
Hey Colin, That doesn't mean guava wouldn't be a nice solution here still. Just .invalidateAll to purge it. Might even use softValues to let the gc decide to remove some entries when running low on memory. Emulating a mutable thing in clojure with an atom is not always better than using the mut

Re: Using an atom for a caching map

2014-09-01 Thread Colin Fleming
Hi Thomas, Normally I'd agree with you, but in my case it actually works quite well since I don't need to expire or worry about sizing. This is for caching objects on IntelliJ parse tree elements, and the element with its cached values is thrown away every time the document is parsed, so these are

Re: Using an atom for a caching map

2014-09-01 Thread Colin Fleming
Hi Marcus, That's an interesting approach. My first reaction was that the update is still not atomic, since you have: (when-not (contains? @cache k) (swap! cache assoc k (calc-value* k))) Which I thought could cause different clients to see different values depending on the race condition ther

Re: Using an atom for a caching map

2014-09-01 Thread Colin Fleming
Hi Beau, I've not used the STM stuff at all, but my understanding is that dosync is intended for co-ordinated updates to refs, and has no effect on atoms. It's really intended for co-ordinating updates to multiple refs rather than as a synchronisation primitive on a single entity. I might be wrong

Re: Using an atom for a caching map

2014-09-01 Thread Thomas Heller
As much as I like Clojure and atoms, I do not think they are a good fit for caching. Not only is it impossible to address the concurrency issues related to multiple threads loading the same object, but you also have to do expiration and size management yourself. Immutability doesn't help much f

Re: Using an atom for a caching map

2014-08-30 Thread Marcus Magnusson
How about something like this? You can still keep your cache as an atom that you can pass around, and you avoid unnecessary recomputation through memoize: (def cache (atom {})) (def lookup (let [calc-value* (memoize calc-value)] (fn [cache k] (when-not (contains? @cache k) (

Re: Using an atom for a caching map

2014-08-30 Thread Beau Fabry
I must be missing something, because this is too simple? (defn get-maybe-cached [cache key] (dosync (if-let [v (get @cache key)] v (do (reset! cache (assoc @cache key (calculate key))) (get @cache key) On Saturday, August 30, 2014

Re: Using an atom for a caching map

2014-08-30 Thread Eldar Gabdullin
You are absolutely right. Sorry, for distraction. Deleted my message immediately after I realized that, but it turns out it managed to go already. суббота, 30 августа 2014 г., 16:25:21 UTC+4 пользователь Colin Fleming написал: > > I'm not sure I understand - the contention will be the same whe

Re: Using an atom for a caching map

2014-08-30 Thread Colin Fleming
I'm not sure I understand - the contention will be the same whether or not the value is calculated in the swap! call, right? And if I calculate the value and then pass the calculated value to swap!, that value will be thrown away every time the cache contains an existing value for my key. On 30

Re: Using an atom for a caching map

2014-08-30 Thread Eldar Gabdullin
I see... Just in case, I'd like to point out one possible deficiency in your original snippet. Generally, it should be better move value calculation outside of swap call, thus reducing a chance of contention, especially when you have many threads asking for different keys. суббота, 30 август

Re: Using an atom for a caching map

2014-08-30 Thread Colin Fleming
In my case I can't use memoize because I need to supply the cache map - that in turn is stored on another object so it can be invalidated by events outside my control. On 30 August 2014 20:00, Ray Miller wrote: > On 30 August 2014 06:26, Colin Fleming > wrote: > > > > I want to use a map to ca

Re: Using an atom for a caching map

2014-08-30 Thread Colin Fleming
Yes, in my case the update is atomic but doesn't strictly avoid recomputation (since the Atom can call the calculation function many times). I've given this some hammock time and I don't think that's a problem. However on further investigation I do have to ensure that for each of my keys the value

Re: Using an atom for a caching map

2014-08-30 Thread Eldar Gabdullin
Oh, forget to wait for value in unwrap. Here is a better version (defn unwrap [v] (if-let [p (get v ::promise)] @p v)) (defn compute [cache k] (let [p {::promise (promise)} c (swap! cache assoc k p) val (get c k)] (when (identical? val p) (let [result (calc

Re: Using an atom for a caching map

2014-08-30 Thread Ray Miller
On 30 August 2014 06:26, Colin Fleming wrote: > > I want to use a map to cache values based on a key. I'm planning to use an > atom for this. My basic operation is "give me the value for this key" - if > the value exists in the map then that value should be returned, otherwise a > new value should

Re: Using an atom for a caching map

2014-08-30 Thread Eldar Gabdullin
Snippet you showed before is also not an atomic. If you want strictly to avoid recomputations you need something more elaborate. May be this (def cache (atom {})) (defn unwrap [v] (if-let [a (get v ::atom)] @a v)) (defn compute [cache k] (let [p {::atom (atom nil)} c (swap

Re: Using an atom for a caching map

2014-08-30 Thread Colin Fleming
True, but only if you don't mind possibly calculating the value more than once since the update is not atomic. On 30 August 2014 18:31, Eldar Gabdullin wrote: > Something like the following would be fine for me > > (def cache (atom {})) > > (defn lookup [cache k] > (let [v (get @cache k ::nil

Re: Using an atom for a caching map

2014-08-29 Thread Eldar Gabdullin
Something like the following would be fine for me (def cache (atom {})) (defn lookup [cache k] (let [v (get @cache k ::nil)] (if (= v ::nil) (let [v (calc-value k)] (swap! cache assoc k v) v) v))) (let [value (lookup cache k)] ; use value and @cache here )

Using an atom for a caching map

2014-08-29 Thread Colin Fleming
Hi all, I want to use a map to cache values based on a key. I'm planning to use an atom for this. My basic operation is "give me the value for this key" - if the value exists in the map then that value should be returned, otherwise a new value should be calculated, inserted in the map and then ret