Fork me on GitHub

unless I misunderstand what you want, you can't and shouldn't use atoms for this


can you clarify the "can't" scenario?


you can't "read and then conditionally write" as separate operations


understood, i'm talking about deciding not to change the value passed into the swap function and just returning that same value in some scenarios


you are spending more cycles trying to avoid contention that the cost of the contention


hm ok i will have to write some perf tests to prove it to myself. thanks


"if the swap implementation never performed a CAS for an identical value" is nonsensical - you can't know the read value to determine identity without being in the CAS (that's the C)


if you want to know whether a particular swap changed the value, you can do that with the newer swap-vals! which returns both the old and new value


thanks, I'm aware of swap-vals! - this isn't about detecting the result of a swap from user code, it's just a suggested way to reduce cas contention when using a swap function that doesn't always result in a modification


@rutledgepaulv It feels a bit like you're trying to reinvent clojure.core.cache.wrapped or clojure.core.memoize? Can you explain a bit more about the actual problem you're trying to solve here?


I think your function is trying to do something similar to clojure.core.cache.wrapped/lookup-or-miss (which deals with various edge cases around cache miss/race conditions).


Totally agree those are available / better mechanisms for doing caching. i've sometimes used an approach like this as a poor man's cache though and haven't encountered issues with doing so. Presumably there are other cases where a swap function sometimes degrades into identity ? i don't yet see a reason to spend a cas in those cases


Given all the other issues involved with avoiding race conditions etc, avoiding CAS in this one case seems like a premature optimization...?


could be! I haven't measured any of this but if I had a lot of simultaneous operations on one atom the retries on f can increase pretty quickly and this is one way to remove a lot of those if some of the simultaneous f invocations end up not needing to do anything


Try clojure.core.cache.wrapped/lookup-or-miss and see how it behaves. I'd be interested in feedback.


definitely looks like it implements the same delay / swap pattern. what i am wondering is if the swap! itself adds some overhead in the "hit" cases (if your hit is a no-op, which is probably not true of core.cache due to eviction policies, etc)


The basic cache is a plain atom.


i'll take a look. fwiw i just wanted to discuss the idea and see if there was a potential opportunity for a backwards compatible improvement in core, i'm totally happy with "no" or "use something else if that matters to you" as the answer 🙂


My gut says your proposed change could break existing programs but I have no idea how I would construct a test to show that 🙂 so I may be wrong and the differences wouldn't actually "matter". But I will say that it took quite a while to arrive at how lookup-or-miss works to 🙂


atoms are very fast - clojure's atom is based on Java's atoms which typically compile right down to things with hardware support. I would not suggest trying to optimize it


that makes sense. but running f multiple times when f is content with a no-op seems like an opportunity?


I just don't think it matters. people do this stuff with atoms all the time


yeah i don't think it's a massively important thing, just an idea. i will try to cobble together some perf test scenarios for my own edification if nothing else


get/`get-in` could have 1-arity to make them useable in transducer context


I'm in some very deep trees...


ah, there is completing for that, right