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 colle
Another difference is that because conj
takes a sequence as the first argument, it plays nicely with alter
when updating a ref
to some sequence:
(dosync (alter a-sequence-ref conj an-item))
This basically does (conj a-sequence-ref an-item)
in a thread-safe manner. This wouldn't work with cons
. See the chapter on Concurrency in Programming Clojure by Stu Halloway for more info.
My understanding is that what you say is true: conj on a list is equivalent to cons on a list.
You can think of conj as being an "insert somewhere" operation, and cons as being an "insert at the head" operation. On a list, it is most logical to insert at the head, so conj and cons are equivalent in this case.
There are dedicated functions in the Tupelo Library to add append or prepend values to any sequential collection:
(append [1 2] 3 ) ;=> [1 2 3 ]
(append [1 2] 3 4) ;=> [1 2 3 4]
(prepend 3 [2 1]) ;=> [ 3 2 1]
(prepend 4 3 [2 1]) ;=> [4 3 2 1]
Another difference is the behavior of list?
(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false
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.