Common Lisp the Language, 2nd Edition
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.
[Function]
typep object type
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
(ARRAY-TYPE-ELEMENT-TYPE-SEMANTICS)
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
(typep foo '(array bignum))
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?
[Function]
subtypep type1 type2
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:
t t type1 is definitely a subtype of type2 nil t type1 is definitely not a subtype of type2 nil nil subtypep could not determine the relationship
X3J13 voted in January 1989
(SUBTYPEP-TOO-VAGUE)
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.
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 (ARRAY-TYPE-ELEMENT-TYPE-SEMANTICS) 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
(subtypep '(array A) '(array B))
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
(subtypep '(complex A) '(complex B))
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
(subtypep '(complex single-float) '(complex float))
must be true in all implementations; but
(subtypep '(array single-float) '(array float))
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.