0

I’m trying to add several values at once to a given list, which may be nil or already contain some other values.

After some research, I found the two following versions are working well:

(let ((x '(a)))
 (setq x (append x '(b c)))
 x)
⇒ (a b c)

(let ((x '(a)))
 (nconc x '(b c)) 
 x)
⇒ (a b c)

However, if the "main" list is nil at the beginning, nconc does not seem to work as expected:

(let (x)
 (setq x (append x '(b c)))
 x)
⇒ (b c)

(let (x) 
 (nconc x '(b c))
 x)
⇒ nil

Is someone able to explain the last exemple? I would have expected it to return (b c). Did I miss something?

Just in case, I also tested with an empty list, same result:

(let ((x '()))
 (nconc x '(b c))
 x)
⇒ nil

However, the following is working, but in that case I don’t see added value compared to append:

(let (x) ;; work with an empty list too
 (setq x (nconc x '(b c)))
 x)
⇒ (b c)

Thank you very much to any explanation on this.

1
  • This is likely a duplicate question. See the doc (Elisp manual) for nconc and other "destructive" functions that can modify list structure. In general, they do not modify structure if they don't need to. And more importantly here: they don't know about or care about variables that might point to parts of the structure. It's up to you to set a variable you care about to a possibly new value. So what you did using setq is necessary. See the doc.
    – Drew
    Aug 12 at 18:00
0

For some existing explanations on this, see:

(let ((x '(a)))
  (nconc x '(b c)) 
  x)

Don't do this. nconc works by destructively modifying all but its last argument, but x in this case is a quoted list literal, which is not safe to modify; see (info "(elisp) Mutability").

It is okay to use nconc if you know for sure that x is safe to modify, as in the following example:

(let ((x (list 'a)))
  (nconc x '(b c))
  x)

If you are not sure whether x is safe to modify, use append instead, or make a copy of x first, e.g. using copy-sequence.

Is someone able to explain the last exemple? I would have expect it to return (b c). Did I miss something?

Yes. What nconc does is link all of its arguments together, by modifying each argument (except the last) in-place to point to the next argument.

How would this work for a nil argument? The answer is it doesn't: appending an empty list to any other list is the same as not appending the empty list at all!

For example, (nconc (list 1) () (list 2)) is equivalent to (nconc (list 1) (list 2)), which is (1 2) - the empty list is skipped.

Besides, even if it did make sense to link nil like any other list, then Emacs would complain, because nil is a protected constant that can't be modified.

However, the following is working

Yes, that is the correct way to use nconc if you don't know ahead of time whether x will be empty. If x is guaranteed to be non-nil, then using nconc without assigning the result back to same variable is fine. If, OTOH, x might be nil, then you must assign the result back to some variable, otherwise the result will be lost.

but in that case I don’t see added value compared to append

append makes a copy of all but its last argument. It can also concatenate sequences of other types, like strings and vectors, in addition to lists.

By contrast, nconc is intended for a very specific purpose: linking mutable lists in-place and fast, without any extra memory allocations due to copying. It is effectively a generalisation of setcdr that operates on the last link of any number of cons cells.

If you're not sure which of the two to use, it's safer to go with append.

7
  • Please consider adding a link to the Elisp manual, which covers this well. Thx.
    – Drew
    Aug 12 at 18:01
  • @Drew Done, thanks.
    – Basil
    Aug 12 at 18:05
  • Thank you very much for your very complete answer! I realize I still not understand some core concept of lisp :/ I read the documentation of both nconc and append before asking but did not find it very explicit. I think I now understand how append and nconc work but it still a bit blurry. I’ll need to test to understand. However, if you don’t mind, could you elaborate a bit about mutable variable? I’m not sure to understand why (list 'a) is safer than '(a). The mutability info node does not explain that :( Thanks again
    – Étienne
    Aug 12 at 20:01
  • 1
    In your sample code, the '(a) is part of the source code you were running. Modifying it destructively thus also modifies the source code; if you were to run the same source code again then you would get different results.
    – db48x
    Aug 12 at 20:29
  • 1
    @ÉtienneDeparis Each time you evaluate (list 'a) or (string ?a), a new list/string is created, that is guaranteed to be unique from any other list/string with the same contents, i.e. (eq (list 'a) (list 'a)) is always nil. By contrast, '(a) and "a" are treated as self-evaluating constants - it is possible, but not guaranteed, that (eq '(a) '(a)) will be non-nil. In particular, the byte-compiler will often share (aka "fold") the same literal whenever it reappears in a program, so modifying one of its appearances actually modifies all of its appearances.
    – Basil
    Aug 12 at 20:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.