Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: cons (seq) vs. conj (list)

I know that cons returns a seq and conj returns a collection. I also know that conj "adds" the item to the optimal end of the collection, and cons always "adds" the item to the front. This example illustrates both of these points:

user=> (conj [1 2 3] 4) ; returns a collection [1 2 3 4] user=> (cons 4 [1 2 3]) ; returns a seq (4 1 2 3) 

For vectors, maps, and sets these differences make sense to me. However, for lists they seem identical.

user=> (conj (list 3 2 1) 4) ; returns a list (4 3 2 1) user=> (cons 4 (list 3 2 1)) ; returns a seq (4 3 2 1) 

Are there any examples using lists where conj vs. cons exhibit different behaviors, or are they truly interchangeable? Phrased differently, is there an example where a list and a seq cannot be used equivalently?

like image 270
dbyrne Avatar asked Jun 09 '10 17:06

dbyrne


1 Answers

One difference is that conj accepts any number of arguments to insert into a collection, while cons takes just one:

(conj '(1 2 3) 4 5 6) ; => (6 5 4 1 2 3)  (cons 4 5 6 '(1 2 3)) ; => IllegalArgumentException due to wrong arity 

Another difference is in the class of the return value:

(class (conj '(1 2 3) 4)) ; => clojure.lang.PersistentList  (class (cons 4 '(1 2 3)) ; => clojure.lang.Cons 

Note that these are not really interchangeable; in particular, clojure.lang.Cons does not implement clojure.lang.Counted, so a count on it is no longer a constant time operation (in this case it would probably reduce to 1 + 3 -- the 1 comes from linear traversal over the first element, the 3 comes from (next (cons 4 '(1 2 3)) being a PersistentList and thus Counted).

The intention behind the names is, I believe, that cons means to cons(truct a seq)1, whereas conj means to conj(oin an item onto a collection). The seq being constructed by cons starts with the element passed as its first argument and has as its next / rest part the thing resulting from the application of seq to the second argument; as displayed above, the whole thing is of class clojure.lang.Cons. In contrast, conj always returns a collection of roughly the same type as the collection passed to it. (Roughly, because a PersistentArrayMap will be turned into a PersistentHashMap as soon as it grows beyond 9 entries.)


1 Traditionally, in the Lisp world, cons cons(tructs a pair), so Clojure departs from the Lisp tradition in having its cons function construct a seq which doesn't have a traditional cdr. The generalised usage of cons to mean "construct a record of some type or other to hold a number of values together" is currently ubiquitous in the study of programming languages and their implementation; that's what's meant when "avoiding consing" is mentioned.

like image 167
Michał Marczyk Avatar answered Sep 25 '22 15:09

Michał Marczyk