type
(type typespec {var}*)
(typespec {var}*)
typespec—a type specifier.
Affects only variable bindings and specifies that the vars take on values only of the specified typespec. In particular, values assigned to the variables by setq, as well as the initial values of the vars must be of the specified typespec. type declarations never apply to function bindings (see ftype).
This is already said in the stuff on COMMON-LISP package. !!! Maybe add a cross-reference? -kmp 9-Feb-92 \issue{LISP-SYMBOL-REDEFINITION:MAR89-X3J13} Except where explicitly allowed, the consequences are undefined if a \term{symbol} in \thepackage{common-lisp} is used as a \param{var} argument. If such a \term{symbol} is not globally defined "by this standard" added per barmar: -kmp 28-Dec-90 by this standard as a variable or a constant, it is allowed to lexically bind it and to declare the {\tt type} of that \term{binding}. For example, the lexical variable names {\tt list} and {\tt car} are permitted. \editornote{KMP: This is unintelligible to me and needs to be rewritten to clarify that binding CL special variables is ok, but that their type decls are lexical.} \endissue{LISP-SYMBOL-REDEFINITION:MAR89-X3J13}
A type declaration of a symbol defined by symbol-macrolet is equivalent to wrapping a the expression around the expansion of that symbol, moved here from symbol-macrolet dictionary entry --sjl 5 mar 92 although the symbol's macro expansion is not actually affected.
The meaning of a type declaration is equivalent to changing each reference to a variable (var) within the scope of the declaration to (the typespec var), changing each expression assigned to the variable (new-value) within the scope of the declaration to (the typespec new-value), and executing (the typespec var) at the moment the scope of the declaration is entered.
A type declaration is valid in all declarations. The interpretation of a type declaration is as follows:
setq of the declared variable within the scope of the declaration, the consequences are "unspecified" -> "undefined" per barmar. i concur. -kmp
unspecifiedundefined if the newly assigned value of the declared variable is not of the declared type.
A type declaration affects only variable references within its scope. and the meaning of free and "variable-binding-associated" type declarations can be described identically.
If nested type declarations refer to the same variable, then the value of the variable must be a member of the intersection of the declared types.
If there is a local type declaration for a dynamic variable, and there is also a global type proclamation for that same variable, then the value of the variable within the scope of the local declaration must be a member of the intersection of the two declared types.
type declarations can be free declarations or bound declarations.
!!! This paragraph might be better off elsewhere, but can live here for now.A symbol cannot be both the name of a type and the name of a declaration. Defining a symbol as the name of a class, structure, condition, or type, when the symbol has been declared as a declaration name, or vice versa, signals an error.
The following was inserted after GLS checked this out.
Within the lexical scope of an array type declaration, all references to array elements are assumed to satisfy the expressed array element type (as opposed to the upgraded array element type). The consequences are undefined if this is ever violated.A compiler can treat the code within the scope of the array type declaration as if each access of an array element were surrounded by an appropriate the form.
(defun f (x y)
(declare (type fixnum x y))
(let ((z (+ x y)))
(declare (type fixnum z))
z)) → F
(f 1 2) → 3
;; The previous definition of F is equivalent to
(defun f (x y)
;; This declaration is a shorthand form of the TYPE declaration
(declare (fixnum x y))
;; To declare the type of a return value, it's not necessary to
;; create a named variable. A THE special form can be used instead.
(the fixnum (+ x y))) → F
(f 1 2) → 3
There were some comments on this code (from the Cleanup issue, probably) that no one liked, so I removed them. This example might do better with some explanation, but better no explanation than a broken one. -kmp 14-Feb-92
(defvar *one-array* (make-array 10 :element-type '(signed-byte 5)))
(defvar *another-array* (make-array 10 :element-type '(signed-byte 8)))
(defun frob (an-array)
(declare (type (array (signed-byte 5) 1) an-array))
(setf (aref an-array 1) 31)
(setf (aref an-array 2) 127)
(setf (aref an-array 3) (* 2 (aref an-array 3)))
(let ((foo 0))
(declare (type (signed-byte 5) foo))
(setf foo (aref an-array 0))))
(frob *one-array*)
(frob *another-array*)
The above definition of frob is equivalent to:
(defun frob (an-array)
(setf (the (signed-byte 5) (aref an-array 1)) 31)
(setf (the (signed-byte 5) (aref an-array 2)) 127)
(setf (the (signed-byte 5) (aref an-array 3))
(* 2 (the (signed-byte 5) (aref an-array 3))))
(let ((foo 0))
(declare (type (signed-byte 5) foo))
(setf foo (the (signed-byte 5) (aref an-array 0)))))
!!! Barmar: What does upgrading matter?Given an implementation in which fixnums are 29 bits but fixnum arrays are upgraded to signed 32-bit arrays, the following "should->could" per barmar. i concur. -kmp 27-Dec-90
shouldcould be compiled with all fixnum arithmetic:
(defun bump-counters (counters)
(declare (type (array fixnum *) bump-counters))
(dotimes (i (length counters))
(incf (aref counters i))))
(typespec {var}*) is an abbreviation for (type typespec {var}*). provided that \param{typespec} is one of the standard \term{type specifier}
\term{symbols} in \figref\StandardizedAtomicTypeSpecs.
A type declaration for the arguments to a function does not necessarily imply anything about the type of the result. The following function is not permitted to be compiled using implementation-dependent fixnum-only arithmetic:
(defun f (x y) (declare (fixnum x y)) (+ x y))
To see why, consider (f most-positive-fixnum 1). Common Lisp defines that F must return a bignum here, rather than signal an error or produce a mathematically incorrect result. If you have special knowledge such “fixnum overflow” cases will not come up, you can declare the result value to be in the fixnum range, enabling some compilers to use more efficient arithmetic:
(defun f (x y) (declare (fixnum x y)) (the fixnum (+ x y)))
Note, however, that in the three-argument case, because of the possibility of an implicit intermediate value growing too large, the following will not cause implementation-dependent fixnum-only arithmetic to be used:
(defun f (x y) (declare (fixnum x y z)) (the fixnum (+ x y z)))
To see why, consider (f most-positive-fixnum 1 -1). Although the arguments and the result are all fixnums, an intermediate value is not a fixnum. If it is important that implementation-dependent fixnum-only arithmetic be selected in implementations that provide it, consider writing something like this instead:
(defun f (x y) (declare (fixnum x y z)) (the fixnum (+ (the fixnum (+ x y)) z)))