38

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
  • 75,699
  • 9
  • 109
  • 225
shadowthief
  • 383
  • 1
  • 3
  • 5
  • 3
    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
  • 75,699
  • 9
  • 109
  • 225
  • 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](https://www.gnu.org/software/emacs/manual/html_node/elisp/Generalized-Variables.html). – Drew May 20 '19 at 16:32
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,793
  • 12
  • 15
17

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
  • 8,984
  • 2
  • 31
  • 65