push does not work as I would expect it - why?

common-lisp, lisp

Solution

If you read the CL Hyperspec for PUSH, you will read that `push` expects a place.

A place is something like a variable, a structure slot, a class slot, an array access, or similar. Since Lisp uses linked cons cells for lists, it does not make sense to push something in front of a cons cell, without a reference for that.

So above is simple: we can't push to a direct list.

Why this error message?

This gets a bit complicated...

(push 2 '(3 5 7 11))

Is actually:

(push 2 (quote (3 5 7 11))

A function can be a place, it then needs a corresponding setter function. Here the setter is thought to be `(setf quote)` - that's right, Common Lisp can sometimes have lists as function names, not only symbols.

If we look at the macroexpansion of above:

? (pprint (macroexpand '(push 2 (quote (3 5 7 11)))))

(LET* ((#:G328 2) (#:G327 (3 5 7 11)) (#:G326 (CONS #:G328 '#:G327)))
  #:G327
  #:G326
  (FUNCALL #'(SETF QUOTE) #:G326 #:G327))

You can see that it tries to call the setter. But it also thinks that `(3 5 7 11)` is a Lisp form.

I give you an example, where it actually works, but we don't use `quote`, but a real accessor function:

CL-USER 40 > (let ((v (vector (list (list 'a 'b 'c) (list 'd 'e 'f))
                              (list (list 1 2 3)    (list 4 5 6)))))
               (print v)
               (push 42 (first (aref v 1)))
               (print v)
               (values))

#(((A B C) (D E F)) ((1 2 3) (4 5 6))) 
#(((A B C) (D E F)) ((42 1 2 3) (4 5 6))) 

In above `first` is the getter and CL knows the corresponding setter. The form `(aref v 1)` is the call and returns the index 1 element of the vector. We are then pushing to the first list of the element.

Your call has a similar structure and `(3 5 7 11)` is at a similar position as `(aref v 1)`. The Lisp system says that in `(3 4 7 11)` then number `3` is not a valid function. Which is correct. But the real error was about the `push` operation. Since the macro could not detect the error, the error gets later detected in the macro expanded code.

Problem

I am experiencing a behavior of the `push` function that I don't get. Maybe someone could explain to me why Lisp behaves this way. Supposed I define a list as a global variable, and then try to push a new value to it using the following code: ``` (defparameter *primes* '(3 5 7 11)) (push 2 *primes*) ``` Then `*primes*` is now `(2 3 5 7 11)`. So far, so good. Now I try to do the same thing, but without the `*primes*` variable, i.e.: ``` (push 2 '(3 5 7 11)) ``` The result is an error message: EVAL: 3 is not a function name; try using a symbol instead Now I have two questions: - Why does this not work? I would expect that `push` returns the list `(2 3 5 7 11)`, why does this not happen? Where am I wrong? - Apart from that, I don't get the error message. What is Lisp trying to tell me with `3 is not a function name`? Of course, `3` is not a function name, but I don't try to call a function named `3` anywhere, do I? Any help is appreciated :-)

Original source