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

Reply via email to