Perhaps the most important predicates in Lisp are those that deal with data types; that is, given a data object one can determine whether or not it belongs to a given type, or one can compare two type specifiers.
If a data type is viewed as the set of all objects belonging to the type, then the typep function is a set membership test, while subtypep is a subset test.
typep is a predicate that is true if object is of type type, and is false otherwise. Note that an object can be “of” more than one type, since one type can include another. The type may be any of the type specifiers mentioned in chapter 4 except that it may not be or contain a type specifier list whose first element is function or values. A specifier of the form (satisfies fn) is handled simply by applying the function fn to object (see funcall); the object is considered to be of the specified type if the result is not nil.
X3J13 voted in January 1989 to change typep to give specialized array and complex type specifiers the same meaning for purposes of type discrimination as they have for declaration purposes. Of course, this also applies to such type specifiers as vector and simple-array (see section 4.5). Thus
in the first edition asked the question, Is foo an array specialized to hold bignums? but under the new interpretation asks the question, Could the array foo have resulted from giving bignum as the :element-type argument to make-array?
The arguments must be type specifiers that are acceptable to typep. The two type specifiers are compared; this predicate is true if type1 is definitely a (not necessarily proper) subtype of type2. If the result is nil, however, then type1 may or may not be a subtype of type2 (sometimes it is impossible to tell, especially when satisfies type specifiers are involved). A second returned value indicates the certainty of the result; if it is true, then the first value is an accurate indication of the subtype relationship. Thus there are three possible result combinations:
X3J13 voted in January 1989 to place certain requirements upon the implementation of subtypep, for it noted that implementations in many cases simply “give up” and return the two values nil and nil when in fact it would have been possible to determine the relationship between the given types. The requirements are as follows, where it is understood that a type specifier s involves a type specifier u if either s contains an occurrence of u directly or s contains a type specifier w defined by deftype whose expansion involves u.
In addition, X3J13 voted to clarify that in some cases the relationships between types as reflected by subtypep may be implementation-specific. For example, in an implementation supporting only one type of floating-point number, (subtypep ’float ’long-float) would return t and t, since the two types would be identical.
Note that satisfies is an exception because relationships between types involving satisfies are undecidable in general, but (as X3J13 noted) and, or, not, and member are merely very messy to deal with. In all likelihood these will not be addressed unless and until someone is willing to write a careful specification that covers all the cases for the processing of these type specifiers by subtypep. The requirements stated above were easy to state and probably suffice for most cases of interest.
X3J13 voted in January 1989 to change subtypep to give specialized array and complex type specifiers the same meaning for purposes of type discrimination as they have for declaration purposes. Of course, this also applies to such type specifiers as vector and simple-array (see section 4.5).
If A and B are type specifiers (other than *, which technically is not a type specifier anyway), then (array A) and (array B) represent the same type in a given implementation if and only if they denote arrays of the same specialized representation in that implementation; otherwise they are disjoint. To put it another way, they represent the same type if and only if (upgraded-array-element-type ’A) and (upgraded-array-element-type ’B) are the same type. Therefore
is true if and only if (upgraded-array-element-type ’A) is the same type as (upgraded-array-element-type ’B).
The complex type specifier is treated in a similar but subtly different manner. If A and B are two type specifiers (but not *, which technically is not a type specifier anyway), then (complex A) and (complex B) represent the same type in a given implementation if and only if they refer to complex numbers of the same specialized representation in that implementation; otherwise they are disjoint. Note, however, that there is no function called make-complex that allows one to specify a particular element type (then to be upgraded); instead, one must describe specialized complex numbers in terms of the actual types of the parts from which they were constructed. There is no number of type (or rather, representation) float as such; there are only numbers of type single-float, numbers of type double-float, and so on. Therefore we want (complex single-float) to be a subtype of (complex float).
The rule, then, is that (complex A) and (complex B) represent the same type (and otherwise are disjoint) in a given implementation if and only if either the type A is a subtype of B, or (upgraded-complex-part-type ’A) and (upgraded-complex-part-type ’B) are the same type. In the latter case (complex A) and (complex B) in fact refer to the same specialized representation. Therefore
is true if and only if the results of (upgraded-complex-part-type ’A) and (upgraded-complex-part-type ’B) are the same type.
Under this interpretation
must be true in all implementations; but
is true only in implementations that do not have a specialized array representation for single-float elements distinct from that for float elements in general.
The following predicates test for individual data types.
null is true if its argument is (), and otherwise is false. This is the same operation performed by the function not; however, not is normally used to invert a Boolean value, whereas null is normally used to test for an empty list. The programmer can therefore express intent by the choice of function name.
symbolp is true if its argument is a symbol, and otherwise is false.
The predicate atom is true if its argument is not a cons, and otherwise is false. Note that (atom ’()) is true, because () ≡nil.
The predicate consp is true if its argument is a cons, and otherwise is false. Note that the empty list is not a cons, so (consp ’()) ≡ (consp ’nil) ⇒ nil.
listp is true if its argument is a cons or the empty list (), and otherwise is false. It does not check for whether the list is a “true list” (one terminated by nil) or a “dotted list” (one terminated by a non-null atom).
numberp is true if its argument is any kind of number, and otherwise is false.
integerp is true if its argument is an integer, and otherwise is false.
rationalp is true if its argument is a rational number (a ratio or an integer), and otherwise is false.
floatp is true if its argument is a floating-point number, and otherwise is false.
complexp is true if its argument is a complex number, and otherwise is false.
characterp is true if its argument is a character, and otherwise is false.
stringp is true if its argument is a string, and otherwise is false.
bit-vector-p is true if its argument is a bit-vector, and otherwise is false.
vectorp is true if its argument is a vector, and otherwise is false.
vectorp is true if its argument is a simple general vector, and otherwise is false.
simple-string-p is true if its argument is a simple string, and otherwise is false.
simple-bit-vector-p is true if its argument is a simple bit-vector, and otherwise is false.
packagep is true if its argument is a package, and otherwise is false.
functionp is true if its argument is suitable for applying to arguments, using for example the funcall or apply function. Otherwise functionp is false.
functionp is always true of symbols, lists whose car is the symbol lambda, any value returned by the function special operator, and any values returned by the function compile when the first argument is nil.
Because the vote also specifies that types cons and symbol are disjoint from the type function, this is an incompatible change; now functionp is in fact always false of symbols and lists.
compiled-function-p is true if its argument is any compiled code object, and otherwise is false.
See also standard-char-p, string-char-p, streamp, random-state-p, readtablep, hash-table-p, and pathnamep.