[comp.lang.clos] accessing clos objects

mikeb@wdl35.wdl.loral.com (Michael H Bender) (02/12/91)

I've just started learning and using CLOS and I have
encountered a problem I don't know how to solve. I
hope that one of you wizards out there can help me.

==> After I have created a class and I have instantiated
    it with a number of objects, is there any function
    I can use to access these objects later on without 
    refering to them by name. 
    In other words, is there any easy way to access all 
    the objects that belong to a given class?

I apologize if this question is trivial (I hope it is
clear) and thanks in advance for the answers.

Mike Bender
   
    

egdorf@ZAPHOD.LANL.GOV (Skip Egdorf) (02/12/91)

>   ==> After I have created a class and I have instantiated
>       it with a number of objects, is there any function
>       I can use to access these objects later on without
>       refering to them by name.
>       In other words, is there any easy way to access all
>       the objects that belong to a given class?
>
>   Mike Bender

   Ummm... "without refering to them by name???" Do you really mean
"without keeping track of the instance object returned by
(make-instance ...) ?"

   CLOS does not currently name its instances. I believe that this is
because CLOS instances are supposed to be very lightweight
so that they can be used for things like intermediate representations
in new arithmetic systems.

   It is relatively easy to use the meta object protocol to add
functionality to a class such that it maintains a list of all instances,
and/or provides named instances. One adds a slot to each class object that
maintains a name-instance mapping. A global symbol table is nice
to allow global instance lookup by name.

   I have implemented such a metaclass using the accessors for
class name-object mapping as a model. This scheme allows
(class-direct-instances class), (instance-name object), (find-instance obect),
and (setf (instance-name object) ...) on any instances of classes of this
new metaclass.

So, to answer your question: No, there is no such function, but it is
easy to implement yourself for those classes where you need to access
all instances. It is an indication of some sort of success for the
CLOS design that it both provides for the notion of very lightweight instances,
and also gives the ability to easily extend the basis where needed.

					Skip Egdorf
					hwe@lanl.gov

barmar@think.com (Barry Margolin) (02/12/91)

In article <MIKEB.91Feb11101840@wdl35.wdl.loral.com> mikeb@wdl35.wdl.loral.com (Michael H Bender) writes:
>    In other words, is there any easy way to access all 
>    the objects that belong to a given class?

There is no automatic mechanism that keeps track of all the instances of a
class.  You may implement such a mechanism for classes you define, though.
You can have a per-class slot (a slot with the (:ALLOCATION :CLASS) option)
that contains a list of all its instances, and define a :BEFORE or :AFTER
method on MAKE-INSTANCE that adds the new instance to the list.  You might
also want to define a REMOVE-INSTANCE method that would remove the
specified instance from the list (otherwise, instances will never get
GCed).

Also, several Common Lisp implementations (Lucid and Symbolics for sure,
and probably also Franz and Allegro) include an extension called
"resources", which maintain caches of recently created, similar objects.
They generally allow the program to list all the objects in a resource, and
automatically delete references to unused objects when there are lots of
them (to allow them to be GCed).  I don't know whether this would be useful
to your application, but you might consider using it if your implementation
provides it (and if complete portability isn't an issue).

--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

lgm@cbnewsc.ATT.COM (lawrence.g.mayka) (02/12/91)

In article <1991Feb11.223240.20960@Think.COM> barmar@think.com (Barry Margolin) writes:
   method on MAKE-INSTANCE that adds the new instance to the list.  You might
   also want to define a REMOVE-INSTANCE method that would remove the
   specified instance from the list (otherwise, instances will never get
   GCed).

Some Common Lisp vendors (e.g., Symbolics) offer some form of "weak
reference" - a way to refer to an object without protecting it from
GC.  Using weak references to keep track of extant instances avoids
the need for explicit de-allocation.


	Lawrence G. Mayka
	AT&T Bell Laboratories
	lgm@iexist.att.com

Standard disclaimer.

john@LINUS.MITRE.ORG (02/13/91)

From: barmar@think.com  (Barry Margolin)
Organization: Thinking Machines Corporation, Cambridge MA, USA
Subject: Re: accessing clos objects
Message-Id: <1991Feb11.223240.20960@Think.COM>
References: <MIKEB.91Feb11101840@wdl35.wdl.loral.com>
To: commonloops@cis.ohio-state.edu

barmar@think.com (Barry Margolin) writes:

  There is no automatic mechanism that keeps track of all the
  instances of a class.  You may implement such a mechanism for
  classes you define, though.  You can have a per-class slot (a slot
  with the (:ALLOCATION :CLASS) option) that contains a list of all
  its instances, and define a :BEFORE or :AFTER method on
  MAKE-INSTANCE that adds the new instance to the list.

It's not clear how one would do this with :BEFORE and/or :AFTER
methods.  An ordinary method specialized on the class in question
would never get called, since MAKE-INSTANCE gets called on symbols and
classes.  So, presumably the idea is to define a method on
MAKE-INSTANCE that is EQL specialized to the class itself, or the name
of the class.  But a :BEFORE or :AFTER method isn't going to be able
to get access to the new instance, only to the class.  One would have
to use, for instance, an :AROUND or a primary method and use
CALL-NEXT-METHOD.  Thus:

  (DEFCLASS FLINTSTONE ()
      ((INSTANCES :ALLOCATION :CLASS
                  :INITFORM '()
                  :ACCESSOR FLINTSTONE-INSTANCES)
       (NAME :ACCESSOR FLINTSTONE-NAME)))

  (DEFMETHOD MAKE-INSTANCE ((F-CLASS (EQL (FIND-CLASS 'FLINTSTONE)))
                            &REST INITARGS)
    (LET ((NEW-FLINTSTONE (CALL-NEXT-METHOD)))
      (PUSH NEW-FLINTSTONE (FLINTSTONE-INSTANCES NEW-FLINTSTONE))
      NEW-FLINTSTONE))

In general, I believe that EQL methods are fairly lousy with respect
to performance.  Alternatively, on ecould define the recording
behavior on SHARED-INITIALIZE, or something like that.  Defining a new
metaclass is probably a better solution, certainly aesthetically
speaking, although it makes it a little trickier to have the above
behavior be inherited.

John Burger

kanderso@BBN.COM (02/13/91)

  It's not clear how one would do this with :BEFORE and/or :AFTER
  methods.  An ordinary method specialized on the class in question
  would never get called, since MAKE-INSTANCE gets called on symbols and
  classes.  So, presumably the idea is to define a method on
  MAKE-INSTANCE that is EQL specialized to the class itself, or the name
  of the class.  But a :BEFORE or :AFTER method isn't going to be able
  to get access to the new instance, only to the class.  One would have
  to use, for instance, an :AROUND or a primary method and use
  CALL-NEXT-METHOD.  Thus:

    (DEFCLASS FLINTSTONE ()
        ((INSTANCES :ALLOCATION :CLASS
                    :INITFORM '()
                    :ACCESSOR FLINTSTONE-INSTANCES)
         (NAME :ACCESSOR FLINTSTONE-NAME)))

    (DEFMETHOD MAKE-INSTANCE ((F-CLASS (EQL (FIND-CLASS 'FLINTSTONE)))
                              &REST INITARGS)
      (LET ((NEW-FLINTSTONE (CALL-NEXT-METHOD)))
        (PUSH NEW-FLINTSTONE (FLINTSTONE-INSTANCES NEW-FLINTSTONE))
        NEW-FLINTSTONE))

  In general, I believe that EQL methods are fairly lousy with respect
  to performance.  Alternatively, on ecould define the recording
  behavior on SHARED-INITIALIZE, or something like that.  Defining a new
  metaclass is probably a better solution, certainly aesthetically
  speaking, although it makes it a little trickier to have the above
  behavior be inherited.

Here's an example of doing it each way.  It once ran in PCL.

;;;-*-Mode:LISP; Package:(PCL LISP 1000); Base:10; Syntax:Common-lisp -*-

(defclass named-mixin
	  ()
     ((name :initarg :name :initform NIL :reader name)))

(defmethod print-object ((thing named-mixin) stream)
  (printing-random-thing (thing stream)
   (format stream "~a ~a" (class-name (class-of thing)) (name thing))))


(defclass compatible-class-mixin
    ()
  ()
  (:documentation
   "A metaclass mixin that provides compatibility with standard-class."))

(defmethod check-super-metaclass-compatibility
	   ((class compatible-class-mixin) (super standard-class))
  t)

(defmethod describe ((thing compatible-class-mixin) &rest args)
  (apply 'describe-instance thing args))


(defclass instances-class-mixin
	  ()
     ((instances :initform () :accessor class-instances))
  (:documentation
    "Lets a class record its instances."))

(defmethod make-instance ((class instances-class-mixin) &rest initargs)
  (declare (ignore initargs))
  (let ((instance (call-next-method)))
    (add-instance class instance)
    instance))

(defmethod add-instance ((class instances-class-mixin) instance)
  (pushnew instance (class-instances class)))

(defmethod remove-instance ((class instances-class-mixin) instance)
  (setf (class-instances class)
	(delete instance (class-instances class) :test #'eq)))


;;; This version uses :class allocated slots, so each instance knows
;;; who its siblings are but the class doesn't know who its instances
;;; are.

(defclass i-mixin
	  ()
     ((instances :initform () :accessor instances :allocation :class)))

(defmethod *initialize-instance :before ((object i-mixin) &rest initargs)
	   (pushnew object (instances object)))

(defclass foo-x
	  (i-mixin)
     ((x)
      (y)))

(defclass foo-y
	  (i-mixin)
     ((z)
      (y)))

#||
;;; Example.
(defclass instance-recording-class
	  (instances-class-mixin
	    compatible-class-mixin
	    standard-class)
     ())

(defclass ifrob ()
     ((x :initarg x :accessor x)
      (y :initarg y :accessor y))
  (:metaclass instance-recording-class))

(defclass jfrob (ifrob)
     ((z :initarg x :accessor z))
  (:metaclass instance-recording-class))

(setq x (make-instance 'ifrob))
(setq y (make-instance 'jfrob))
(class-instances (find-class 'ifrob))

||#

jonl@LUCID.COM (Jon L White) (02/13/91)

I would think that whether you "record" the instances in a global
list or in a shared slot is independent of whether you use the
:metaclass/MAKE-INSTANCE approach, or the more plain
mixin-class/INITIALIZE-INSTANCE approach.   For example, the
the :metaclass approach could provide the "class" slot simply by
having a "maximal"class in that particular metaclass (just as
STANDARD-OBJECT is the "maximal" class in STANDARD-CLASS) and this
class has the direct "class" slot; but the vanilla mixin-class
approach would just have the inherited mixin provide the class slot.

On the other hand, with many implementations, there may be a
performance penalty to pay if you use the approach that overrides
the MAKE-INSTANCE method on STANDARD-CLASS.  In your example, you
have a method on INSTANCE-RECORDING-CLASS that "overrides" the
one on STANDARD-CLASS. [Note: recent metaboject proposals define
"extends" and "overrides" in more technical terms, and in that
terminology, "extends" is the corect word; but without requiring
every reader of this list to read these documents, I think "overrides"
is more intuitively clear.  As the distinction between "extends" and
"overrides" becomes generally known, I believe it will be a better
set of terms; but for right now . . . ]

I will now provide examples of doing it both ways; partly to show
how simple the "vanilla" approach is, and partly to show how to
create metaclasses with "maximal" elements (at least in Lucid's 4.0
release).  The first approach is pure, standard CLOS; the second, of
course, touches on the not-yet-standard metaobject protocols, and
parts will no doubt change as time goes by.


Here is an an example of the more vanilla approach, putting the
enumeration listing in *two* places:

> (defclass enumeration-mixin ()
    ((instances :initform nil :allocation :class :accessor class-instances)))
#<Standard-Class ENUMERATION-MIXIN>
> (setq *enumerations-map* nil)
NIL
> (defmethod initialize-instance :after ((x enumeration-mixin) &rest w)
    (declare (ignore w))
    (let* ((direct-class (class-of x))
           (entry (or (assq direct-class *enumerations-map*)
                      (car (push (cons direct-class nil)
                                 *enumerations-map*)))))
      (push x (cdr entry))				;in global place
      (push x (class-instances x))			;in shared slot
      ))
#<Init-Method INITIALIZE-INSTANCE :AFTER (ENUMERATION-MIXIN)>
> (defclass enumerated-class-1 (enumeration-mixin	;could have many more
							; superclasses here.
                                )
     ((z-axis :initform nil :accessor z :documentation "What the Z?")))
#<Standard-Class ENUMERATED-CLASS-1>
> (make-instance 'enumerated-class-1)
#<Enumerated-Class-1 #X937083>
> (setq x (make-instance 'enumerated-class-1))
#<Enumerated-Class-1 #X93A19B>
> *enumerations-map*
((#<Standard-Class ENUMERATED-CLASS-1> #<Enumerated-Class-1 #X93A19B> #<Enumerated-Class-1 #X937083>))
> (class-instances x)
(#<Enumerated-Class-1 #X93A19B> #<Enumerated-Class-1 #X937083>)
>



Now, here's an example of the metaclass approach.  Note the use of
CLOS-SYSTEM:VALIDATE-SUPERCLASS instead of PCL's
CHECK-SUPERCLASS-COMPATIBILITY.  Likely the VALIDATE-SUPERCLASS
terminology will become a standard.  However, I'm not sure whether or
not a technique for the creation of a "maximal" element of a metaclass
is part of the emerging proposals.  In the code below, this his how
you would do it in the patched Lucid 4.0 release; in particular, note
the circular dependencies between this "maximal" element and the
metaclass, and how the use of CHANGE-CLASS fixes up the problem.  In
the current Symbolics release, it might be that a maximal element is
supported by some additional metaobject generic function; but in Lucid's
release, it is determined by the default value form for the class slot
CLOS::DIRECT-SUPERCLASSES [note well,  this is an instance slot in class
objects, and not a shared slot; it is accessed by the function from the
"de-facto" standards proposal CLOS:CLASS-DIRECT-SUPERCLASSES.]

In the code below, all use of CLOS::... symbols is Lucid-speicifc; and
all uses of CLOS-SYSTEM:... symbols is Lucid specific *except* for the
one CLOS-SYSTEM:VALIDATE-SUPERCLASS -- I believe this one will be in
the standards proposals, although at the time of Lucid's release, it was
still being debated.  All uses of CLOS:... symbols are from the set of
three or four dozen functions that Symbolics and Lucid (and possibly
others?) subscribed to last year as a "de-facto metaobject standard",
meaning that any future standard would surely include these functions
(which were almost entirely "introspective")


;;; First, start this guy out as a plain old 'standard-class' class.
> (defclass enumerated-object ()
    ((instances :initform nil :allocation :class :accessor class-instances)))
#<Standard-Class ENUMERATED-OBJECT>
> (defclass enumerated-class (standard-class)
    ((clos::direct-superclasses
      :initform (list (find-class 'enumerated-object))  ;overrides value in
							; STANDARD-CLASS
	 )))
#<Standard-Class ENUMERATED-CLASS>
> (defmethod clos-system:validate-superclass ((class enumerated-class)
                                              (super standard-class))
    t)
#<Standard-Method VALIDATE-SUPERCLASS (ENUMERATED-CLASS STANDARD-CLASS)>
> (change-class (find-class 'enumerated-object) (find-class 'enumerated-class))
#<Enumerated-Class ENUMERATED-OBJECT>
> (defclass frob ()
    ((z :initform nil :documentation "Zome Zlot"))
    (:metaclass enumerated-class))
#<Enumerated-Class FROB>
> (clos:class-direct-superclasses (find-class 'frob))
(#<Enumerated-Class ENUMERATED-OBJECT>)
> (defmethod initialize-instance :after ((x enumerated-object) &rest w)
    (declare (ignore w))
    (push x (class-instances x)))
#<Init-Method INITIALIZE-INSTANCE :AFTER (ENUMERATED-OBJECT)>
> (setq x (make-instance 'frob))
#<Frob #X95698B>
> (setq y (make-instance 'frob))
#<Frob #X959EAB>
> (class-instances x)
(#<Frob #X959EAB> #<Frob #X95698B>)
>



-- JonL --

jeff@aiai.edinburgh.ac.uk (Jeff Dalton) (02/13/91)

> In article <1991Feb11.223240.20960@Think.COM> barmar@think.com (Barry Margolin) writes:
> Some Common Lisp vendors (e.g., Symbolics) offer some form of "weak
> reference" - a way to refer to an object without protecting it from
> GC.  Using weak references to keep track of extant instances avoids
> the need for explicit de-allocation.

A good point.

However, there is a problem with using weak reference for named
objects.  One of the points of having names is that the user can refer
to the object by name.  If weak links are used, the reference from the
name will cease to work once there are no more real pointers to the
object in the Lisp system.  But there may still be pointers (so to
speak)in the user's head.

Suppose I create an object named BILL, and don't do anything with
it for a while.  It gets gc'd and when I get around to trying to
use it, it's gone.

Of course, weak links are often just what you want when keeping
track of  all instances of some class.

-- Jeff

jeff@aiai.ed.ac.uk (Jeff Dalton) (02/14/91)

In article <9102122248.AA22313@kolyma> jonl@LUCID.COM (Jon L White) writes:

>On the other hand, with many implementations, there may be a
>performance penalty to pay if you use the approach that overrides
>the MAKE-INSTANCE method on STANDARD-CLASS.

I'm glad to see someone defending (in a sense) the vanilla
mixin approach as compared to the now so fashionable metaclass
method.  (Indeed, one advantage of CLOS over TELOS (the EuLisp
Object System) is that it's friendlier towards multiple inheritance
so that mixins are something you can expect to use.)

Anyway, one thing I've wondered about the MOP approach to slot access
is how slow access to slots of some new slot class would be.  How much
optimization could be done?

>                                        In your example, you
>have a method on INSTANCE-RECORDING-CLASS that "overrides" the
>one on STANDARD-CLASS. [Note: recent metaboject proposals define
>"extends" and "overrides" in more technical terms, and in that
>terminology, "extends" is the corect word; but without requiring
>every reader of this list to read these documents, I think "overrides"
>is more intuitively clear.

Some sort of distinction is needed, because it makes a big difference
whether defining a new methed breaks the way things currently work
(so that all slot access must be de-optimized, for example).

-- jeff

Gallagher@GILGAMESH.CS.UMASS.EDU (Kevin Gallagher) (02/14/91)

    > Date: Tue, 12 Feb 91 14:48:41 PST
    > From: Jon L White <jonl@lucid.COM>
    >
    > Here is an an example of the more vanilla approach, putting the
    > enumeration listing in *two* places:
    >
    > (defclass enumeration-mixin ()
    >   ((instances :initform nil :allocation :class :accessor class-instances)))


Note that in this example the INSTANCES slot will be shared by all
subclasses of ENUMERATION-MIXIN, and so will contain all instances of
all subclasses.  In the context of this discussion this is probably not
what is desired.  More likely, you would like (CLASS-INSTANCES <some-x>)
to return a list of all instances of class X and only instances of class
X.

For example, the following defines two classes EC-1 and EC-2 which use
ENUMERATION-MIXIN from JonL's message.

  (defclass ec-1 (enumeration-mixin)
    ((z-axis :initform nil :accessor z-axis)))

  (defclass ec-2 (enumeration-mixin)
    ((x-axis :initform nil :accessor x-axis)))

  (make-instance 'ec-1)

  (setq x1 (make-instance 'ec-1))
  (setq x2 (make-instance 'ec-2))

  ;; This is probably not what you want, because it has instances
  ;; of EC-1 and EC-2
  > (class-instances x1)
  (#<EC-2 #xF6E7E8> #<EC-1 #xF6E93D> #<EC-1 #xF6EAEC>)

  > (class-instances x2)
  (#<EC-2 #xF6E7E8> #<EC-1 #xF6E93D> #<EC-1 #xF6EAEC>)

  ;; Aside: The table is more like what you want
  ;; (an alist of (<class> . <instances>)).
  > *enumerations-map*
  ((#<STANDARD-CLASS EC-2> #<EC-2 #xF6E7E8>)
   (#<STANDARD-CLASS EC-1> #<EC-1 #xF6E93D> #<EC-1 #xF6EAEC>))


Using the simple mixin approach, the only way to get around this is to
add a slot spec for INSTANCES in each subclass.  e.g.,

  (defclass enumerated-class-1 (enumeration-mixin)
    ((instances :allocation :class)
     (z-axis :initform nil :accessor z-axis)))

This is obviously undesirable.


The metaclass solution presented also has the same problem, because the
`maximal' class (ENUMERATED-OBJECT) has a class allocated slot
(INSTANCES) which is used to hold all the instances.

One way to fix this would be to have an instance allocated slot in the
metaclass itself rather than in the maximal class (i.e.,
ENUMERATED-CLASS rather than ENUMERATED-OBJECT).  For example,

  ;; First, start this guy out as a plain old 'standard-class' class.
  ;; This class now has no slots, but is still used to specialize methods.
  (defclass enumerated-object ()
    ())

  (defclass enumerated-class (standard-class)
    ((clos::direct-superclasses :initform (list (find-class 'enumerated-object)))
     (instances :initform nil :accessor class-instances-internal)))

  (defmethod clos-system:validate-superclass ((class enumerated-class)
                                              (super standard-class))
      t)

  (change-class (find-class 'enumerated-object) (find-class 'enumerated-class))

  (defmethod class-instances ((obj enumerated-object))
    (class-instances-internal (class-of obj)))

  (defmethod (setf class-instances) (nv (obj enumerated-object))
    (setf (class-instances-internal (class-of obj)) nv))

  (defmethod initialize-instance :after ((x enumerated-object) &rest w)
    (declare (ignore w))
    (push x (class-instances x)))

  (defclass ec-1 ()
    ((z :initform nil :documentation "Zome Zlot"))
    (:metaclass enumerated-class))

  (defclass ec-2 ()
    ((x :initform nil :documentation "Xome Xlot"))
    (:metaclass enumerated-class))

  > (make-instance 'ec-1)
  #<Ec-1 #X107F0C56>

  > (setq x1 (make-instance 'ec-1))
  #<Ec-1 #X107F228E>

  > (setq x2 (make-instance 'ec-2))
  #<Ec-2 #X107F244E>

  > (class-instances x1)
  (#<Ec-1 #X107F228E> #<Ec-1 #X107F0C56>)

  > (class-instances x2)
  (#<Ec-2 #X107F244E>)

jonl@LUCID.COM (Jon L White) (02/14/91)

re: Note that in this example the INSTANCES slot will be shared by all
    subclasses of ENUMERATION-MIXIN, and so will contain all instances of
    all subclasses.  In the context of this discussion this is probably not
    what is desired.  More likely, you would like (CLASS-INSTANCES <some-x>)
    to return a list of all instances of class X and only instances of class
    X. . . .


Good point.  In fact, there is even a bit of terminological confusion
about what "instance" means.   I _think_ that amongst some subset of
of CLOS'ers, we've agreed to use "direct instance" to mean what you
are referring to here; to use "indirect instance" to mean "direct
instance of a subclass" (or, ocasionally "derived instance", following
the C++ inspired terms of "base class" and "derived class"); and to
admit that an unqualified "instance" is ambiguous in that different
sectors of the community use it differently.

So, independently of the design issues you bring up, _I_ would expect to
see the slot named INSTANCES pointing to the union of all direct and
derived instances, and to see the slot named DIRECT-INSTANCES used for
the more immediate set.  Incidentally, I note that Skip Egdorf did
follow this nomenclature in his recent mesages wherein he presesented
the generic function CLASS-DIRECT-INSTANCES.


One more thing -- sigh -- it seems that the mailer has lost my copy
of the message I sent out:

    > Date: Tue, 12 Feb 91 14:48:41 PST
    > From: Jon L White <jonl@lucid.COM>

You don't suppose you could forward me a copy?



-- JonL --

jkelly@BBN.COM (02/15/91)

I've used the metaclass approach to store instances, with pcl on a
symbolics.  Recently I recompiled in genera 8, which includes
symbolics clos implementation.  Doesn't quite work anymore however.
Perhaps people are interested in the problem and symbolics' response.

-Jim

(I've removed the names, addresses of the people at symbolics.  Don't
think it's appropriate for me to include that without their consent
(which I haven't solicited) as these were messages just between me and
them.

-----

	Subject: make-instance, metaclass bug?
	From: jkelly@BBN.COM
	Date: Wed, 30 Jan 1991 11:19 EST

	I'm trying to define a metaclass which keeps a list of its
	instances.  This worked fine in genera 7.2 but doesn't completely seem
	to work in 8.0.1.  If you could let me know how to get around the
	problem I'd appreciate it.

	Thanks,

	-Jim

	;; The following gets compiled and loaded:

	(defclass CLASS-WITH-INSTANCES (standard-class)
	     ((instances
		:initform nil
		:accessor class-instances)))


	(defmethod MAKE-INSTANCE :around ((class CLASS-WITH-INSTANCES) &rest ignore)
	  (let ((instance (call-next-method)))
	    (push instance (class-instances class))
	    instance))


	(defclass class-with-instances-test ()
	    ()
	  (:metaclass CLASS-WITH-INSTANCES))


	(defun foo ()
	  (make-instance 'class-with-instances-test))


	;; As follows make-instance doesn't work when called within a function:


	Command: (make-instance 'class-with-instances-test)
	#<CLASS-WITH-INSTANCES-TEST 44115530>

	Command: (class-instances (find-class 'class-with-instances-test))
	(#<CLASS-WITH-INSTANCES-TEST 44115530>)

	Command: (foo)
	1Error: INTERNAL ERROR: FAST-P true but
	0       1(CALL-METHOD #<STANDARD-METHOD MAKE-INSTANCE # (CLASS-WITH-INSTANCES) 271303317> (#)) occurred unexpectedly

	CLOS-INTERNALS::VERIFY-FAST-P
	0   Arg 0 (GENERIC-FUNCTION): #<STANDARD-GENERIC-FUNCTION MAKE-INSTANCE 524532057>
	   Arg 1 (CLOS-INTERNALS::PRIMARY-CALLED): NIL
	   Arg 2 (CLOS-INTERNALS::FAST-P): T
	   Arg 3 (CLOS-INTERNALS::FORM):
	(CALL-METHOD #<STANDARD-METHOD MAKE-INSTANCE # (CLASS-WITH-INSTANCES) 271303317> (#))
	s-A, :    Return to Breakpoint ZMACS in Editor Typeout Window 9
	s-B:           Editor Top Level
	s-C:           Restart process Zmacs Windows 2


-----------------------


Date: Mon, 11 Feb 1991 12:59-0500
Subject: [jkelly@BBN.COM: make-instance, metaclass bug?]


"It's a constructors bug.  It still happens in 8.1.

There's no need to be using a metaclass to do this kind of thing; a
mixin with an after method on shared-initialize would do the job."


In addition, the following comments come from another developer here.
I'm not sure how applicable they are to your problem, but I thought I
should include them.


We do not support metaobject protocol (a.k.a. MOP, a.k.a.  "CLOS Chapter
3").  If you think something worked in 7.2 (which is before we
introduced CLOS), our guess is that you don't maintain your own world
but rather just uses a world that someone else has set up for you, and
that probably in 7.2 you were loading PCL, which is where the
meta-object protocol originated.  Probably in 8.0 you switched to our
CLOS, and got bitten because you were using all those chapter 3 things
which are really not part of the CLOS that has been adopted by X3J13.

The meta-object stuff may never be adopted as is - our guess is that it
will change a lot as part of standardization efforts because other
implementations will want to do some things differently; in some ways
it's still believed to be too dependent on the PCL way of doing things.
But in any case, it will almost certainly not appear in ANSI CL, and so
is not something you will be able to depend on in portable code, and
we'd recommend you not get hooked on it for the practical reason of
leaving your code open to later porting.

-----

welch@CIS.OHIO-STATE.EDU (Arun Welch) (02/15/91)

>One more thing -- sigh -- it seems that the mailer has lost my copy
>of the message I sent out:
>
>    > Date: Tue, 12 Feb 91 14:48:41 PST
>    > From: Jon L White <jonl@lucid.COM>
>
>You don't suppose you could forward me a copy?
>

Messages to CommonLoops (and thus comp.lang.clos) are automatically
archived by the software that handles the mailing list, and at the end
of the month I compress the file and put it on tut.cis.ohio-state.edu
in directory pub/lispusers/commonloops, available via anonymous ftp
(and uucp, if you need that send me a message). If you need something
from the current month, let me know and I'll extract it from the file
(it being a "live" file, I'd rather not put it in the ftp area). Space
permitting, I'll try and keep at least a year's worth of archives
online.

...arun
CommonLoops-request@cis.ohio-state.edu

egdorf@ZAPHOD.LANL.GOV (Skip Egdorf) (02/15/91)

	   (defclass CLASS-WITH-INSTANCES (standard-class)
		((instances
		   :initform nil
		   :accessor class-instances)))

X3J13 88-002R Page 1-33
CLtL-II Page 800
   standard-class is specified as a pre-defined metaclass, and describes it.

	   (defmethod MAKE-INSTANCE :around ((class CLASS-WITH-INSTANCES) &rest ignore)
	     (let ((instance (call-next-method)))
	       (push instance (class-instances class))
	       instance))

X3J13 88-002R Page 1-35
CLtL-II Page 801
   make-instance described as a generic function to allow customization

X3J13 88-002R Page 1-41
CLtL-II Pages 808-809
   Shows an example definition of make-instance that seems to allow the
   example above.

X3J13 88-002R Page 2-60
CLtL-II Page 848
  (make-instance (class standard-class) ...) is shown as a primary method.
  Use of the meta-object protocol ... defining new methods specialized on
  standard-class is mentioned.

	   (defclass class-with-instances-test ()
	       ()
	     (:metaclass CLASS-WITH-INSTANCES))

X3J13 88-002R Page 2-24
CLtL-II Page 825
  :metaclass in a defclass is described.

	   (defun foo ()
	     (make-instance 'class-with-instances-test))

           ... [error descriptions] ...

           ... [Now Symbolic's response]...

   There's no need to be using a metaclass to do this kind of thing; a
   mixin with an after method on shared-initialize would do the job."

We've been beating this one to death in the last few days.
There's no need to use Lisp/CLOS for this. A Turing machine would do the job.

   We do not support metaobject protocol (a.k.a. MOP, a.k.a.  "CLOS Chapter
   3").

I don't see any Chapater 3  stuff here, Everything given in this example
uses stuff described in X3J13 88-002R Chapters 1 and 2, and CLtL-II.

True, the Draft MOP document goes into a lot more detail about specific
implementation issues, but I view this example as a part of Chapters 1 and 2,
NOT chapter 3.

   The meta-object stuff may never be adopted as is - our guess is that it
   will change a lot as part of standardization efforts because other
   implementations will want to do some things differently; in some ways
   it's still believed to be too dependent on the PCL way of doing things.

While this may be so, there are a LOT of programs being written that depend
on the CHAPTER 2 behavior used here. While I am careful enough not to depend
on some obscure function in the draft MOP, :metaclass in a defclass is well
enough defined that if one doesn't support it to the extent of this example,
then I believe that it is safe to say that that vendor does not support CLOS.

   But in any case, it will almost certainly not appear in ANSI CL, and so
   is not something you will be able to depend on in portable code, and
   we'd recommend you not get hooked on it for the practical reason of
   leaving your code open to later porting.

Allright, to this extent, there is as yet NO ANSI CL. So as long as some
splinter group wishes to oppose DEFSTRUCT or ARRAYS or something, then it
is fair to claim that "it will certainly not appear in ANSI CL" for whatever
pet ox is being gored.

With this said, I expect that defstruct, defmethod, etc from chapter 2 "WILL
almost certainly appear in ANSI CL" [note the NOT removed] in a form very
similar to what is in CLtL-II, including basic :metaclass functionality.
Remember, loop was added, not because there was such a great feeling that
loop was technically "right," but rather that there was so much bloody use of
it in existing code! I believe that the example given here falls into a similar
catagory. I can't imagine X3J13 leaving this out at this late date. All the
gory details of the draft MOP? Maybe, At least with lots of changes and
clarifications. But removing :metaclass from defclass??? I don't think so.

One can only hope that this comment only reflects a few individual opinions
within Symbolics rather than Symbolics' corporate opinion. (Such is the
usual protocol on the Net and in e-mail in general, I believe.) However,
the behavior of the Genera 8 CLOS does not give me much hope. Just as I
have given in and accepted hideous LOOP as a part of the language due to
its common usage, I hope that Symbolics will accept at least the basics of
Metaclasses as described in Chapters 1 and 2 of the CLOS spec due to their
common usage. It would be a good start to acknowledge that the Genera 8
behavior given in this example is a BUG and start taking steps to fix it.

					Skip Egdorf
					hwe@lanl.gov

Oh, Yeah. That last paragraph requires:
This is my personal opinion, and does not necessarily reflect the opinions
of the Dept. of Energy, the Univ. of Calif, or Los Alamos National Laboratory.

egdorf@ZAPHOD.LANL.GOV (Skip Egdorf) (02/15/91)

Oh yes, another thing about the Symbolics Bug/feature with :metaclass

;;; Sun Common Lisp, Development Environment 4.0.1, 6 July 1990
;;; Sun-4 Version for SunOS 4.0.x and sunOS 4.1
;;;
;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990
;;;            by Sun Microsystems, Inc.,  All Rights Reserved
;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990
;;;            by Lucid, Inc., All Rights Reserved
;;; This software product contains confidential and trade secret
;;; information belonging to Sun Microsystems, Inc.  It may not be copied
;;; for any reason other than for archival and backup purposes.
;;;
;;; Sun, Sun-4, and Sun Common Lisp are trademarks of Sun Microsystems Inc.

;;; Loading source file "/home/zaphod/egdorf/lisp-init.lisp"
> (defclass CLASS-WITH-INSTANCES (standard-class)
  ((instances
    :initform nil
    :accessor class-instances)))
#<Standard-Class CLASS-WITH-INSTANCES>
> (defmethod MAKE-INSTANCE :around ((class CLASS-WITH-INSTANCES) &rest ignore)
	   (let ((instance (call-next-method)))
	     (push instance (class-instances class))
	     instance))
#<Init-Method MAKE-INSTANCE :AROUND (CLASS-WITH-INSTANCES)>
> (defclass class-with-instances-test ()
  ()
  (:metaclass CLASS-WITH-INSTANCES))
#<Class-With-Instances CLASS-WITH-INSTANCES-TEST>
> (defun foo ()
  (make-instance 'class-with-instances-test))
FOO
> (make-instance 'class-with-instances-test)
#<Class-With-Instances-Test #X166748E>
> (class-instances (find-class 'class-with-instances-test))
(#<Class-With-Instances-Test #X166748E>)
> (foo)
#<Class-With-Instances-Test #X1671AC6>
> (class-instances (find-class 'class-with-instances-test))
(#<Class-With-Instances-Test #X1671AC6> #<Class-With-Instances-Test #X166748E>)
>

Works for me...
And running it all from gnu emacs with some local additions to cmulisp mode,
I could just cut and paste to this message.
I don't miss zmacs at all.

					Skip Egdorf
					hwe@lanl.gov

jonl@LUCID.COM (Jon L White) (02/15/91)

re: The name CLASS-DIRECT-INSTANCES, as noted, was used following the model
    of CLASS-DIRECT-SUBCLASSES already in the MOP.

I thought as much.  Still, there is an exact linquistic correlation
between  DIRECT-SUBCLASSES/SUBCLASSES and DIRECT-INSTANCES/INSTANCES
(assuming that one allows the ambiguous term "instances"; if not, then
one must question whether "subclasses" is always used unambiguously.)


-- JonL --

jonl@lucid.com (Jon L White) (02/16/91)

re: I think you have to establish metaclass compatibility, . . .

Well, that is only one option for a metaobject protocol; it's perfectly
reasonable NOT to have such a requirement.

In fact, Lucid's 4.0 imposes the requirement, but Symbolics' 8.1 defaults
all subclasses of STANDARD-CLASS to be mutually compatible.  I think the
intent of the current "Chapter 3" proposal is to require a positive step
by the metaclass programmer (i.e., the default is not having the enabling
method  on VALIDATE-SUPERCLASS, and the programmer must write it himself
if so wanted), and I would argue for this position.  But the other position
-- Symbolics's 8.1 choice -- isn't ruled out by "Chapters 1 & 2" alone.


-- JonL --

egdorf@ZAPHOD.LANL.GOV (Skip Egdorf) (02/16/91)

>   > I don't see any Chapater 3  stuff here, Everything given in this example
>   > uses stuff described in X3J13 88-002R Chapters 1 and 2, and CLtL-II.
>
>   I think you have to establish metaclass compatibility, and that is
>   not (so far as I can tell) in Chapter 1 or 2.  If compatibility wasn't
>   handled in this example, I think it should be.

I believe that you have zeroed in on a main flaw in my argument! Metaclass
compatibility is indeed not in any chapter 1 or 2 discussions. And I was
hoping no one would notice (:-).

In my response to jonl@lucid, I suggest that a good interim solution from a
user's point of view might be to define some subset of a MOP that just
deals with modifications to standard-class, without admitting to metaclasses
that are independent of standard-class. This would involve dictating that
any subclass of standard-class is compatible with standard-class. I believe that
this is reasonable.

					Skip Egdorf
					hwe@lanl.gov

jeff@aiai.edinburgh.ac.uk (Jeff Dalton) (02/16/91)

> In my response to jonl@lucid, I suggest that a good interim solution from a
> user's point of view might be to define some subset of a MOP that just
> deals with modifications to standard-class, without admitting to metaclasses
> that are independent of standard-class. This would involve dictating that
> any subclass of standard-class is compatible with standard-class. I
> believe that this is reasonable.

I think this may well be a good idea (despite my disagreement with
the "major players" approach in another message).  (It's the process
of approval, rather than the suggesiton itself, I have some trouble
with.)

-- jeff