Clojure

Clojure Notes - An aimless dump of anything and everything interesting I find **and** manage to record.

Synopsis

An aimless dump of anything and everything interesting I find and manage to record.

Interesting styles

The purpose is to capture styles I know of, and they may not necessarily be my own. Since I did not note where I first saw each style, attributions may be missing.

Optional data in map?

I saw this first in the Sente library by Peter Taoussanis. Taking an example from sente itself - a server event message is expected to have the following shape


  {:keys [event id ?data send-fn ?reply-fn uid ring-req client-id]}

So, the keys ?data and ?reply-fn represent optional entries in the map. Pretty neat.

Splitting a namespace across files

The clojure.core namespace is implemented across multiple files. Specifically, as an example, core_print.clj contains some /clojure.core/ functions. Here's how it is done.


  ;; In core.clj

  (load "core_print")

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; In core_print.clj, the beginning line is
  (in-ns 'clojure.core)

  ;; And the implementation follows
#object[clojure.lang.Namespace 0x5d0697e2 "clojure.core"]

Useful short snippets

A useful collection of short snippets of Clojure code I've come across. No original stuff here - only a compilation from various sources I've come across.

Convert a map (keyed by integers) into a correspondingly ordered vector


  ;; From Core Clojure - clojure.data

  (defn- vectorize
    "Convert an associative-by-numeric-index collection into
         an equivalent vector, with nil for any missing keys"
    [m]
    (when (seq m)
      (reduce
       (fn [result [k v]] (assoc result k v))   ;; 1
       (vec (repeat (apply max (keys m))  nil)) ;; 2
       m)))

  (vectorize {0 :A 2 :C 9 :J})
  ;; Output #'user/vectorize[:A nil :C nil nil nil nil nil nil :J]
#'user/vectorize[:A nil :C nil nil nil nil nil nil :J]

On line 2, you initialize a vector of length


  (apply max (keys m))

with nil/-s. And reduce /m with the fn defined on 1 into this vector.

Temporarily descending into mutable-land.

Immutable is good, but not necessarily always pleasant. Especially when you are dealing with a large collection and your processing involves creation (and discarding) of as many immutable temporary objects. filterv from clojure.core is a short example of how (and when) you'd use transient in your code.


  (defn filterv
    "Returns a vector of the items in coll for which
    (pred item) returns true. pred must be free of side-effects."
    {:added "1.4"
     :static true}
    [pred coll]
    (-> (reduce (fn [v o] (if (pred o) (conj! v o) v))
                (transient [])
                coll)
        persistent!))

conj! adds an element to the transient vector - notice the *!* at the end. And finally, the collection is made persistent with persistent!.