stumbling towards the clojure api
Here are two examples of code I’ve written and re-written lately:
My first example came about from dealing with DOM Document elements and Nodes, specifically getting a named attribute from a given Node:
;; bad, throws a NullPointerException if any of the Java methods return nil
(defn get-attribute1 [elt attr]
(.. elt getAttributes (getNamedItem attr) getValue))
;; this works great
(defn get-attribute2 [elt attr]
(when-let [attrs (. elt getAttributes)]
(when-let [item (. attrs (getNamedItem attr))]
(. item getValue))))
;; because lord knows I love monads
(require '[clojure.contrib.monads :as m])
(defn get-attribute3 [elt attr]
(m/domonad m/maybe-m
[attrs (. elt getAttributes)
item (. attrs (getNamedItem attr))]
(. item getValue)))
;; a slight improvement
(defn get-attribute4 [elt attr]
((m/with-monad m/maybe-m
(m/m-chain [(memfn getAttributes)
#(. % (getNamedItem attr)) ;; take note that (memfn getNamedItem attr) will _not_ work here
(memfn getValue)]))
elt))
;; recommended (look at the source of .?. for yet another way to express this, as well as some other nil-safe variants)
(use '[clojure.contrib.core :only (.?.)])
(defn get-attribute5 [elt attr]
(.?. elt getAttributes (getNamedItem attr) getValue))
My second example came about while trying to remove duplicates from a list while keeping the unique elements in the order they were originally given.
;; this is no good, it doesn't preserve order
(defn no-dupes0 [lst]
(into #{} lst))
;; this only works for objects implementing comparable, and even then would
;; need to be done so in a clever way that incorporates the list which the elements came from
;; as it's not a natural ordering
(defn no-dupes1 [lst]
(sorted-set lst))
;; at a loss for what to do, I banged out this loop/recur solution quickly
(defn no-dupes2 [lst]
(loop [result (list)
seen #{}
xs lst]
(if (empty? xs)
(reverse result)
(let [x (first xs)]
(if (contains? seen x)
(recur result seen (rest xs))
(recur (conj result x) (conj seen x) (rest xs)))))))
;; this took me a long time to come up with as I'd never tried to write
;; a lazy sequence without higher level functions like map, iterate, et al
;; http://clojure.org/lazy was a great help figuring out how to use lazy-seq
(defn no-dupes3 [lst]
;; I have long wondered when to use letfn instead of let when defining functions
;; this is a good example, as a fn defined in a let isn't visible to itself
(letfn [(internal [lst seen]
(lazy-seq
(when (seq lst) ;; learned: (seq lst) can be used as the opposite of (empty? lst)
(let [x (first lst)]
(if (contains? seen x)
(internal (rest lst) seen)
(cons x (internal (rest lst) (conj seen x))))))))]
(internal lst #{})))
;; recommended (look at the source to see the pros at work.
;; it uses destructuring to avoid my (let [x (first lst)] clause
;; as well as recur, which probably saves the function from blowing the
;; stack on ridiculously long seqs)
(defn no-dupes4 [lst]
(distinct lst))

leave a comment