Determining a supertype path
Asked Answered
U

2

5

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?

Uphemia answered 13/10, 2013 at 13:21 Comment(8)
What you are trying to do is also known as linearization. However, CLOS preceded this invention. It was probably first planned for Dylan, but it really only exists in Python or (maybe Perl?). AFAIK, CLOS never enforced any particular linearization rule, so you probably won't be able to find a path, rather a set of all superclasses (i.e. it won't be ordered).Chosen
@wvxvw: yes it does! otherwise the applicable methods would be called in an arbitrary order. See my answer for references.Ulcerative
@Ulcerative I feel uncomfortable reading this: "All methods on this generic function must compute the class precedence list as a function of the ordered direct superclasses of the superclasses of class. The results are undefined if the rules used to compute the class precedence list depend on any other factors." What is "ordered direct superclasses"? How are they supposed to be ordered? I'm more afraid that it might be implementation-defined, and not defined in MOP.Chosen
@wvxvw: The order is well defined in the ANSI CL standardUlcerative
@Ulcerative sorry to be a bore, but I can't find where it says how to resolve conflicts (and it uses a lot of ASCII art instead of words). Conflicts as in, for example, how to treat common superclasses inherited through different branches. It mentions that "R" (which is probably a relation over superclasses with inherit-from being this relation's operation, but maybe it's something else - the doc. doesn't say it) can be "it is assumed that they are consistent and that R generates a partial ordering". At the same time it says nothing about what happens when they are inconsistent.Chosen
@wvxvw: I agree that the standard can be opaque at times, but the bottom line is that it does specify the behavior uniquely. If you would like it to be clarified, these comments are not the proper forum - please ask a separate question.Ulcerative
@Ulcerative I think there are two things at once: 1. It, like, for example, C++ does not exhaustively specify how inheritance must be resolved (i.e. when it is inconsistent, the implementation may treat it whichever way it likes). Linearisation is the mechanism to address this issue (why else would it be invented?). 2. is the unintelligible language of the CLHS, which makes the first issue difficult to discover.Chosen
@Ulcerative Ah, sorry, nevermind, it does say it is an error, here: lispworks.com/documentation/HyperSpec/Body/04_ceb.htm when it discusses the case when classes cannot be ordered. But this would really take awhile to find...Chosen
U
12

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.

Ulcerative answered 13/10, 2013 at 18:0 Comment(0)
E
2

That's not easy, IIRC.

I would use CLOS in some way:

CL-USER 34 > (loop for c = (class-of 1)
                   then (first (class-direct-superclasses c))
                   collect (class-name c)
                   until (eq c (find-class t)))
(FIXNUM INTEGER RATIONAL REAL NUMBER T)

Note that you need CLASS-DIRECT-SUPERCLASSES which is provided by CLOS implementations.

Equimolecular answered 13/10, 2013 at 14:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.