Alternate version of swap! also returning swapped out value

后端 未结 5 781
栀梦
栀梦 2021-01-20 03:33

I talked about this a bit on IRC\'s #clojure channel today but would like to go more in detail here. Basically, in order to better understand atoms, swap!

相关标签:
5条回答
  • 2021-01-20 04:21

    Clojure's swap! is just a spinning compare-and-set. You can define an alternate version that returns whatever you like:

    (defn alternate-swap [atom f & args]
      (loop []
        (let [old @atom
              new (apply f old args)]
          (if (compare-and-set! atom old new)
            [old new]  ; return value
            (recur)))))
    
    0 讨论(0)
  • 2021-01-20 04:21

    If you want the return value, Stuart answer is the correct one, but if you are just going to do a bunch of println to understand how atoms/refs work, I would recommend to add a watch to the atom/ref http://clojuredocs.org/clojure_core/1.2.0/clojure.core/add-watch

    (add-watch your-atom :debug (fn [_ _ old new] (println "out" old "new" new)))
    
    0 讨论(0)
  • 2021-01-20 04:27

    Atoms are un-coordinated so it seems likely that any attempt to do this outside of the swapping function it's self will likely fail. You could write a function that you call instead of swap! which constructs a function that saves the existing value before applying the real function, and then pass this constructed function to swap!.

    user> (def foo (atom  []))
    #'user/foo
    user> (defn save-n-swap! [a f & args] 
             (swap! a (fn [old-val] 
                        (let [new-val (apply f (cons old-val args))] 
                           (println "swapped out: " old-val "\n" "swapped in: " new-val) 
                           new-val)))) 
    #'user/save-n-swap!
    user> (save-n-swap! foo conj 4) 
    swapped out:  [] 
     swapped in:  [4]
    [4] 
    user> (save-n-swap! foo conj 4) 
    swapped out:  [4]
     swapped in:  [4 4]
    [4 4] 
    

    This example prints it, It would also make sense to push them to a changelog stored in another atom

    0 讨论(0)
  • 2021-01-20 04:32

    You could use a macro like:

    (defmacro swap!-> [atom & args]
      `(let [old-val# (atom nil)
             new-val# (swap! ~atom #(do
                                      (swap! old-val# (constantly %))
                                      (-> % ~args)))]
         {:old @old-val# :new new-val#}))
    
    (def data (atom {}))
    
    (swap!-> data assoc :a 3001)
    => {:new {:a 3001} :old {}}
    
    0 讨论(0)
  • 2021-01-20 04:32

    You could rely on a promise to store the current value inside the swap! operation. Then you return the new and old value in a vector, as follows:

    (defn- swap-and-return-old-value!
      [^clojure.lang.IAtom atom f & args]
      (let [old-value-promise (promise)
            new-value (swap! atom
                         (fn [old-value]
                           (deliver old-value-promise old-value)
                           (apply f old-value args)))]
        [new-value @old-value-promise]))
    
    0 讨论(0)
提交回复
热议问题