Stuart Sierra’s anti-patterns for Clojure

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

This is a subtle difference:

Be explicit about your types even when they’re dynamic.

If the operation requires a collection, then pass it a collection every time.

A “helper” like wrap-coll saves you a whopping two characters over just wrapping the argument in a literal vector, at the cost of lost clarity and specificity.

If you often forget to wrap the argument correctly, consider adding a type check:

(defn process-batch [items]
  {:pre [(coll? items)]}
  ;; ... 
  )

If there actually are two distinct operations, one for a single object and one for a batch, then they should be separate functions:

(defn process-one [item]
  ;; ...
  )

(defn process-batch [items]
  ;; ...
  )

In another post he offers to flip side of the rule:

If you have an operation on a single object, you don’t need to define another version just to operate on a collection of those objects.

That is, if you have a function like this:

(defn process-thing [thing]
;; process one thing
)
There is no reason to also write this:

(defn process-many-things [things]
(map process-thing things))

The idiom “map a function over a collection” is so universal that any Clojure programmer should be able to write it without thinking twice.

In other words, write a function that does something with one item, and then write map to apply it to collections.

Post external references

  1. 1
    http://stuartsierra.com/2015/06/10/clojure-donts-heisenparameter
  2. 2
    http://stuartsierra.com/2015/08/10/clojure-donts-redundant-map
Source