AbstProcDo · 2022-04-24 11:31
I read the "modifying list variables" and find "push" not quote List but "add-to-list" quote List
As for push:
(setq l '(a b))
⇒ (a b)
(push 'c l)
⇒ (c a b)
As for add-to-list
(setq foo '(a b))
⇒ (a b)
(add-to-list 'foo 'c) ;; Add c.
⇒ (c a b)
The docs explain why quote 'foo as:
> The argument symbol is not implicitly quoted; =add-to-list= is an ordinary function, like =set= and unlike =setq=. Quote the argument yourself if that is what you want.
However, the question remains, why push not quote List?
What's the ideas behind to create the difference?
franburstall · 2022-04-24 12:02
The key point is that `add-to-list` is a function and so always evaluates its arguments while `push` is a macro and therefore need not. In fact, the second argument of `push` is a generalised variable (see the docs for `setf`) and so you can do things like:
(push 'c (cdr foo))
after which `foo` will be `(a c b)`.
0lMon · 2022-04-24 12:03
Hej Hej,
I am by no means an expert, and hope somebody corrects me if I am wrong.
push is a macro and does in your example the following:
```
(setq l
(cons 'c l))
```
So the quote is hidden inside with the setq.
You can check that yourself by writting `(push 'c l)` and using `pp-macroexpand-last-sexp`
Cheers!
flyin1501 · 2022-04-24 12:20
Looking at the commit history:
`push` is there first, and it's inherited from Common Lisp.
`add-to-list` was added later in this change: https://github.com/emacs-mirror/emacs/blob/6e3af6305a07eda134fde4c61cd59e0b6f41c96d/lisp/subr.el#L714
(Using the mirror on GitHub; when Emacs switched to Git as the version control the history was also imported, hence the commit seemingly predating Git)
The change did not explain why it was added though. But this is why `push` is there.
And if `add-to-list` implicitly quoted the argument, it would basically be identical to `pushnew` (now `cl-pushnew`), and I guess RMS (assuming the commit author is correct) felt that's inconvenient and added an alternative?
---
Edit: Under lexical binding, `add-to-list` is still only able to use global bindings:
(let ((y '()))
(add-to-list 'y 10)
y) ; errors out in lexical binding as y is not an existing variable
This is a bit surprising and I think explains why the docstring discourages the use of `add-to-list` for anything other than configuration.
AbstProcDo · 2022-04-24 12:36
check u/flyin1501 explain why use function but not marco "And if add-to-list implicitly quoted the argument, it would basically be identical to pushnew (now cl-pushnew), and I guess RMS (assuming the commit author is correct) felt that's inconvenient and added an alternative?"
usaoc · 2022-04-24 13:07
It’s a function because it deals with the *global* value of a symbol, like `set` does. `push` and friends are special operators/macros because they deal with generalized variables, which can potentially be lexical. Read [the Elisp manual](https://www.gnu.org/software/emacs/manual/html_node/elisp/List-Variables.html) very carefully, and see [*Common Lisp: The Language*](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node58.html) for the motivation of such a distinction in traditional Lisps.
franburstall · 2022-04-24 15:59
`push` does a lot behind the scenes. `(push 'c (cdr foo))` expands to
(let* ((v foo))
(setcdr v (cons 'c (cdr v))))
ieure · 2022-04-25 01:49
> It’s a function because it deals with the global value of a symbol, like set does.
`add-to-list` is a normal function and works with any symbol without regard for its scope.
(let ((x '(2 1)))
(add-to-list 'x 3))
usaoc · 2022-04-25 03:11
The crucial part is that `add-to-list` modifies the global value of a symbol, not the scope. The scope doesn’t matter for the global value, because a dynamic binding has *indefinite* scope. The implication is that this code is not a bug when `lexical-binding` is `nil` or when the variable is declared special.
(eval (let ((xs (gensym)))
`(let ((,xs '()))
(add-to-list ',xs 'x)
,xs)))
=> (x)
(eval (let ((xs (gensym)))
`(let ((,xs '()))
(add-to-list ',xs 'x)
,xs))
'lexical)
=> *error*
(eval (let ((xs (gensym)))
`(progn
(defvar ,xs)
(let ((,xs '()))
(add-to-list ',xs 'x)
,xs)))
'lexical)
=> (x)
Somewhat confusingly, `let`-binding a special variable is nothing more than temporarily setting its global value in the `let` form’s dynamic extent. This is not the case when `lexical-binding` is non-`nil`, where `let`-bound *non-special* variables are lexical. See, again, [*Common Lisp: The Language*](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node43.html) for the precise specification of scope and extent in traditional Lisps.
Edit: Use `gensym` in the code snippet.
flyin1501 · 2022-04-25 08:47
Ah, right, `set` and `symbol-value` (and so by extension `add-to-list`) only deal with global variables! So this is why the docstring discourages its use, this is quite confusing.
(let ((x 3))
(set 'x 4)
x) ; => 3 when using lexical binding
I didn't realize this, thank you.