[comp.lang.clos] Procedural attachment

berger@tartarus.uchicago.edu (Jeff Berger) (06/12/91)

The June issue of AI Expert contains an article titled "Frames in
CLOS", by J. Veitch. One of the topics covered is how to define CLOS
classes so that slot values for CLOS instances can be retrieved from a
data base as needed rather than being actually present in memory.
That's close enough to a problem I'm trying to solve that I thought I
could easily adapt the code to my purposes.

I want calls of the form (slot-value instance201ofclass3 'slot2) to
return (slot-value instance17ofclass1 'slot4). The two classes have
minimally overlapping slots. I want to be sure that changes in value
for a quantity stored in the instance of one class are automatically
reflected in the value of the same quantity in another instance of a
different class. While I'm a beginner at CLOS, the code fragments
given in the article, although marred by typo's and minor bugs, (AI
Expert needs a good proof-reader) seemed clear enough to convince me I
could make an easy adaptation. 

The trick is to write methods for SLOT-VALUE-USING-CLASS which apply
to a user-defined metaclass. To wit:

#|

  FILENAME: ORGAN-TOLERANCE-CLASS.CL

  Code for the definition of the class ORGAN-TOLERANCE. One slot in 
  the class is read from an ASSUMPTION class object.

|#

(defclass META-ORGAN-TOLERANCE (clos:standard-class)
	  ())  ; The user-defined metaclass


(defclass ORGAN-TOLERANCE () ; instances of this class should get the
                             ; value for the LIMIT slot from the VALUE
                             ; slot a (particular) instance of the ASSUMPTION
                             ; class (defined elsewhere).         
	  ((organ :initarg :organ :accessor :organ)
	   (limit :reader :limit :allocation :assumption)
	   (fraction :accessor :fraction :initarg :fraction :initform 1.0))
	  (:metaclass meta-organ-tolerance)) 


(defmethod SLOT-VALUE-USING-CLASS
    ((class meta-organ-tolerance) instance slot-name)
  (let ((slot (do ((x (clos:class-slots class) (cdr x)))
		  ((or (null x)
		       (eq slot-name (clos:slot-definition-name (car x))))
		   (car x)))))
    (if (and slot
             (eq (clos:slot-definition-allocation slot) :assumption))
	(get-tolerance-from-assumption instance)
      (call-next-method))))



(defun GET-TOLERANCE-FROM-ASSUMPTION ; This pulls the desired value
                                     ; from an instance of the ASSUMPTION
                                     ; class retrieved by
                                     ; GET-CURRENT-ASSUMPTION.
    (organtolerance
     &aux (organ (slot-value organtolerance :organ)) ; gets a symbol
	  (tname (symbol-catenate organ 'tolerance)) ; catenates the symbols
	  (assumption (get-current-assumption tname)))
  (if assumption
      (slot-value assumption 'value)
    nil))


According to the article, making the allocation of the LIMIT slot an
option unknown to CLOS (i.e., :ASSUMPTION) will suppress slot allocation.
Unfortunately, when I attempt to create an instance of the class
ORGANTOLERANCE, I get an error. 

<cl> (SETQ ILUNGT
       (MAKE-INSTANCE 'ORGAN-TOLERANCE :ORGAN 'IPSILATERAL-LUNG :FRACTION 0.9))
Error: The slot LIMIT is missing from the object #<ORGAN-TOLERANCE @ #x1990766>
 of class #<META-ORGAN-TOLERANCE ORGAN-TOLERANCE @ #x189637e>

Looking at the execution stack, it seems that SHARED-INITIALIZE is
checking whether or not the slots of the object it is initializing are
unbound and is discovering that the slot is missing. I tried writing a
method to kludge over the situation:

(defmethod SLOT-BOUNDP-USING-CLASS
    ((class organ-tolerance) object slot-name)
  (or (eq 'limit slot-name)
      (call-next-method)))

But the method is never found. Here's part of the stack:

Evaluation stack:

 ->call to ERROR
required arg: EXCL::DATUM = "The slot ~S is missing from the object ~S of class ~S"
&rest EXCL::ARGUMENTS = (LIMIT #<ORGAN-TOLERANCE @ #x1990766> #<META-ORGAN-TOLERANCE ORGAN-TOLERANCE @ #x189637e>)

   call to (CLOS:METHOD SLOT-MISSING (T T T ...))
required arg: CLOS:CLASS = #<META-ORGAN-TOLERANCE ORGAN-TOLERANCE @ #x189637e>
required arg: CLOS::OBJECT = #<ORGAN-TOLERANCE @ #x1990766>
required arg: CLOS::SLOT-NAME = LIMIT
required arg: CLOS::OPERATION = SLOT-BOUNDP
optional arg: CLOS::NEW-VALUE = 0.9

   call to CLOS::SLOT-BOUNDP--STANDARD-CLASS
required arg: CLOS::OBJECT = #<ORGAN-TOLERANCE @ #x1990766>
required arg: CLOS::SLOT-NAME = LIMIT

   call to (CLOS:METHOD CLOS:SLOT-BOUNDP-USING-CLASS (CLOS:STANDARD-CLASS CLOS:STANDARD-OBJECT T))
required arg: CLOS:CLASS = #<META-ORGAN-TOLERANCE ORGAN-TOLERANCE @ #x1a935b6>
required arg: CLOS::OBJECT = #<ORGAN-TOLERANCE @ #x1a932de>
required arg: CLOS::SLOT-NAME = LIMIT

   call to (CLOS:METHOD SLOT-BOUNDP (CLOS:STANDARD-OBJECT T))
required arg: CLOS::INSTANCE = #<ORGAN-TOLERANCE @ #x1a932de>
required arg: CLOS::SLOT-NAME = LIMIT

   call to (CLOS:METHOD SHARED-INITIALIZE (CLOS:STANDARD-OBJECT T))
required arg: CLOS::INSTANCE = #<ORGAN-TOLERANCE @ #x1a932de>
required arg: CLOS::SLOT-NAMES = T
&rest CLOS::INITARGS = (:ORGAN IPSILATERAL-LUNG :FRACTION 0.9)


I know, this example exhibits lots of quick and dirty hacks, BUT
besides that, what am I doing wrong?

P.S. To Mr. Veitch (if he happens to see this). I probably should have
written to you at AI EXPERT about this. But I crave instant answers.
--
Jeff Berger			|USmail:	Ryerson 256
berger@tartarus.uchicago.edu	|		1100 East 58th Street
(312) 702-8584			|		Chicago, IL  60637

jim@Franz.COM (Jim Veitch) (06/14/91)

Jeff Berger writes:

> The June issue of AI Expert contains an article titled "Frames in
> CLOS", by J. Veitch. One of the topics covered is how to define CLOS
> classes so that slot values for CLOS instances can be retrieved from a
> data base as needed rather than being actually present in memory.
> That's close enough to a problem I'm trying to solve that I thought I
> could easily adapt the code to my purposes.

  [ ... code examples elided ... ]

> According to the article, making the allocation of the LIMIT slot an
> option unknown to CLOS (i.e., :ASSUMPTION) will suppress slot allocation.
> Unfortunately, when I attempt to create an instance of the class
> ORGANTOLERANCE, I get an error.

Your code has exposed a bug in the CLOS MOP in ACL 4.0.

The slot 'limit' in class ORGAN-TOLERANCE has indeed had its'
allocation suppressed.  Now when 'make-instance' gets called,
'shared-initialize' gets invoked (as your backtrace shows) and this is
where the bug is.  'shared-initialize' has a bug which assumes all
slots are allocated.  I will be sending you a patch direct under
separate cover.  This problem is fixed in the upcoming summer release
of ACL 4.1.

Please let me know if this fixes your problem.  Also feel free to
write direct to bugs@franz.com for support of this nature; if you are
paying for it you should use it!