Next: Problems with Code Movement, Previous: Other Features, Up: Top
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 cdr
s 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:
iterate
will use this information when available. In the
current example, the variable list17
will be given the type
list
, since that is the only type that makes sense; and the
variable result
will be given the type fixnum
, on the
assumption that you will not be counting high enough to need bignums.
You can override this assumption only by using and explicitly declaring a
variable:
(iter (declare (iterate:declare-variables)) (for el in number-list) (count (oddp el) into my-result) (declare (integer my-result)) (finally (return my-result)))
Other examples of the type assumptions that iterate
makes are: type
list
for into
variables of collection clauses; type
list
for expressions that are to be destructured; type
vector
for the variable holding the vector in a
for... in-vector
clause, and similarly for string
and the for... in-string
clause; and the
implementation-dependent type (type-of array-dimension-limit)
for the index and limit variables generated by sequence iteration
drivers like for... in-vector
and for...
in-string
(but not for... in-sequence
, because it may be
used to iterate over a list).
iterate
will examine expressions and try to determine their
types in a simple-minded way. If the expression is self-evaluating
(like a number, for instance), iterate
knows that the expression's
type is the same as the type of the value it denotes, so it can use
that type. If the expression is of the form (the
type
expr)
, iterate
is smart enough to extract type and use
it. However, the current version of iterate
does not examine
declarations of function result types or do any type inference. It
will not determine, for example, that the type of (+ 3 4)
is
fixnum
, or even number
.
iterate
generates an internal
variable for (f x)
in the clause (for i from 1 to (f
x))
, and in the absence of other information will give it the same
type as i
. If, however, the expression had been written
(the fixnum (f x))
, then iterate
would have given the internal
variable the type fixnum
regardless of i
's type. The
type incompatibility errors that could arise in this situation are not
checked for.
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
.
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.