Search code examples
common-lisp

Cant use let variables in when condition


I have following code snippet:

(defun upsi()
    (let ((firstpos (car *old-list*)) (secondpos (car(cdr *old-list*))))
        (let ((found nil)(secondlocation (list (car (cdr firstpos)) (car secondpos))))
            (when (= (found nil))
                (setq *new-list* (list(firstlocation)))))))

This gives me following error "The function COMMON-LISP-USER::FOUND is undefined" But when I try this code, it works perfectly.

(defun upsi()
    (let ((firstpos (car *stenches*)) (secondpos (car(cdr *stenches*))))
        (let ((found nil)(secondlocation (list (car (cdr firstpos)) (car secondpos))))
            (print found)
            (print secondlocation))))

What is the cause, that I cant use the variables found and secondlocation in my when condition. In fact I also cant use them in a loop. But when I print them I dont get any errors. It looks to me that they are out of scope but I dont understand why ?

I am just into common-lisp but this confuses me so much^^


Solution

  • Parentheses carry semantic meaning in Common Lisp, and you can't just place them anyplace you like as you can in many other languages. The first position of a list expression is interpreted as an identifier for a function, and that function is called on the arguments; this will fail when there is no such function.

    In (when (= (found nil)) you are calling the function found, which doesn't exist. You probably meant (when (= found nil)), but this wouldn't work either because = is meant for comparing numbers in Common Lisp. You could use the null predicate, or it might be better to use an unless form instead of when.

    The same problem also seems to occur in the body of the when form: (list (firstlocation)). Here the parentheses indicate that firstlocation is a function that you want to call. I don't think that this is the case given that secondlocation is a list, but I don't see firstlocation defined anyplace so I could be wrong.

    A couple of other observations: there is a let* form that allows you to use identifiers which were previously bound in the same form. There is also cadr to use instead of calling car on the result of calling cdr, or you could use second. As presented here, your code is very difficult to read and reason about; you should really use an editor that properly indents your code, and look at some examples of lisp written by experienced programmers to get a feel for what it should look like.

    Here are a couple of rewritten versions of your code; the first uses null and the second uses unless. I have assumed that firstlocation is a list, and just renamed secondlocation to firstlocation since secondlocation isn't used in the posted code for the sake of getting the code to function.

    (defvar *old-list* '())
    (defvar *new-list* '())
    
    ;; Using `let*`, `cadr`, and `when` with `null`:
    
    (defun upsi()
      (let* ((firstpos (car *old-list*))
             (secondpos (car (cdr *old-list*)))  ; could also use `cadr` or `second`
             (found nil)
             (firstlocation (list (cadr firstpos) (car secondpos))))
        (when (null found)
          (setq *new-list* (list firstlocation)))))
    
    ;; Using `let*`, `second`, and `unless`:
    
    (defun upsi()
      (let* ((firstpos (car *old-list*))
             (secondpos (car (cdr *old-list*)))  ; could also use `cadr` or `second`
             (found nil)
             (firstlocation (list (second firstpos) (car secondpos))))
        (unless found
          (setq *new-list* (list firstlocation)))))