[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Name That Class!



Before I left on my ill-fated trip to Europe (where I confronted face-to-face,
once more, my muse) I wrote out some examples of how class objects and their
names interacted. The idea was to explore what it meant to build a hierarchy
with names (using DEFLCASS) and with class objects (blasting fields). I think
these examples are right (I could be wrong) and they might strike some as
odd, they are generally in accord  with acceptable behavior of functions and
the symbols that name them (and DEFSTRUCT names and types as well). I send them
in hopes they might help stimulate thought on the issues:

1. (defclass foo ...)

   (let ((a (class-named 'foo)))
    (defclass foo ...)
    (eq a (class-named 'foo))) => T

This has been agreed to be the best sufficient way to achieve the
old-methods-still-work behavior.

2. (defclass foo ...)

  (let ((c (make-instance 'standard-class)))
   (setf (<various aspects> c) <certain values>) 
   (setf (class-named 'foo) c) 
   (eq (class-named 'foo) c)) => ?

MAKE-INSTANCE is not psychic, so it must cons.  It seems that (DEFLCASS FOO ...)
is equivalent to

   (let ((c (make-instance 'standard-class)))
    (setf (<various aspects> c) <certain values>)
    (setf (class-named 'foo) c)
    c)

Note that there is no similar expression in CLtL to which DEFTSRUCT is
equivalent.  Therefore the EQ must return NIL. This implies that (setf
(class-named ...) ...)  is smart and worries about the EQness between the
previous class, if it exists, and the new one.

3. (let ((c (make-instance 'standard-class)))
    (setf (<various aspects> c) <certain values>)
    (setf (class-named 'foo) c) ;foo otherwise undefined
    (eq (class-named 'foo) c)) => ?

Why should (setf (class-named ...) ...) unnecessarily cons? Therefore
the EQ should return T.

4. (defclass foo ...)
   (defclass baz ...)
 
   (let ((c1 (class-named 'foo))
	 (c2 (class-named 'baz)))
    (setf (class-named 'baz)  (class-named 'foo))
    ;;; (eq c1 c2) => NIL
    (eq (class-named 'foo) (class-named 'baz))) => ?

Because (eq c1 (class-named 'foo)) => T, (eq c1 c2) => NIL, and (eq c2
(class-named 'baz)) => T, this must return NIL.

5. (defclass foo ...)
   (defmethod f ((x foo)) ...) ;no other methods
   (defclass baz ...)
   (defmethod g ((x baz)) ...) ;no other methods
   (setq instance1 (make-instance 'foo))
   (setq instance2 (make-instance 'baz))
   (rotatef (class-named 'foo) (class-named 'baz))
   (f instance1) => well-defined?
   (f instance2) => well-defined?
   (g instance1) => well-defined?
   (g instance2) => well-defined?

The first generic function invocation is well-defined; the second is an
error of some sort; the third is an error of some sort; the fourth is
well-defined.

6. (defclass c1 (c2 c3) ...)
   (defclass c2 ...)
   (defclass c3 ...)
   (defmethod f ((x c2)) ...) ;no other methods
   (setq instance1 (make-instance 'c1))
   ;;; Flush the superclass link from C1 to C2
   (setf (class-super-classes (class-named 'c1)) `(,(class-named 'c3)))
   (f instance1) => well-defined?

I don't know what this should do, but I suspect the expression (f instance1)
is not well-defined because the SETF alters the topology of the graph.

It seems that once a name is given to a class, the symbol that is the
name and the storage for that class are linked forever unless something
like

	(setf (class-named 'foo) nil)

is defined to work.

It is also clear that the the applicability of methods within a class
graph depends on the topology of the graph and not on the substance of the
classes in that graph. If a user were to mistakenly exchange the names of
two classes in a graph at the outset, he would not be able to correct that
naming error using CLASS-NAMED and (SETF (CLASS-NAMED ...) ...) - example
4 shows this. He would have to alter the superclass slots in the classes
surrounding the ones he wishes to switch, according to the technique in 
example 6, assuming that would even work. 

Enjoy!

			-rpg-