Today I was trying to figure out how I could use Clojure's agents to write a concurrent queue. The part I was struggling with was how do I pop a value off the queue. A pop involves not only removing an element from the queue but also returning that value.
I came up with a function called query-and-alter
.
(defn query-and-alter [a query-fn alter-fn] (let [p (promise) f (fn [x] (deliver p (query-fn x)) (alter-fn x))] (send a f) @p))gist
It takes 2 functions, the first queries the value associated with the agent and the second alters the agent value.
Here's how you can use it.
eagents.core> (def my-agent (agent (sequence []))) #'eagents.core/my-agent eagents.core> @my-agent () eagents.core> (send my-agent conj :a) #<Agent@376433e4: ()> eagents.core> (send my-agent conj :b) #<Agent@376433e4: (:a)> eagents.core> (send my-agent conj :c) #<Agent@376433e4: (:b :a)> eagents.core> (send my-agent conj :d) #<Agent@376433e4: (:c :b :a)> eagents.core> @my-agent (:d :c :b :a) eagents.core> (query-and-alter my-agent last drop-last) :a eagents.core> @my-agent (:d :c :b)You can see we added 4 items to the queue then we popped an item off the queue. The returned value was
:a
and :a
was removed from the queue. All in one send to the agent.