Jozef,
I think you can solve this by adding a little more info and keeping
the update function and side effects separate:
(def aval (atom { :contents "hello world" }))
(defn update-item [{:keys [dumped] :as item}]
(assoc item :dumped true :needs-dump (not dumped)))
(let [{:keys [needs-dump contents]} (swap! aval update-item)]
(if needs-dump
(spit "a.out" contents :append true)))
Whoever gets to aval first while it hasn't been dumped will see
needs-dump as true and write the file. Depending on your requirements,
if the file write fails, you'll might need to do some extra work to
put aval back in a state consistent with reality.
Hope this helps,
Dave
Or something higher level like transactions. Use a ref and have
update! alter the ref, then (send-off some-agent #(spit ...)), inside
a dosync. The agent send only goes if the transaction commits.
Wrong. Jozef has this:
(def aval (atom {:dumped false :contents "hello world"}))
(defn update!
[item]
(when-not (:dumped item)
(spit "a.out" (:contents item) :append true)
(assoc item :dumped true)))
(swap! aval update!)
Which can be transformed to this:
(def aval (ref {:dumped false :contents "hello world"}))
(def aagent (agent nil))
(defn update
[item]
(dosync
(if (:dumped @item)
(ref-set item nil)
(do
(alter item assoc :dumped true)
(send-off aagent (fn [_] (do (spit "a.out" (:contents @item)
:append true) nil)))))))
(update aval)
which should have the same semantics, except that the spit cannot be
done twice (or more) for one call to update.
There is no call for taking a rude tone. I correctly answered the
question as originally posed.