Common Lisp the Language, 2nd Edition

Next: Primitives Up: Optimization Previous: Defining New Series

## A.3.4. Declarations

A key feature of Lisp is that variable declarations are strictly optional. Nevertheless, it is often the case that they are necessary in situations where efficiency matters. Therefore, it is important that it be possible for programmers to provide declarations for every variable in a program. The transformation of series expressions into loops presents certain problems in this regard, because the loops created contain variables not evident in the original code. However, if the information described below is supplied by the user, appropriate declarations can be generated for all of the loop variables created.

All the explicit variables that are bound in a series expression (for example, by a `let` that is part of the expression) should be given informative declarations making use of the type specifier `(series element-type)` where appropriate.

Informative types should be supplied to series functions (such as `scan` and `map-fn`) that have type arguments. When using `scan` it is important to specify the type of element in the sequence as well as the sequence itself (for example, by using `(vector * integer)` as opposed to merely `vector`). The form `(list element-type)` can be used to specify the type of elements in a list.

If it is appropriate to have a type more specific than `(series t)` associated with the output of `#M`, `#Z`, `scan-alist`, `scan-file`, `scan-hash`, `scan-lists-of-lists-fringe`, `scan-lists-of-lists`, `scan-plist`, `series`, `latch`, or `catenate`, then the form `the` must be used to specify this type.

Finally, if the expression computing a non-series argument to a series variable is neither a variable nor a constant, `the` must be used to specify the type of its result.

For example, the declarations in the series expressions below are sufficient to ensure that every loop variable will have an accurate declaration.

    (collect-last (choose-if #'plusp (scan '(list integer) data)))

    (collect '(vector * float)
             (map-fn 'float #'/
                     (series (the integer (car data)))
                     (the (series integer) (scan-file f))))

The amount of information the user has to provide is reduced by the fact that this information can be propagated from place to place. For instance, the variable holding the output of `choose-if` holds a subset of the elements held by the input variable. As a result, it is appropriate for it to have the same type. When defining a new series function, the type specifier `series-element-type` can be used to indicate where type propagation should occur.

[Type specifier]
`series-element-type`

The type specifier `(series-element-type variable)` denotes the type of elements in the series held in variable. Variable must be a variable carrying a series value (for example, a series argument of a series function). `series-element-type` can be used only in three places: in a declaration in a `let`, `mapping`, `producing`, or other binding form in a series expression; in a declaration in a `defun` being used to define a series function; or in a type argument to a series function. As an example, consider that `collect-last` could have been defined as follows. The use of `series-element-type` ensures that the internal variable keeping track of the most recent item has the correct type.

    (defun collect-last (items &optional (default nil))
      (declare (optimizable-series-function))
      (collect-fn '(series-element-type items)
                  #'(lambda () default)
                  #'(lambda (old new) new)
                  items))

Next: Primitives Up: Optimization Previous: Defining New Series