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


Interesting code/styles I've found in various Clojure open-source codebases.

Exploring the Core

We'll use the built-in clojure.repl namespace for our explorations

(ns clojurexplore)
(require '[clojure.repl :refer [doc dir dir-fn]])

(dir-fn 'clojure.core)
(* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *default-data-reader-fn* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-namespace-maps* *print-readably* *read-eval* *reader-resolver* *source-path* *suppress-read* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Eduction ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE Inst NaN? PrintWriter-on StackTraceElement->vec Throwable->map abs accessor aclone add-classpath add-tap add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and any? apply areduce array-map as-> aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array boolean? booleans bound-fn bound-fn* bound? bounded-count butlast byte byte-array bytes bytes? case cast cat char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement completing concat cond cond-> cond->> condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare dedupe default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array double? doubles drop drop-last drop-while eduction empty empty? ensure ensure-reduced enumeration-seq error-handler error-mode eval even? every-pred every? ex-cause ex-data ex-info ex-message extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext f for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by halt-when hash hash-combine hash-map hash-ordered-coll hash-set hash-unordered-coll ident? identical? identity if-let if-not if-some ifn? import in-ns inc inc' indexed? infinite? init-proxy inst-ms inst-ms* inst? instance? int int-array int? integer? interleave intern interpose into into-array ints io! isa? iterate iteration iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-entry? map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mix-collection-hash mod munge name namespace namespace-munge nat-int? neg-int? neg? newline next nfirst ? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents parse-boolean parse-double parse-long parse-uuid partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos-int? pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues qualified-ident? qualified-keyword? qualified-symbol? quot rand rand-int rand-nth random-sample random-uuid range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read+string read-line read-string reader-conditional reader-conditional? realized? record? reduce reduce-kv reduced reduced? reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-tap remove-watch repeat repeatedly replace replicate require requiring-resolve reset! reset-meta! reset-vals! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq run! satisfies? second select-keys send send-off send-via seq seq-to-map-for-destructuring seq? seqable? seque sequence sequential? set set-agent-send-executor! set-agent-send-off-executor! set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents simple-ident? simple-keyword? simple-symbol? slurp some some-> some->> some-fn some? sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! swap-vals! symbol symbol? sync tagged-literal tagged-literal? take take-last take-nth take-while tap> test the-ns thread-bound? time to-array to-array-2d trampoline transduce transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing unreduced unsigned-bit-shift-right update update-in update-keys update-proxy update-vals uri? use uuid? val vals var-get var-set var? vary-meta vec vector vector-of vector? volatile! volatile? vreset! vswap! when when-first when-let when-not when-some while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap)

Interesting Styles   style

Optional data

This is from the Sente library by Peter Taoussanis. Taking an example from sente itself - a server event message is a map expected to have multiple keys, as below. Since we are in Lisp-land, almost every character on the keyboard is available for identifiers.

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

We start the identifier with a ? - something that we use to indicate optional. So, keys ?data and ?reply-fn represent optional entries in the map.

Splitting a namespace across files

When your namespace is growing too large for a single file, you can split it across multiple files. For example, take the clojure.core namespace, which is split across multiple files. As a more concrete piece, core_print.clj contains some clojure.core functions. Here's how it is done - notice that the base file-name, without the .clj suffix, is used.

  ;; In core.clj
  (load "core_print")

Inside core_print.clj, use the in-ns directive to tell the clojure reader/compiler how to treat the code in here.

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

  ;; And the implementation follows

Smart Tricks   snippets

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

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

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

On line marked 2, you initialize a vector of the required length with nil-s, using max

  (apply max (keys m))

And reduce m with the function defined on line marked 1 into this vector.

Temporarily descending into mutable-land.   performance

Immutability is great, but it's okay to mutate within the confines

  (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 [])

conj! adds an element to the transient vector - notice the ! at the end. Also, the transient-functions always return a reference that you are expected to use for the next step. This is important - even though it mutates in place, we are not supposed to hold on to any old reference while building the transient datastructure. And finally, the collection is made persistent with persistent!.

Control Flow

An interesting discussion can be found at How are clojurians handling control flow on their projects?

Uncommon Stuff

deftype and Mutable Members

deftype allows for the definition of mutable members using the ^:volatile-mutable tag on a member. But they become private to the object, and we need to implement methods on them (via interfaces or protocols) to access these private members for modification.

(ns msync.deftype)

(defprotocol BarBazOperators
  (get-bar [this])
  (bar-inc! [this])
  (baz [this])
  (baz-inc! [this]))

(deftype FooBarBaz [foo ^:volatile-mutable bar ^:volatile-mutable baz]
  (get-bar [_] bar)
  (bar-inc! [_] (set! bar (inc bar)))
  (baz [_] baz)
  (baz-inc! [_] (set! baz (inc baz))))

(def foo-bar-baz (FooBarBaz. 10 20 30))
;; We have a problem
(.bar foo-bar-baz)
class java.lang.IllegalArgumentException

These are method calls.

(.get-bar foo-bar-baz)
(.baz foo-bar-baz)

So are these too - method calls.

(bar-inc! foo-bar-baz)
(get-bar foo-bar-baz)
(baz-inc! foo-bar-baz)
(baz foo-bar-baz)

But foo is different.

(.foo foo-bar-baz)

There is no method foo

(foo foo-bar-baz)
class clojure.lang.Compiler$CompilerException

&env in Macros

Inside of macros, you have access to the context in which you are being evaluated.

(defmacro show-env []
  (into [] (map class (keys &env))))
(defn show-env-wrapper [x y] (show-env))
(show-env-wrapper 10 20)
[clojure.lang.Symbol clojure.lang.Symbol]

Although, it is an extremely tricky place to be in. map is lazy, so let's try to return the result of the map operation rather than the call to (into []..)

(defmacro show-env []
  (map class (keys &env)))
(defn show-env-wrapper [x y] (show-env))
(show-env-wrapper 10 20)
class java.lang.ClassCastException

Nope. Did not go well.

Let's try getting the keys out.

(defmacro show-env []
  (into [] (keys &env)))
(defn show-env-wrapper [x y] (show-env))
(show-env-wrapper 10 20)
[10 20]

Wut!? The keys actually turn out to be the vals! What if we used vals instead of keys (non-lazy)?

(defmacro show-env []
  (into [] (vals &env)))
(defn show-env-wrapper [x y] (show-env))
class clojure.lang.Compiler$CompilerException

Nope - we can't get the vals to escape. What are those?

(defmacro show-env []
  (into [] (map class (vals &env))))
(defn show-env-wrapper [x y] (show-env))
(show-env-wrapper 10 20)
[clojure.lang.Compiler$LocalBinding clojure.lang.Compiler$LocalBinding]

Turns out - a core Compiler artifact - an inner class LocalBinding.

The keys are Symbol objects. Printing them gets us the values held in them. Ok, we can get all we want from the keys. Here's the culimnation (final version of show-env via @codesmith on the Clojurians Slack, which was also the inspiration for this section on &env)

(defmacro show-env []
  (into {} (map (juxt (comp keyword name) identity) (keys &env))))
(defn show-env-wrapper [x y] (show-env))
(show-env-wrapper 10 20)
(let [a-for "Apple"
      b-for "Bat"]
  (show-env-wrapper 100 200))
(let [a-for "Apple"
      b-for "Bat"]
  ((fn [x y] (show-env)) 1000 2000))
{:x 10, :y 20}
{:x 100, :y 200}
{:a-for "Apple", :b-for "Bat", :x 1000, :y 2000}

As you can notice above, macros do their things at compile time. The first let block bindings aren't available to the show-env-wrapper function. The second let block's bindings, which uses an inline function, are available via the &env processing.

Java Interop

Some useful libraries


It's macro-land, after all

If you'd like to declare some constants at the top of your project.clj for reuse within defproject

(def my-version "1.2.3")

(defproject org.msync/my-pet-project ~my-version
  ;; And all your directives

The eval-reader for dynamism

Some self-evident example code. Notice the use of the eval reader #=.

:source-paths [#=(eval (str (System/getProperty "user.home") "/.lein/dev-srcs"))

You can unlimit your imagination and find quite a few uses.



with-redefs and inlined-annotated vars don't play well.

  (with-redefs [+ -] (+ 10 20 30))

+, - and other functions (many math operations, among others) fall under this category. For reference, here is the source of + in Clojure 1.10.3

  (defn +
    "Returns the sum of nums. (+) returns 0. Does not auto-promote
    longs, will throw on overflow. See also: +'"
    {:inline (nary-inline 'add 'unchecked_add)
     :inline-arities >1?
     :added "1.2"}
    ([] 0)
    ([x] (cast Number x))
    ([x y] (. clojure.lang.Numbers (add x y)))
    ([x y & more]
       (reduce1 + (+ x y) more)))


Since no article is complete without an attempt at monads, we refer to Monads for completeness' sake.

This is only a re-run of 05-monads.

Monads refer to a category of values associated with a pair of functions

  1. unit
  2. bind

which have certain properties, as described below.

First, let us introduce some terminology and symbols

The category to which our monads belong.
Any value
A value belonging to the Category
An arbitrary function, that returns a value belonging to the Category
(unit x) results in a value that belongs to this category, given any x
(bind f m) returns a value in the said category