Uma diferença é que conj
aceita qualquer número de argumentos para inserir em uma coleção, enquanto cons
leva apenas um:
(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
Outra diferença está na classe do valor de retorno:
(class (conj '(1 2 3) 4))
; => clojure.lang.PersistentList
(class (cons 4 '(1 2 3))
; => clojure.lang.Cons
Observe que eles não são realmente intercambiáveis; em particular, clojure.lang.Cons
não implementa clojure.lang.Counted
, então a count
on ele não é mais uma operação de tempo constante (neste caso, provavelmente seria reduzido para 1 + 3 - o 1 vem do percurso linear sobre o primeiro elemento, o 3 vem de (next (cons 4 '(1 2 3))
ser um PersistentList
e portanto Counted
).
A intenção por trás dos nomes é, creio eu, que cons
significa contr (construir a seq) 1 , ao passo que conj
significa conj (inserir um item em uma coleção). A seq
sendo construído por cons
começa com o elemento passado como primeiro argumento e tem como next
/ rest
parte a coisa resultante da aplicação de seq
para o segundo argumento; como mostrado acima, tudo é de classe clojure.lang.Cons
. Em contraste, conj
sempre retorna uma coleção mais ou menos do mesmo tipo da coleção passada a ele. (Grosso modo, porque a PersistentArrayMap
será transformado em PersistentHashMap
assim que crescer além de 9 entradas.)
1 Tradicionalmente, no mundo Lisp, cons
cons (constrói um par), então Clojure se afasta da tradição Lisp ao ter sua cons
função construir um seq que não possui um tradicional cdr
. O uso generalizado de cons
para significar "construir um registro de algum tipo ou outro para manter uma série de valores juntos" é atualmente onipresente no estudo de linguagens de programação e sua implementação; é isso que se quer dizer quando se menciona "evitar golpes".