conj vs cons in clojure

by paul, on 12.23.2012

I think a lot of people, myself included, came to clojure after learning another dialect of lisp. One thing that I have found confusing in the past is that while cons is a fundamental building block in lisp, idiomatic clojure code uses conj more frequently. This can be confusing when you're new to the language, so I wanted to write a post explaining the difference.

One of the first 'gotchas' is that cons will append an item to the end of a list or vector, whereas the placement of the element into a data structure using conj depends on the type. so, conjing onto a vector gives

(conj [1 2 3] 1) ;  result [1 2 3 1]
, whereas onto a list gives
(conj '(1 2 3) 1) ; result (1 1 2 3)
. The philosophy behind this is that conj will add the item in the most efficient place possible for that collection type.

There are some other differences:

  1. conj takes a variable number of arguments, whereas cons takes one. this behavior also means that the argument order of conj and cons are reversed, the collection you are conjing onto comes first in the argument order.
  2. The return types of conj and cons are different.
    user=> (class (conj '(1 2 3) 1))
    clojure.lang.PersistentList
    user=> (class (conj [1 2 3] 1))
    clojure.lang.PersistentVector

The behavior of conj means that it's pretty easy to write a reverse function if you conj things onto a list. One of the 4clojure.org questions is to write a reverse function, my naive initial solution was

(fn [l] my-reverse
  (if (empty? l) [] 
    (conj (my-reverse (rest l)) (first l))))
, but a much much nicer approach is to
reduce #(conj %1 %2) '()
(hopefully 0x89 won't mind me posting their elegant solution). The key to this solution is the empty list, an empty vector won't work.

Hopefully this post makes the difference between conj and cons a bit more clear, if you are new to the language.

Categories: Clojure