4.5 Blocks and Exits

Common Lisp blocks provide a non-local exit mechanism very similar to catch and throw, with lexical scoping. This package actually implements cl-block in terms of catch; however, the lexical scoping allows the byte-compiler to omit the costly catch step if the body of the block does not actually cl-return-from the block.

Macro: cl-block name forms…

The forms are evaluated as if by a progn. However, if any of the forms execute (cl-return-from name), they will jump out and return directly from the cl-block form. The cl-block returns the result of the last form unless a cl-return-from occurs.

The cl-block/cl-return-from mechanism is quite similar to the catch/throw mechanism. The main differences are that block names are unevaluated symbols, rather than forms (such as quoted symbols) that evaluate to a tag at run-time; and also that blocks are always lexically scoped. In a dynamically scoped catch, functions called from the catch body can also throw to the catch. This is not an option for cl-block, where the cl-return-from referring to a block name must appear physically within the forms that make up the body of the block. They may not appear within other called functions, although they may appear within macro expansions or lambdas in the body. Block names and catch names form independent name-spaces.

In true Common Lisp, defun and defmacro surround the function or expander bodies with implicit blocks with the same name as the function or macro. This does not occur in Emacs Lisp, but this package provides cl-defun and cl-defmacro forms, which do create the implicit block.

The Common Lisp looping constructs defined by this package, such as cl-loop and cl-dolist, also create implicit blocks just as in Common Lisp.

Because they are implemented in terms of Emacs Lisp’s catch and throw, blocks have the same overhead as actual catch constructs (roughly two function calls). However, the byte compiler will optimize away the catch if the block does not in fact contain any cl-return or cl-return-from calls that jump to it. This means that cl-do loops and cl-defun functions that don’t use cl-return don’t pay the overhead to support it.

Macro: cl-return-from name [result]

This macro returns from the block named name, which must be an (unevaluated) symbol. If a result form is specified, it is evaluated to produce the result returned from the block. Otherwise, nil is returned.

Macro: cl-return [result]

This macro is exactly like (cl-return-from nil result). Common Lisp loops like cl-do and cl-dolist implicitly enclose themselves in nil blocks.

Macro: cl-tagbody &rest labels-or-statements

This macro executes statements while allowing for control transfer to user-defined labels. Each element of labels-or-statements can be either a label (an integer or a symbol), or a cons-cell (a statement). This distinction is made before macroexpansion. Statements are executed in sequence, discarding any return value. Any statement can transfer control at any time to the statements that follow one of the labels with the special form (go label). Labels have lexical scope and dynamic extent.