2

I am having a problem in understanding how the with keyword works. In particular, I considered it to be same as a let statement but it does not follow.

For example, these two codes "should" print the same values but the first one gives (nil nil) while the latter works fine.

(loop for c in clauses
    with p = (car c)
    collect p)

(loop for c in clauses
    collect (car c))

2 Answers 2

8

with is to create local auxiliary variables. They are initialized once before the loop started and thus would be identical to writing this:

(let ((p (car c))
  (loop for c in clauses
    collect p))

Except the fact that it seems c exists as nil at the same time (car c) i sdone. I think it's because loop creates all their variables in one go at the beginning like I created p here.

You are looking for for:

(loop 
  for c in clauses
  for p = (car c)
  collect p)

Why not do it with destructuring?:

(loop 
  for (p) in clauses
  collect p)
Sign up to request clarification or add additional context in comments.

2 Comments

The for= clause executes each time when no then is given; you do not need to repeat the form.
@Svante That makes sense. I try to avoid using = since while you can do everything with it it is never the simplest way to do it.
5

One thing which helps to understand LOOP a bit better is that a LOOP has three different clause sections

(loop

  ; first a single optional NAME clause

  ; then zero or more variable clauses with WITH, INITIAL, FINALLY and/or FOR

  ; then zero or more main clauses with DO, RETURN, COLLECT, APPEND, SUM, NCONC, ...
  )

One has to keep the order of these clause sections.

There are two ways to introduce variables: FOR and WITH. FOR updates the variable in each iteration and WITH will do it only once. You can write these clauses in any order within the correct section, but generally the WITH binding and its value will be created before the FOR variable will have a correct value - though not always.

LispWorks warns about your specific code:

CL-USER 6 > (loop for c in '((1) (2) (3))
                  with p = (car c)
                  collect p)

Warning: Local Variable Initialization clause
  (the binding of P) follows iteration forms but will be evaluated before them.

(NIL NIL NIL)

Often

(loop for c in clauses
    with p = (car c)
    collect p)

will be implemented by something like this:

(...
   (let ((c nil) ...)
     (let ((p (car c)))   ; your (CAR ...)  form

        ; iteration code ...

     )))

In this case you had some 'luck', since (car nil) happens to work and only the result is not what you expect - silently.

But this will create an error:

(loop for c in '((1) (2) (3))
      with p = (1+ c)    ; note the 1+ instead of CAR
      collect p)

Here (1+ nil) won't work and will be an error, because the function 1+ accepts only numbers as arguments. You won't see an unexpected result, but an error.

Style Rules

  • Don't mix FOR and WITH clauses.
  • Write WITH clauses before FOR clauses.
  • Don't depend on implementation specific behaviour and effects.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.