Chapter 14

The Built-In Macros and Special Definitions


Local Declarations

Local declarations are used to create bindings or install handlers that are active for the remainder of the innermost body containing the declaration. Bindings created by local declarations can be referenced only in the remaining program text of the body. Handlers installed are active while the execution of the remainder of the body is active, which includes the time during which any functions called from the remainder of the body are active.

Table 14-2 Local Declarations

Macro

Description

Page

let

Creates and initializes new local bindings within the smallest enclosing implicit body.

390

local

Creates new local bindings within the smallest enclosing implicit body and initializes them to local methods that can be self-recursive and mutually recursive.

391

let handler

Establishes a condition handler for the duration of the execution of smallest enclosing implicit body.

392

let [Local Declaration]


Creates and initializes new local bindings within the smallest enclosing implicit body.

Macro Call:

let variables = init ;

Arguments:
variables

variablebnf | ( variable-listbnf )

init

expressionbnf

Description:

let creates local bindings for the variables, and initializes them to the values returned by init. The bindings are visible for the remainder of the smallest enclosing implicit body.

The first value returned by the init is bound to the first variable, the second value to the second variable, etc. The last variable may be preceded by #rest, in which case it is bound to a sequence containing all the remaining values.

Each variable is a variable-name or a variable-name followed by a specializer.

If more than one binding is defined, the variables are enclosed in parentheses and separated by commas.

let start = 0;

let (whole-part, remainder) = truncate(amount);

let (first-value, #rest rest-values) = get-initial-values();

Local variables may be specialized. This ensures that their value will always be of a given type. An attempt to initialize or assign the variable to a value not of that type will signal an error of type <type-error>.

let elapsed-time :: <integer> = 0;

let the-front-window :: <window> = front-window();

let(whole-part :: <integer>, remainder :: <real>) = truncate(amount);

local [Local Declaration]


Creates new local bindings within the smallest enclosing implicit body and initializes them to local methods that can be self-recursive and mutually recursive.

Macro Call:

local { [ method ] name parameter-list [ body ] end [ method ] [ name ] } ,+

Arguments:
name

variable-namebnf

parameter-list

parameter-listbnf

body

bodybnf

Description:

local creates local methods that may be mutually recursive and self-recursive.

Each name creates a new local binding. The binding is initialized to a new method specified by the parameter-list and body. In addition to being visible for the remainder of the smallest enclosing implicit body, the bindings created for the names are visible to the parameter-lists and bodies of all the methods created by the local declaration.

The parameter-list is a standard method parameter list. A complete description of parameter lists is given in "Parameter Lists" on page 84.

The body is an implicit body.

let handler [Local Declaration]


Establishes a condition handler for the duration of the execution of smallest enclosing implicit body.

Macro Call:

let handler condition = handler

Arguments:
condition

type | ( type { , option }* )

type

expressionbnf

option

test-option | init-option

test-option

test: expressionbnf

init-option

init-arguments: expressionbnf

handler

expressionbnf

Description:

let handler establishes a new condition handler that is in effect for the duration of the execution of the remainder of the smallest enclosing implicit body. Unlike the local declarations let and local, let handler does not create any bindings.

  • The condition describes the conditions for which the handler is applicable.
    • The type is the type of the applicable conditions. The handler will be applicable to conditions that are general instances of type.
    • The test-option is a function that is called to further test the applicability of the handler. When a condition of type type is signaled, the test function will be called with that condition as an argument. If the test returns true, the handler is considered applicable to the condition. If the test returns false, the handler is considered to be inapplicable to the condition. The default value of this option is a function that always returns true. There can be at most one test-option.

      An example use for this feature is a restart handler for restarting only from a particular condition object, for example restarting from an unbound-slot error by setting the slot and retrying the invocation of the accessor. The <set-and-continue> restart condition will have the signaled <unbound-slot> condition in a slot, and the handler's test will check for it. (These class names are invented for this example and are not part of the specification.)
    • The init-option is a sequence of alternating keywords and objects that can be used as initialization arguments to construct a condition to which the handler is applicable. For example, if the handler is a restart handler, a program could retrieve the init-option by calling do-handlers, and could then use them to construct a corresponding restart. There can be at most one init-option. init-option defaults to an empty sequence.
  • The handler is function called to handle a condition that matches type and passes test-option. The function should accept two arguments. The first argument will be the condition being signaled, and the second argument will be a next-handler function. The handler handles the condition by taking a nonlocal exit, returning values according to the condition's recovery protocol, or tail-recursively calling signal of a restart. The function can decline to handle the condition by tail-recursively calling the next-handler function with no arguments.

test-option and handler are distinct so that handler applicability can be tested without actually handling (which might take a nonlocal exit). One use for this is constructing a list of available restart handlers.

There is no "condition wall," i.e., when executing handler the set of available handlers is not reset to the handlers that were in effect when the let handler was entered.

Implementations are encouraged to implement let handler in a way that optimizes establishing a handler for both speed and space, even if that increases the cost of signaling. The assumption is that most of the time a handler will never be used, because the exception it is looking for will never occur.

type, handler, test-option, and init-option are executed before execution of the rest of the enclosing body begins.