On Mon, Nov 29, 2010 at 7:42 PM, Stuart Sierra <the.stuart.sie...@gmail.com> wrote: > On Nov 28, 5:36 pm, Takahiro Hozumi <fat...@googlemail.com> wrote: >> Which style do you like? > > This one: > >> (defn dec-or-dissoc! [key] >> (dosync >> (alter *counts* >> (fn [m] >> (let [n (m key)] >> (if (< 1 n) >> (assoc m key (dec n)) >> (dissoc m key))))))) > > I like to avoid deref'ing inside alter, instead relying on the value > passed in to the altering function. > > The other one looks more like imperative code.
+1. As another commenter noted, this has the added benefit that the function passed to alter can be extracted and given its own name reflecting is purpose. In this case, you might write (defn dec-or-dissoc [m k] (let [n (m k)] (if (> n 1) (assoc m k (dec n)) (dissoc m k)))) and then (defn drop-count! [k] (dosync (alter *counts* dec-or-dissoc k))) This makes dec-or-dissoc into a pure function that can be tested easily; the amount of code inside (dosync ... ) is kept to a minimum. A rule of thumb to consider is that a line of code inside (dosync ... ) is 10x as much work to debug as a line of code in a pure function, because of the potential long range interactions whenever you have stateful behavior. If dec-or-dissoc! seems to be doing the wrong thing, it may be a bug in the actual meat of the logic, or a problem with the transaction, or something contaminated *counts* with bogus data that makes dec-or-dissoc! choke (e.g. a nonnumeric value for a key), or it's actually working perfectly and some *other* transaction is screwing up *counts* a moment later. Isolating the meat of the dec-or-dissoc function into a pure function lets you test it separately from all that other crap, and the drop-count! function is so bare-bones you can eyeball debug it and see that it can't possibly be wrong, so if anything is going wrong it's going wrong somewhere else. Another way this facilitates debugging is that we can easily tack a (println k n) in drop-or-dissoc right after let [n ...] and a) generate a console log of the count values at decrement events -- in particular if a nonnumeric value is getting in there it will show up just before the CCE it causes to be thrown and you'll actually see what the nonnumeric object actually was -- and b) the println itself won't be buried in a deeply nested set of functions where it's more likely to be forgotten when it's no longer needed for debugging. :) You can also reuse dec-or-dissoc if you eventually have more cases where you want to decrement and remove numbers in associative structures; whereas dec-or-dissoc! only worked on *counts*, dec-or-dissoc works on any map and (with numeric keys) on vectors too. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en