Of course, if you pass a fixnum and the result cannot actually be stored in a fixnum, that's no good. So you need to either handle that case or not make such a declaration.
> Compatibility note: This construct is borrowed from the Interlisp DECL package; Interlisp, however, allows an implicit progn after the type specifier rather than just a single form. The MacLisp fixnum-identity and flonum-identity constructs can be expressed as (the fixnum x) and (the single-float x).
Right, though I'm not sure it's easy to infer an ordinary function's return type from this kind of declaration.
Speaking of return types, do you have any idea if any implementation takes a generic function's return type declaration seriously? I believe SBCL currently doesn't, which is unfortunate (defmethods keep clobbering the ftype declaration).
Modern compilers, and in particular SBCL is able to use type inference so that in many cases it is able to deduce these optimisations even without explicit type specifications. Or at least you can add the type specification in one place and then other functions will be able to take advantage of this.
I should probably write a followup article that goes into more details on this.
I've had no trouble getting SBCL to infer return types when calling other functions with the appropriate DECLARE or DECLAIM (as displayed with DISASSEMBLE and DESCRIBE), but so far I've not been able to verify that the compiler will infer the argument types in the same case. For example, if F takes two (signed-byte 8) and returns a (signed-byte 8) and I (defun g (a b) (f a b)) DESCRIBE will say that G takes two arguments of type T and returns a (signed-byte 8).
Is this something you can please shed some light on? Without argument type inference it's hard to imagine how efficient code is going to be reliably generated without explicit declarations.
Also, the "hot loop hypothesis" still tends to hold in CL; you only end up needing to annotate the crap out of the small number of functions that end up being most of your runtime. (It can sometimes be worth it to declare types for other functions, but that's as much for error checking as performance.)
Being able to set the compiler flags on a per-function level is also really helpful for this, since these hot functions can be compiled for run-time speed rather than safety, debuggability, or compile-time speed (once one's sure they're correct, of course!).
i maintain two big projects. i only ever annotate code after i know i am really done with it.
and also not every code. its a dynamic language after all.
its quite nice though that I can do it when I want and omit when not
The big difference between SBCL (and related implementations like CMUCL) compared to other Common Lisp implementations is that SBCL uses type declarations and type inference for compile time checks -> thus it can be less dynamic.
I don't really agree with that. It's hardly "too verbose to be useful" when a handful of type declarations can result in a significant runtime speed up.
In practice only a relatively small amount of code will need type declarations and optimizations enabled, and type declarations double as safety checks when using "(declaim (optimize (speed 0) (safety 3)))" instead of "(declaim (optimize (speed 3) (safety 0)))", so it's a win for more than just performance reasons.
It's not an optimization panacea, but it's low hanging fruit to get started.