39

I've found that different packages in their installation instructions use either push or add-to-list (For example adding a directory to load-path) and I was wondering what the difference is and what the use case for each would be.

Drew
  • 77,472
  • 10
  • 114
  • 243
shadowthief
  • 393
  • 1
  • 3
  • 5
  • 4
    I was struggling to convert code using add-to-list to code using cl-pushnew, and I found this blog post to be quite enlightling: https://yoo2080.wordpress.com/2013/09/11/emacs-lisp-lexical-binding-gotchas-and-related-best-practices/ – Daniel Apr 07 '18 at 12:57

3 Answers3

29

What #zck mentions is one difference. But if that were the only difference then you could ask about cl-pushnew and add-to-list.

Another important difference: add-to-list is a function, which means that it evaluates all of its arguments, in particular, the first one. push is a macro (as is cl-pushnew) - it does not evaluate its second argument; instead, it interprets it as a generalized place.

For example, if the second argument is a symbol then it is regarded as a variable, and the value of the first argument is consed onto the value of that symbol as a variable, and the variable is set to that new cons.

As the doc string of add-to-list says:

This is handy to add some elements to configuration variables,
but please do not abuse it in Elisp code, where you are usually
better off using `push' or `cl-pushnew'.
Drew
  • 77,472
  • 10
  • 114
  • 243
  • 7
    Also according to the byte-compiler: add-to-list can't use lexical var ...; use push or cl-pushnew – Malabarba Jan 15 '15 at 12:05
  • (push (5 6) my-list) still gives me an error -- 5 is not a function. How is this different than add-to-list's behavior? – markasoftware May 19 '19 at 17:46
  • @markasoftware: What are you trying to do? If you want to push the list (5 6) to the place (value of variable) my-list then you need to create the list (5 6). One way to do that is to use '(5 6); another is to use (list 5 6). push evaluates the argument. – Drew May 19 '19 at 19:56
  • @Drew you say now that it evaluates the argument, but your answer literally states "it does not evaluate its first argument", which is the source of my confusion. – markasoftware May 20 '19 at 02:00
  • @markasoftware: I'm sorry; I had a typo - I wrote "first argument" where I should have written "second argument". Corrected now - thanks. The second arg to push is a place, such as a variable. The first arg is evaluated, consed onto the value of that variable, and the variable is set to that new cons. add-to-list evaluates its first arg to produce the variable whose value gets updated. push does not evaluate its second arg, which is the variable to update. The arg order is reversed between the two. – Drew May 20 '19 at 02:46
  • @Drew Got it! For my own reference, are there any "places" that push accepts but, when quoted, wouldn't work in add-to-list? – markasoftware May 20 '19 at 03:31
  • @markasoftware: Not sure what you mean, but push accepts a generalized variable (the "place"). add-to-list accepts only an ordinary variable (a symbol - the result of evaluating its first arg). See the Elisp manual, node Generalized Variables. – Drew May 20 '19 at 16:32
  • @markasoftware concretely: Try this (let ((my-alist '((foo 3 4 5) (bar 7 8 9)))) (push 111 (alist-get 'foo my-alist)) my-alist) That doesn't work with add-to-list. – hraban Sep 03 '23 at 18:03
21

Another difference:

push adds element to the beginning of list.

add-to-list allows you to add element to either the beginning or end of list.

(setq testasdf nil)

(push 'a testasdf)

testasdf
(a)

(add-to-list 'testasdf 'b)

testasdf
(b a)

;; add element to the end
(add-to-list 'testasdf "hello" t)

testasdf
(b a "hello")
undostres
  • 1,813
  • 14
  • 15
18

From the Emacs documentation, or C-h f push:

Macro: push element listname

This macro creates a new list whose car is element and whose cdr is the list specified by listname, and saves that list in listname.

From the same page, or C-h f add-to-list:

Function: add-to-list symbol element &optional append compare-fn

This function sets the variable symbol by consing element onto the old value, if element is not already a member of that value.

So add-to-list only pushes if the element isn't already there.

zck
  • 9,092
  • 2
  • 33
  • 65