I'm looking to improve the quality and quantity of compiler warnings I receive --
is there a way in Common Lisp to include type predicates
over declared types as well as over instances -- implementation-specific answers
are fine, I'm interested in seeing how it's done, if anyone's doing it.
Compiling the following in CCL:
(defun non-list (o)
(not (listp o)))
(deftype non-list ()
'(satisfies non-list))
(defun example (a)
(list a))
(declaim (ftype (function (non-list) list) example))
(defun hmm ()
(declare (optimize (debug 3) (safety 3)))
(let ((a '(a b c))
(b '(d e f)))
(declare (type list a))
(example '(g h i))
(example a)
(example b)))
I'll get a compiler warning on the first call to example
-- the one that provides an
instance which can be checked against satisfies
. This is good, and with debug settings I'll get a runtime error which is good. What I'm wondering is if I can write something like the following:
(defun non-list-typep (type)
(not (subtypep type 'list)))
and somehow integrate it so that at least the second call -- (example a)
will warn at compile time as its declared type list
would fail the predicate non-list-typep
Cheers!
A bit background:
There are two different things: a standard language Common Lisp and widely different languages implementing and extending Common Lisp.
Common Lisp as a language does not require type warnings/errors like that at compile time. An implementation can mostly ignore type declarations. The language also does not really describe what should be done when the compiler does not ignore type declarations and how it should use them to statically check types.
Actual Common Lisp compilers fall in the following groups when it comes to statically declared types:
ignore them mostly. For example the compiler of the Symbolics Lisp Machine falls into this group.
uses declared types mostly for optimization purposes. I'd say CCL falls into this group. I don't think it does much type inference or propagation of type information. I would guess that's also a reason why the compiler is faster than SBCL - it does less.
uses declared types mostly for optimization purposes, but can do type inference and can give type information (for optimization). For example LispWorks falls into this group.
uses declared types for optimization purposes, does type inference, can give type information and regards type declarations as compile-time type assertions. The CMUCL and SBCL compilers are the main members of this group.
So, for implementation of type 2. you best need to declare everything, if you want the compiler to recognize it. Note that Common Lisp provides THE
and LOCALLY
to write declarations.
If you want to have type declarations as static type assertions and have the compiler check them, you should better use a compiler like SBCL.
A little background on its approach to type declarations can be found in the SBCL Manual: Handling of Types and Type Errors at Compile Time. More background here: The Python Compiler for CMU Common Lisp. Note that the compiler of CMU Common Lisp (and later of SBCL) is called Python - it has nothing to do with the programming language Python. The compiler already had its name (early 1980s) before the language Python (1991) existed.
An other way around is to implement a type checking language atop Common Lisp, like Qi. I guess it started with a couple smart macros around defun, to solve the problem you are facing.