Next: Compiler Policy
Up: The Compiler
Previous: Weakened Type Checking
  Contents
  Index
Getting Existing Programs to Run
to run
portability
compatibility with other Lisps
Since Python does much more comprehensive type checking than other
Lisp compilers, Python will detect type errors in many programs
that have been debugged using other compilers. These errors are
mostly incorrect declarations, although compile-time type errors can
find actual bugs if parts of the program have never been tested.
Some incorrect declarations can only be detected by run-time type
checking. It is very important to initially compile programs with
full type checks and then test this version. After the checking
version has been tested, then you can consider weakening or
eliminating type checks. This applies even to previously debugged
programs. Python does much more type inference than other
Common Lisp compilers, so believing an incorrect declaration does much
more damage.
The most common problem is with variables whose initial value doesn't
match the type declaration. Incorrect initial values will always be
flagged by a compile-time type error, and they are simple to fix once
located. Consider this code fragment:
(prog (foo)
(declare (fixnum foo))
(setq foo ...)
...)
Here the variable foo is given an initial value of nil, but
is declared to be a fixnum. Even if it is never read, the
initial value of a variable must match the declared type. There are
two ways to fix this problem. Change the declaration:
(prog (foo)
(declare (type (or fixnum null) foo))
(setq foo ...)
...)
or change the initial value:
(prog ((foo 0))
(declare (fixnum foo))
(setq foo ...)
...)
It is generally preferable to change to a legal initial value rather
than to weaken the declaration, but sometimes it is simpler to weaken
the declaration than to try to make an initial value of the
appropriate type.
Another declaration problem occasionally encountered is incorrect
declarations on defmacro arguments. This probably usually
happens when a function is converted into a macro. Consider this
macro:
(defmacro my-1+ (x)
(declare (fixnum x))
`(the fixnum (1+ ,x)))
Although legal and well-defined Common Lisp, this meaning of this
definition is almost certainly not what the writer intended. For
example, this call is illegal:
(my-1+ (+ 4 5))
The call is illegal because the argument to the macro is (+ 4
5), which is a list, not a fixnum. Because of
macro semantics, it is hardly ever useful to declare the types of
macro arguments. If you really want to assert something about the
type of the result of evaluating a macro argument, then put athe in the expansion:
(defmacro my-1+ (x)
`(the fixnum (1+ (the fixnum ,x))))
In this case, it would be stylistically preferable to change this
macro back to a function and declare it inline. Macros have no
efficiency advantage over inline functions when using Python.
See section inline-expansion.
Some more subtle problems are caused by incorrect declarations that
can't be detected at compile time. Consider this code:
(do ((pos 0 (position #
a string :start (1+ pos))))
((null pos))
(declare (fixnum pos))
...)
Although pos is almost always a fixnum, it is nil
at the end of the loop. If this example is compiled with full type
checks (the default), then running it will signal a type error at the
end of the loop. If compiled without type checks, the program will go
into an infinite loop (or perhaps position will complain
because (1+ nil) isn't a sensible start.) Why? Because if
you compile without type checks, the compiler just quietly believes
the type declaration. Since pos is always a fixnum, it
is never nil, so (null pos) is never true, and the loop
exit test is optimized away. Such errors are sometimes flagged by
unreachable code notes (see section dead-code-notes), but it is still
important to initially compile any system with full type checks, even
if the system works fine when compiled using other compilers.
In this case, the fix is to weaken the type declaration to(or fixnum null).4.3Note that there is usually little performance penalty for weakening a
declaration in this way. Any numeric operations in the body can still
assume the variable is a fixnum, since nil is not a legal
numeric argument. Another possible fix would be to say:
(do ((pos 0 (position #
a string :start (1+ pos))))
((null pos))
(let ((pos pos))
(declare (fixnum pos))
...))
This would be preferable in some circumstances, since it would allow a
non-standard representation to be used for the local pos
variable in the loop body (see section 5.11.3.)
In summary, remember that all values that a variable everhas must be of the declared type, and that you should test using safe
code initially.
Next: Compiler Policy
Up: The Compiler
Previous: Weakened Type Checking
  Contents
  Index
Peter Van Eynde
2001-03-08