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