Since you are asking:
Is it possible to use (declare (type ...))
declarations in functions but also perform type-checking on the function arguments, in order to produce faster but still safe code?
it seems to me that you are missing an important point about the type system of Common Lisp, that is that the language is not (and cannot-be) statically typed. Let's clarify this aspect.
Programming languages can be roughly classified in three broad categories:
- with static type-checking: every expression or statement is
checked for type-correctness at compile time, so that type errors
can be detected during program development, and the code is more
efficient since no check for types must be done at run-time;
- with dynamic type checking: every operation is checked at run time
for type correctness, so that no type-error can occur at run-time;
- without type checking: type-errors can occur at run-time so that the
program can stop for error or have an undefined behaviour.
Edited
With the respect to the previous classification, the Common Lisp specification left to the implementations the burden of deciding if they want to follow the second or the third approach! Not only, but through the optimize
declaration the specification lets the implementations free to change this dinamically, in the same program.
So, most implementations, at the initial optimization and safety levels, implements the second approach, enriched with the following two possibilities:
one can request to the compiler to omit run time type-checking when compiling some piece of code, typically for efficiency reasons, so that, inside that particular piece of code, and depending on the optimization and safety settings, the language can behave like the languages of the third category: this could be supported by hints through type declarations, like (declare (type fixnum x))
for variables and (the fixnum (f x))
for values;
one can insert into the code explicit type-checking tests to be performed at run-time, through check-type
, so that an eventual difference in the type of the value checked will cause a “correctable error”.
Note, moreover, that different compilers can behave differently in checking types at compile times, but they can never reach the level of compilers for languages with static type checking because Common Lisp is a highly dynamical language. Consider for instance this simple case:
(defun plus1(x) (1+ x))
(defun read-and-apply-plus1()
(plus1 (read)))
in which no static type-checking can be done for the call (plus1 (read))
, since the type of (read)
is not known at compile time.
(read)
equivalent at compile-time? – Lysimachus