lispcommon-lispclispgnu-common-lisp

Determining a supertype path


Given a variable with content 1, I know that it's a member of at least five types:

 1 (let* ((fred 1))
 2   (princ (typep fred 'bit)) (terpri)
 3   (princ (typep fred 'integer)) (terpri)
 4   (princ (typep fred 'fixnum)) (terpri)
 5   (princ (typep fred 'rational)) (terpri)
 6   (princ (typep fred t)) (terpri))
T
T
T
T
T

Two questions here.

  1. Given a variable with arbitrary content, how do I determine its supertype path?

  2. Where could I have found the answer for this in documentation?


Solution

  • Types

    You are asking about types, not classes. You cannot expect a good answer to your question because there are far more types which your number 1 belongs to than you really care about. E.g.,

    (typep 1 '(integer -6 42))
    ==> T
    

    If you limit your attention to the Standardized Atomic Type Specifiers, you can use something like

    (defconstant *Standardized-Atomic-Type-Specifiers* ...) ; see Figure 4-2
    (sort (remove-if-not (lambda (type) (typep 1 type)) 
                         *Standardized-Atomic-Type-Specifiers*)
          #'subtypep)
    ==> (BIT FIXNUM UNSIGNED-BYTE SIGNED-BYTE INTEGER RATIONAL REAL NUMBER ATOM T)
    

    Classes

    Now, if you are willing to restrict your interest to classes, the situation becomes much more manageable.

    First of all, CLOS supports multiple inheritance, so "supertype path" is not uniquely defined a priori.

    However, it has to be defined to determine the method precedence order, and this "path" is called class precedence list. It is computed by the standard MOP function compute-class-precedence-list:

    (compute-class-precedence-list (class-of 1))
    ==> (#<BUILT-IN-CLASS INTEGER> #<BUILT-IN-CLASS RATIONAL> #<BUILT-IN-CLASS REAL>
         #<BUILT-IN-CLASS NUMBER> #<BUILT-IN-CLASS T>)
    

    which is present in most Common Lisp implementations (use apropos or find-all-symbols to find which package it is exported from).

    You can use class-name to get the names of the classes instead of their metaobjects:

    (mapcar #'class-name (compute-class-precedence-list (class-of 1)))
    ==> (INTEGER RATIONAL REAL NUMBER T)
    

    Note that bit, fixnum, unsigned-byte, signed-byte, atom are not in this list because they do not name standard classes, just types.