8

What is the difference between add-to-list vs add-hook?

For example I see in progmodes/make-mode.el:

(add-hook 'completion-at-point-functions
          #'makefile-completions-at-point nil t)

instead of add-to-list.

add-hook has interesting properties - it binds non-existing symbol! And it is convenient to use in .emacs to avoid if/when boundp or (defvar ...).

Can I use add-hook instead of add-to-list in general? Or requirement to be a function for second argument of add-hook is mandatory?

Drew
  • 75,699
  • 9
  • 109
  • 225
gavenkoa
  • 3,352
  • 19
  • 36

2 Answers2

15

The question you asked

The biggest difference is the last argument:

This means that you have no control over how add-hook decides whether it already contains what you are adding, while add-to-list cannot control buffer-local status of the symbol you are modifying.

The question you meant to ask

These two functions are not interchangeable: even in a situation when either would do what you want, there is one that you should use and the other that you should not.

  • add-hook is for hooks, i.e., variables described as such in their docs; their name usually ends with -functions or -hook.

  • add-to-list is for other global customization variables, like auto-mode-alist and exec-path.

Both are primarily for your .emacs, not for programming. Use push and cl-pushnew when programming instead.

The reason you should not use these functions for the purposes they are not intended to is relatively "soft". Mostly it has to do with

  • code readability (what you write today will have to be read by others - including yourself 6 months from now) and
  • robustness (what if the Emacs maintainers decide to change the behavior of these very high-level functions? these are not car and cdr whose meaning is set in stone!)

In other words,

"... a computer language is not just a way of getting a computer to perform operations, but rather ... it is a novel formal medium for expressing ideas about methodology" Abelson/Sussman "Structure and Interpretation of Computer Programs".

When writing code, you are communicating with HUMANS first. The fact that there is a computer which can and will execute the code is (almost) inconsequential.

sds
  • 5,928
  • 20
  • 39
  • 1
    Largely agreed, except that using `push` or `cl-pushnew` on a *hook* is also wrong, fundamentally. – Stefan Apr 16 '18 at 01:37
6

In some cases you can use either function to add an element to a list. That you can do that does not mean that you should, however.

The recommendation by Emacs is to use only add-hook for a hook. And pushnew (cl-pushnew) is generally recommended in place of add-to-list. (Common Lisp does not even have something like add-to-list.)

Note that, unlike add-to-list, pushnew does not require its second argument to evaluate to a symbol (variable) -- it is a very general macro. But it is a macro, not a function, so you cannot map it (e.g. cl-mapcar), funcall it, etc.

add-to-list can typically be replaced by macro pushnew. The first add-to-list arg is a symbol that is used as a list-valued variable. Its second is any Lisp value. Its fourth, COMPARE-FN is a function that tests whether the element to add is already present - it has no counterpart in add-hook, which always tests using equal.

The first add-hook arg, the hook variable, need not have a list as its value -- it can be a single function. The second arg should be a function, but there is no check that it is. (That answers your second question.) And its fourth arg, LOCAL, means update the buffer-local, not the global, value of the hook - it has no counterpart in add-to-list.

What you mention about their difference, that add-hook binds an unbound symbol, is true. That's important for a hook, in particular, because most hook variables are not created by default. They are implicit in the definition of a mode, and they are created on the fly only when they are actually needed.

Is that bind-if-not-bound behavior what you really want for every add-something-to-a-list use case? I'm guessing probably not. But if that's important to you for some reason then you can easily add such behavior to your own function that adds an element to a variable that should be list-valued.

See C-h f for other differences, or (better) check their definitions in subr.el.

If you understand the differences then you can of course use each any way you like, taking its behavior into account. But for readability by others (or by you, later on), even in contexts where the behavior differences might not matter, it generally makes sense to use them as they were intended.

By using them in the intended/expected way, you do not mislead readers of your code. The principal reason for using each as it is intended is to signal the intention/use to others and to yourself later.

I differ from SDS's fine answer in this respect:

add-to-list is not just for "other global customization variables". It has nothing particularly to do with customization variables or with .emacs.

It is not the case that add-hook and add-to-list are only for your .emacs and that pushnew is only for Lisp code. For one thing, .emacs contains Lisp code. ;-)

It is perfectly fine, and common, for Lisp code to use add-hook and add-to-list (though pushnew is more common than add-to-list nowadays). (For decades Emacs did not even have pushnew; add-to-list was then recommended for all non-hook lists.)

And it is perfectly fine for your .emacs file to use pushnew.

The simple rule to remember is just this:

Use add-hook only with hook variables, and add-to-list or pushnew only to add an element to a (non-hook) list value.

If you really need or want something different, it is pretty easy to define it, and a good place to start is to look at the existing code for add-hook and either add-to-list or pushnew.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • These are two completely different functions. They are never interchangeable. – sds Jan 26 '17 at 17:22
  • 2
    `add-to-list` does not work on lexical variables (some may say that `add-hook` doesn't either, but in my opinion, `add-hook` doesn't operate on *variables* anyway, it operates on *hooks*, which just happen to be implemented as *symbols*, using the value-cell to hold its contents). – Stefan Jan 27 '17 at 01:17
  • @Stefan: Agreed, and a useful addition to the list of differences. – Drew Jan 27 '17 at 01:30
  • Dear @Drew, please do not get defensive, it was never my intention to insult of detract your answer or yourself in any way. – sds Jan 27 '17 at 03:38