Rafael Munitić
Rafael Munitić

Reputation: 927

Clojure swap! atom dequeuing

Is there a simpler way to write this code in Clojure :

(def queue (atom {:top nil :queue PersistentQueue/EMPTY}))
(swap! queue #(hash-map :top nil :queue (conj (:queue %) "foo")))
(let [{:keys [top]} (swap! queue
                        #(hash-map 
                           :top (peek (:queue %)) 
                           :queue (pop (:queue %))))]
  (println top))

alternative way to write it would be :

(def queue (atom PersistentQueue/EMPTY))
(swap! queue conj "foo")
(let [top (atom nil)]
  (swap! queue 
         (fn [queue]
           (reset! top (peek queue))
           (pop queue)))
  (println @top))

That seems even worse.

Anyway I have a code which uses atoms for queuing a lot and using the former approach is making the code really confusing, I would expect there to be something like :

(swap! queue (fn [queue] (AtomSwapResult. atom-value return-value))

or some similar mechanism in the swap! function since it seems like the kind of thing you would want to do often (not even limited to queuing, I've hit several other use cases where it would be useful to return a different value, for eg. the old value that was swapped out) and it doesn't break the atom/swap! semantics.

Is there a way to do this in Clojure ?

Upvotes: 8

Views: 2434

Answers (3)

Ethan McCue
Ethan McCue

Reputation: 1018

Devil's advocate, don't write this in clojure - just use the high quality Java queues that are already available.

(import java.util.concurrent.LinkedBlockingQueue)

(def queue (LinkedBlockingQueue.))

(defn dequeue! [q]
  (.take q))

(defn enqueue! [q v]
  (.put q v))

Upvotes: 2

hzhao
hzhao

Reputation: 27

Using ref would be a simpler option:

(defn dequeue!
  "Given a ref of PersistentQueue, pop the queue and change the queue"
  [queue-ref]
  (dosync
    (let [val (peek @queue-ref)]
      (alter queue-ref pop)
      val)))

(let [q (ref clojure.lang.PersistentQueue/EMPTY)]
           (dosync (alter q conj 1 2 3)
                   (alter q conj 5))
           (fu/dequeue! q)
           => 1
           (seq @q)
           => (2 3 4 5))

Upvotes: 3

kotarak
kotarak

Reputation: 17299

(defn dequeue!
  [queue]
  (loop []
    (let [q     @queue
          value (peek q)
          nq    (pop q)]
      (if (compare-and-set! queue q nq)
        value
        (recur)))))

(def queue (atom clojure.lang.PersistentQueue/EMPTY))
(swap! queue conj :foo)
(swap! queue conj :bar)
(seq @queue)
(dequeue! queue)
(seq @queue)

Upvotes: 17

Related Questions