Clojure, Agent, lack side-effects
I'm using agents to manipulate a structure, but I don't have all my side effects.
All the messages are sent(I've printed and counted them), but there are times when I don't have all my side-effects. As if not all of my functions were applied on the agent's state, or if the last send is applied on a previous state..
I experimented with doall, dorun but haven't find a solution, appreciate any help.
;; aux function for adding an element to a hashmap
(defn extend-regs [reg s o]
(let [os (get reg s)]
(if (nil? os)
(assoc reg s [o])
(assoc reg s (conj os o)))))
;; the agent's altering function - adding an element to the :regs field(a hashmap)
(defn add-reg! [d s o]
(send d (fn [a] (assoc a :regs (extend-regs (:regs a) s o)))))
;; Creating the agents, dct/init returns an agent
;; pds: data for fields
(defn pdcts->init-dcts! [pds]
(doall (map dct/init (map :nam pds) (repeat nil))))
;; Altering one agent's state, dct/add-reg sends an assoc message to the agent
;开发者_开发问答; d: agent, pd: data for fields
(defn dct->add-regs! [d pd]
(dorun (map (fn [s r] (dct/add-reg! d s r))
(:syms pd)
(:regs pd)))
d)
;; Going through all agents
;; ds: agents, pds: datas
(defn dcts->add-regs! [ds pds]
(dorun (map (fn [d pd] (dct->add-regs! d pd))
ds
pds))
ds)
EDIT: =====================================================
Okay it turned out I just haven't wait enough to my threads to finish their tasks. Now the question is how can I monitor my agents. How can I know that there are unfinished threads in the queue? I've only found swank.core/active-threads and similar ones but they are not a solution.
I do not have a solution for your problem, but I can't resist suggesting some improvement on the first two functions:
(defn extend-regs [reg s o]
(let [os (get reg s)]
(if (nil? os)
(assoc reg s [o])
(assoc reg s (conj os o)))))
;; => place the 'if inside the assoc:
(defn extend-regs [reg s o]
(let [os (get reg s)]
(assoc reg s (if (nil? os) [o] (conj os o)))))
;; => this (if (nil? x) ...) is the pattern of function fnil, so ...
(defn extend-regs [reg s o]
(let [os (get reg s)]
(assoc reg s ((fnil conj []) os o))))
;; with update-in, this will be even clearer, and we can remove the let altogether:
(defn extend-regs [reg s o]
(update-in reg [s] (fnil conj []) o))
As for the second:
(defn add-reg! [d s o]
(send d (fn [a] (assoc a :regs (extend-regs (:regs a) s o)))))
;; => We can, again, use update-in instead of assoc:
(defn add-reg! [d s o]
(send d (fn [a] (update-in a [:regs] extend-regs s o))))
;; or, if you can get rid of extend-regs:
(defn add-reg! [d s o]
(send d (fn [a] (update-in a [:regs s] (fnil conj []) o)))
Finally, as a matter of style, I would place add-reg in a separate function, and directly use the idiom of sending to the agent in the client code (or have a simplified add-reg! function):
(defn add-reg [v s o] (update-in v [:regs s] (fnil conj []) o))
(defn add-reg! [d s o] (send d add-reg))
I know this doesn't answer your initial question, but it was fun to write this step by step refactoring
Use await or await-for to wait until an agent finished it's current work queue:
(await agent1 agent2 agent3)
or
(apply await list-of-agents)
A minor improvement of add-reg:
(defn extend-regs [reg s o]
(update-in reg [s] conj o))
This works because of
(conj nil :b) ; => [:b]
thus
(update-in {} [:a] conj :b) ; => {:a [:b]}
finally we have.
(defn add-reg! [d s o]
(send d update-in s [:regs] conj o)
精彩评论