Common lisp push from function

Solution 1:

If you write functions in Lisp it is preferable to think 'functionally'. A function takes values and returns values. A typical rule would be to avoid side effects. So your function should return a result value, not 'modify' a variable value.

Instead of:

(defparameter *result* '())

(defun foo (a)
   (push a *result*))

use:

(defparameter *result* '())

(defun foo (a result)
  (push a result)
  result)

(setf *result* (foo a *result*))

Note also that aggregate does not need the progn.

Slightly advanced (don't do that):

If you have a global list:

(defparameter *foo* '())

You can't push onto it, as we have seen, like this:

(defun foo (l)
   (push 1 l))

If you call foo the variable *foo* is unchanged. Reason: Lisp does not pass a variable reference, it passes the value of the variable.

But how can we pass a reference? Well, pass a reference: a cons cell would do it (or a structure, a vector, a CLOS object, ...):

CL-USER 38 > (defparameter *foo* (list '()))
*FOO*

CL-USER 39 > (defun foo (ref)
               (push 1 (first ref)))
FOO

CL-USER 40 > (foo *foo*)
(1)

CL-USER 41 > (foo *foo*)
(1 1)

Now, if we look at *foo*, it is changed. But we haven't really changed the variable. We have changed the first entry of the list.

CL-USER 42 > *foo*
((1 1))

But, don't do it. Program in a functional style.

Solution 2:

You cannot modify the contents of a variable with a function that only takes the variable's value.

Take the following simple example:

(defun futile-push (thing list)
  (push thing list))

(let ((foo (list 1)))
  (futile-push 2 foo))

What happens?

  • Foo is evaluated to the list it points to.
  • 2 evaluates to 2.
  • These two arguments are passed to the function.

Inside the function invocation:

  • Thing is now bound to 2.
  • List is now bound to the list (1).

Note that the list does not know that it is also referenced by the variable foo outside the function.

         foo
          |
          v
        ---------
list -> | 1 |NIL|
        ---------
  • Push modifies the variable list in such a way that it is now bound to the list (2 1).

Note that this does not affect foo outside. Foo still points to the same thing as before.

                     foo
                      |
                      v
        ---------   ---------
list -> | 2 | ----> | 1 |NIL|
        ---------   ---------
  • Futile-push returns the return value of the push form, which happens to be the new value of list.

  • That return value is never used or bound, so it vanishes.

     foo
      |
      v
    ---------
    | 1 |NIL|
    ---------
    

The most straightforward way to do what you want is to return the new value and then set the variable outside:

(let ((foo (list 1)))
  (setf foo (not-so-futile-push 2 foo)))

If you need to do that at more than one place, it might be worthwhile to write a macro for that which expands to the setf form. Note that push is itself a macro for exactly these reasons.