4.7.6 Other Clauses

This section describes the remaining loop clauses.

with var = value

This clause binds a variable to a value around the loop, but otherwise leaves the variable alone during the loop. The following loops are basically equivalent:

(cl-loop with x = 17 do …)
(let ((x 17)) (cl-loop do …))
(cl-loop for x = 17 then x do …)

Naturally, the variable var might be used for some purpose in the rest of the loop. For example:

(cl-loop for x in my-list  with res = nil  do (push x res)
         finally return res)

This loop inserts the elements of my-list at the front of a new list being accumulated in res, then returns the list res at the end of the loop. The effect is similar to that of a collect clause, but the list gets reversed by virtue of the fact that elements are being pushed onto the front of res rather than the end.

If you omit the = term, the variable is initialized to nil. (Thus the ‘= nil’ in the above example is unnecessary.)

Bindings made by with are sequential by default, as if by let*. Just like for clauses, with clauses can be linked with and to cause the bindings to be made by let instead.

if condition clause

This clause executes the following loop clause only if the specified condition is true. The following clause should be an accumulation, do, return, if, or unless clause. Several clauses may be linked by separating them with and. These clauses may be followed by else and a clause or clauses to execute if the condition was false. The whole construct may optionally be followed by the word end (which may be used to disambiguate an else or and in a nested if).

The actual non-nil value of the condition form is available by the name it in the “then” part. For example:

(setq funny-numbers '(6 13 -1))
     ⇒ (6 13 -1)
(cl-loop for x below 10
         if (cl-oddp x)
           collect x into odds
           and if (memq x funny-numbers) return (cdr it) end
           collect x into evens
         finally return (vector odds evens))
        ⇒ [(1 3 5 7 9) (0 2 4 6 8)]
(setq funny-numbers '(6 7 13 -1))
     ⇒ (6 7 13 -1)
(cl-loop <same thing again>)
        ⇒ (13 -1)

Note the use of and to put two clauses into the “then” part, one of which is itself an if clause. Note also that end, while normally optional, was necessary here to make it clear that the else refers to the outermost if clause. In the first case, the loop returns a vector of lists of the odd and even values of x. In the second case, the odd number 7 is one of the funny-numbers so the loop returns early; the actual returned value is based on the result of the memq call.

when condition clause

This clause is just a synonym for if.

unless condition clause

The unless clause is just like if except that the sense of the condition is reversed.

named name

This clause gives a name other than nil to the implicit block surrounding the loop. The name is the symbol to be used as the block name.

initially [do] forms

This keyword introduces one or more Lisp forms which will be executed before the loop itself begins (but after any variables requested by for or with have been bound to their initial values). initially clauses can appear anywhere; if there are several, they are executed in the order they appear in the loop. The keyword do is optional.

finally [do] forms

This introduces Lisp forms which will be executed after the loop finishes (say, on request of a for or while). initially and finally clauses may appear anywhere in the loop construct, but they are executed (in the specified order) at the beginning or end, respectively, of the loop.

finally return form

This says that form should be executed after the loop is done to obtain a return value. (Without this, or some other clause like collect or return, the loop will simply return nil.) Variables bound by for, with, or into will still contain their final values when form is executed.

do forms

The word do may be followed by any number of Lisp expressions which are executed as an implicit progn in the body of the loop. Many of the examples in this section illustrate the use of do.

return form

This clause causes the loop to return immediately. The following Lisp form is evaluated to give the return value of the loop form. The finally clauses, if any, are not executed. Of course, return is generally used inside an if or unless, as its use in a top-level loop clause would mean the loop would never get to “loop” more than once.

The clause ‘return form’ is equivalent to ‘do (cl-return form)’ (or cl-return-from if the loop was named). The return clause is implemented a bit more efficiently, though.

While there is no high-level way to add user extensions to cl-loop, this package does offer two properties called cl-loop-handler and cl-loop-for-handler which are functions to be called when a given symbol is encountered as a top-level loop clause or for clause, respectively. Consult the source code in file cl-macs.el for details.

This package’s cl-loop macro is compatible with that of Common Lisp, except that a few features are not implemented: loop-finish and data-type specifiers. Naturally, the for clauses that iterate over keymaps, overlays, intervals, frames, windows, and buffers are Emacs-specific extensions.