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.