Search code examples
listemacslispelispcons

What's the point of building a list with a non-list tail?


The Emacs lisp manual states about the function nconc that:

Since the last argument of nconc is not itself modified, it is reasonable to use a constant list, such as '(4 5), as in the above example. For the same reason, the last argument need not be a list

And indeed I can write

(setq x '(1 2 3))
=> (1 2 3)

(nconc x 0)
=> (1 2 3 . 0)

but that yields a totally broken list:

(length x)
=> eval: Wrong type argument: listp, 0

(butlast x)
=> butlast: Wrong type argument: listp, 0
  • How can I retrieve the original list? (reverse (cdr (reverse '(1 2 3 . 0)))) doesn't cut it either.
  • In which contexts is this a useful pattern? In the standard distribution some functions in minibuffer.el use it, in particular completion-all-completions and the like.

Solution

  • They're not "broken" lists; they're actually known as improper lists (as opposed to nil-terminated lists, which are proper lists). Many list functions, such as length and butlast that you just named, expect proper lists, and listp returns true only for proper lists.

    Improper lists are used in association lists (where the associations are often not proper; though the alist itself must be proper).


    If you want to make an improper list proper, you have two options:

    • Remove the "improper" element.
    • Treat the improper element as the last element of the proper list.

    Here's a procedure I wrote called properise which will do the former:

    (defun properise (x)
      (let ((r nil))
        (while (consp x)
          (push (pop x) r))
        (nreverse r)))
    

    (If you want the latter behaviour, add (unless (null x) (push x r)) just before the nreverse line.)