Next: , Previous: Other Features, Up: Top

4 Types and Declarations

4.1 Discussion

Sometimes efficiency dictates that the types of variables be declared. This type information needs to be communicated to iterate so it can bind variables to appropriate values. Furthermore, iterate must often generate internal variables invisible to the user; there needs to be a way for these to be declared.

As an example, consider this code, which will return the number of odd elements in number-list:

  (iter (for el in number-list)
        (count (oddp el)))

In processing this form, iterate will create an internal variable, let us call it list17, to hold the successive cdrs of number-list, and will bind the variable to number-list. It will also generate a default binding for el; only inside the body of the loop will el be set to the car of list17. Finally, iterate will generate a variable, call it result, to hold the result of the count, and will bind it to zero.

When dealing with type declarations, iterate observes one simple rule: it will never generate a declaration unless requested to do so. The reason is that such declarations might mask errors in compiled code by avoiding error-checks; the resulting problems would be doubly hard to track down because the declarations would be hidden from the programmer. Of course, a compiler might omit error-checks even in the absence of declarations, though this behavior can usually be avoided, e.g. by saying (declaim (optimize (safety 3))).

So, the above iterate form will generate code with no declarations. But say we wish to declare the types of el and the internal variables list17 and result. How is this done?

Declaring the type of el is easy, since the programmer knows the variable's name:

  (iter (for el in number-list)
        (declare (fixnum el))
        (counting (oddp el)))

iterate can read variable type declarations like this one. Before processing any clauses, it scans the entire top-level form for type declarations and records the types, so that variable bindings can be performed correctly. In this case, el will be bound to zero instead of nil. Also, iterate collects all the top-level declarations and puts them at the begining of the generated code, so it is not necessary to place all declarations at the beginning of an iterate form; instead, they can be written near the variables whose types they declare.

Since iterate is not part of the compiler, it will not know about declarations that occur outside an iterate form; these declarations must be repeated inside the form.

Here is another way we could have declared the type of el:

  (iter (for (the fixnum el) in number-list)
        (counting (oddp el)))

iterate extends the Common Lisp the form to apply to variables as well as value-producing forms; anywhere a variable is allowed—in a with clause, as the iteration variable in a driver clause, as the into argument of an accumulation clause, even inside a destructuring template—you can write (the type symbol) instead.

There is one crucial difference between using a the form and actually declaring the variable: explicit declarations are always placed in the generated code, but type information from a the form is not turned into an actual declaration unless you tell iterate to do so using iterate:declare-variables. See below.

Declaring the types of internal variables is harder than declaring the types of explicitly mentioned variables, since their names are unknown. You do it by declaring iterate:declare-variables somewhere inside the top level of the iterate form. (This will also generate declarations for variables declared using the.) iterate does not provide much selectivity here: it's all or none. And unfortunately, since iterate is not privy to compiler information but instead reads declarations itself, it will not hear if you (declaim (iterate:declare-variables)). Instead, set the variable iterate::*always-declare-variables* to t at compile-time, using eval-when.

To determine the appropriate types for internal variables, iterate uses three sources of information:

Note that if you do declare iterate:declare-variables, then iterate may declare user variables as well as internal ones if they do not already have declarations, though only for variables that it binds. For instance, in this code:

  (iter (declare (iterate:declare-variables))
        (for i from 1 to 10)
        (collect i into var))

the variable var will be declared to be of type list.

4.2 Summary

iterate understands standard Common Lisp variable type declarations that occur within an iterate form and will pass them through to the generated code. If the declaration (iterate:declare-variables) appears at the top level of an iterate form, or if iterate::*always-declare-variables* is non-nil, then iterate will use the type information gleaned from user declarations, self-evaluating expressions and the expressions, combined with reasonable assumptions, to determine variable types and declare them.