{"id":118,"date":"2005-03-08T17:00:52","date_gmt":"2005-03-08T15:00:52","guid":{"rendered":"http:\/\/blogs.bl0rg.net\/netzstaub\/?p=118"},"modified":"2005-03-08T17:00:52","modified_gmt":"2005-03-08T15:00:52","slug":"a-short-practical-overview-of-the-metaobject-protocol","status":"publish","type":"post","link":"https:\/\/blogs.bl0rg.net\/netzstaub\/2005\/03\/08\/a-short-practical-overview-of-the-metaobject-protocol\/","title":{"rendered":"A short practical overview of the Metaobject Protocol"},"content":{"rendered":"<p>\nI&#8217;ll try to write this blog entry in english. I&#8217;m not very used to writing in english though, so please bear with me \ud83d\ude42\n<\/p>\n<p>\nI will try to show how to use the Metaobject Protocol for <a href=\"http:\/\/home.comcast.net\/~prunesquallor\/guide.html\">CLOS<\/a> (the Lisp OO system). When learning how to use MOP, I had quite a hard time finding examples on the web, and the seminal work about MOP (<a href=\"http:\/\/www.amazon.com\/exec\/obidos\/ASIN\/0262610744\/\">The Art of the Metaobject Protocol<\/a>) doesn&#8217;t offer that much help either. Furthermore, MOP is not <a href=\"http:\/\/www.lisp.org\/mop\/index.html\">really standardized<\/a> as it is, and most Lisp implementations offer subtly different implementations. The code I&#8217;ll present here has been tested with PCL-based Lisp implementations (<a href=\"http:\/\/www.sbcl.org\/\">SBCL<\/a> and <a href=\"http:\/\/www.cons.org\/cmucl\/\">CMUCL<\/a>, for example), and with <a href=\"http:\/\/www.franz.com\/\">Allegro CL<\/a>. I guess it can easily be ported to other Lisp implementations, but haven&#8217;t got around to do it for now.\n<\/p>\n<p>\nA good deal of the functionality of <a href=\"http:\/\/bknr.net\/\">BKNR<\/a> is based on the Metaobject Protocol, namely:\n<\/p>\n<ol>\n<li>the <strong>Indices<\/strong> package, which offers object-oriented indices on CLOS objects.<\/li>\n<li>the <strong>XML import\/export<\/strong> package, which offers XML bindings for CLOS objects,<\/li>\n<li>the <strong>Datastore<\/strong> package, which offers persistent CLOS objects and support for logged transactions.<\/li>\n<\/ol>\n<p>\nThe Metaobject Protocol is a &#8220;meta&#8221;-layer above CLOS which allows both introspection and customization of the Object System.\n<\/p>\n<p><!--more--><\/p>\n<p><strong>MOP for introspection<br \/><\/strong><br \/>\nMOP presents an object-oriented view to the object-system itself.<br \/>\nFor example, the classes created using the DEFCLASS macro are<br \/>\nthemselves objects. You can get such an object by using<br \/>\nFIND-CLASS:<\/p>\n<pre>\nCL-USER&gt; (defclass foo () ())\n#&lt;STANDARD-CLASS FOO&gt;\nCL-USER&gt; (find-class 'foo)\n#&lt;STANDARD-CLASS FOO&gt;\nCL-USER&gt; (describe *)\n#&lt;STANDARD-CLASS FOO&gt; is an instance of\n    #&lt;STANDARD-CLASS STANDARD-CLASS&gt;:\n The following slots have :INSTANCE allocation:\n  PLIST                   NIL\n  FLAGS                   0\n  DIRECT-METHODS          (NIL)\n  NAME                    FOO\n  DIRECT-SUPERCLASSES     (#&lt;STANDARD-CLASS STANDARD-OBJECT&gt;)\n  DIRECT-SUBCLASSES       NIL\n  CLASS-PRECEDENCE-LIST   NIL\n  WRAPPER                 NIL\n  DIRECT-SLOTS            NIL\n  SLOTS                   NIL\n  PROTOTYPE               NIL\n; No value\nCL-USER&gt; (find-class 'standard-class)\n#&lt;STANDARD-CLASS STANDARD-CLASS&gt;\n<\/pre>\n<p>The class FOO we created is represented by an object of class<br \/>\nSTANDARD-CLASS. The object representing STANDARD-CLASS is itself of<br \/>\nclass STANDARD-CLASS. Beware of the recursion trap if you try too<br \/>\nhard to think about it. The trick here is that the MOP is not<br \/>\nnecessarily the actual implementation of the object system, but is<br \/>\njust a representation.<\/p>\n<p>MOP can be used to inspect not only classes, but also other<br \/>\nparts of the object system. You can inspect slot objects, which<br \/>\nrepresent the slots of classes:<\/p>\n<pre>\nCL-USER&gt; (defclass blorg () (a b (c :initarg :c :initform 1)))\n#&lt;STANDARD-CLASS BLORG&gt;\nCL-USER&gt; (mop:finalize-inheritance (find-class 'blorg))\nNIL\nCL-USER&gt; (mop:class-slots (find-class 'blorg))\n(#&lt;ACLMOP:STANDARD-EFFECTIVE-SLOT-DEFINITION A @ #x55b17b2&gt;\n #&lt;ACLMOP:STANDARD-EFFECTIVE-SLOT-DEFINITION B @ #x55b17fa&gt;\n #&lt;ACLMOP:STANDARD-EFFECTIVE-SLOT-DEFINITION C @ #x55b1842&gt;)\n<\/pre>\n<p>We see the first trick to use in order to use the ACL MOP here.<br \/>\nThe ACL MOP delays most operations until the first instance of a<br \/>\nclass is actually created. We force the finalization of the class<br \/>\nBLORG here by calling the method FINALIZE-INHERITANCE by hand. We<br \/>\ncan now inspect the slot objects:<\/p>\n<pre>\nCL-USER&gt; (describe (first *))\n#&lt;ACLMOP:STANDARD-EFFECTIVE-SLOT-DEFINITION A @ #x55b17b2&gt; is an\n    instance of\n    #&lt;STANDARD-CLASS ACLMOP:STANDARD-EFFECTIVE-SLOT-DEFINITION&gt;:\n The following slots have :INSTANCE allocation:\n  NAME            A\n  TYPE            T\n  DOCUMENTATION   NIL\n  INITFORM        NIL\n  INITFUNCTION    NIL\n  INITARGS        NIL\n  ALLOCATION      :INSTANCE\n; No value\nCL-USER&gt; (describe (car (last **)))\n#&lt;ACLMOP:STANDARD-EFFECTIVE-SLOT-DEFINITION C @ #x561c342&gt; is an\n    instance of\n    #&lt;STANDARD-CLASS ACLMOP:STANDARD-EFFECTIVE-SLOT-DEFINITION&gt;:\n The following slots have :INSTANCE allocation:\n  NAME            C\n  TYPE            T\n  DOCUMENTATION   NIL\n  INITFORM        1\n  INITFUNCTION    #&lt;Closure (:INTERNAL CONSTANTLY 0) @ #x5649a22&gt;\n  INITARGS        (:C)\n  ALLOCATION      :INSTANCE\n; No value\n<\/pre>\n<p>In the same way, we can also inspect generic function objects<br \/>\nand method objects (I won&#8217;t cover that here because I never used<br \/>\nit).<\/p>\n<p><strong>MOP for customization<\/strong><\/p>\n<p>The most interesting part of MOP however is the customization<br \/>\npart. You can use MOP to modify the way CLOS works. Now, most of<br \/>\nthe time, the standard tools of CLOS are sufficient (think about<br \/>\nwhat you can do with method combinations, multiple inheritance, and<br \/>\nthe like). But in special cases, you want to control slot access<br \/>\nfor example, triggering special events when a slot is accessed<br \/>\nwithout overleading all slot accessors, and without creating<br \/>\nwrapper macros. Or you would like to add automatically generated<br \/>\nslots on class creation. You can do all this by creating your own<br \/>\nmetaclasses. Metaclasses are classes instantiated to create class<br \/>\nobjects. For example, STANDARD-CLASS is such a metaclass (and all<br \/>\nyou metaclasses should inherit from STANDARD-CLASS if you want to<br \/>\navoid weird effects). Starting from there, MOP has a myriad of<br \/>\nlittle generic method protocols which specify how classes are<br \/>\ninstantiated, how slots are accessed, how methods are called, how<br \/>\nobjects are initialized. By carefully overloading or modifying some<br \/>\nof these methods for your metaclasses, you can add new<br \/>\nfunctionality to you CLOS classes and objects.<\/p>\n<p>I&#8217;ll present the functionality we added using MOP in our BKNR<br \/>\nframework. However, this will not be an exhaustive coverage of all<br \/>\nthe functionality we implemented, but more a kind of overview. I<br \/>\nhope these examples will help you to get started with MOP. I had a<br \/>\nlot of difficulty grasping it at first. After all this introductory<br \/>\nsmalltalk, I think it is time to get started with the real<br \/>\nthing.<\/p>\n<p><strong>Indices for CLOS objects<\/strong><\/p>\n<p>The Indices package was motivated by the fact that we had to<br \/>\nquery the data that was stored in our first persistent store. For<br \/>\nexample, we had a lot of classes that had unique identification<br \/>\nstring (the username for the USER class, for example, or the image<br \/>\nname for IMAGEs, or the title for ARTICLEs). We factored this<br \/>\nfunctionality out by using a big class definition macro, which was<br \/>\ngladly wept under the rug when we rewrote the datastore. However,<br \/>\nwe wanted to keep the practical aspects of the macro: we were able<br \/>\nto specify an index and the name of the functions to query it in a<br \/>\nfew keywords inside the class declaration. To illustrate what I&#8217;m<br \/>\nsaying, imagine that you are keeping track of gorillas in wildlife<br \/>\npark (these examples are straight from the Indices package<br \/>\ndocumentation). In traditional Common Lisp, you would write<br \/>\nsomething like this:<\/p>\n<pre>\n(defclass gorilla ()\n  ((name        :initarg :name\n        :reader gorilla-name\n        :type string)\n   (description :initarg :description\n        :reader gorilla-description)))\n<\/pre>\n<pre>\n(defun all-gorillas ()\n  (copy-list *gorillas*))\n<\/pre>\n<pre>\n(defun gorilla-with-name (name)\n  (find name *gorillas* :test #'string-equal\n    :key #'gorilla-name))\n<\/pre>\n<pre>\n(defun gorillas-with-description (description)\n  (remove description *gorillas* :test-not #'eql :key\n      #'gorilla-description))\n<\/pre>\n<p>Writing all the query-functions by hand quickly gets tiresome<br \/>\n(especially if all the indices are similar). The original<br \/>\nDEFPERSISTENTCLASS (or whatever it used to be called) handled this<br \/>\nby extracting special keyword arguments from the slot definition<br \/>\nand adding custom :AROUND wrappers on the slot accessors and the<br \/>\nINITIALIZE-INSTANCE method. This did not catch slot acces using the<br \/>\nSLOT-VALUE function though, and handling inheritance was ugly and<br \/>\nprone to error to say the least. Before delving into the<br \/>\nimplementation details of BKNR Indices, let me explain how slots<br \/>\nare handled by MOP.<\/p>\n<p><strong>Slot definitions<\/strong><\/p>\n<p>A class specification, which is written by the programmer using<br \/>\nthe DEFCLASS macro, specificies how &#8220;DIRECT-SLOT-DEFINITION&#8221;<br \/>\nobjects are created. These are the objects that hold the parameters<br \/>\ngiven in the slot definition. The direct slot definition class can<br \/>\nbe specified by overloading the DIRECT-SLOT-DEFINITION-CLASS method<br \/>\nof the metaclass. The new direct slot definition class should<br \/>\ninherit from the STANDARD-DIRECT-DEFINITION class. When a new class<br \/>\nis created, the direct slot definition objects for each slot are<br \/>\ncombined with the direct slot definition objects of the<br \/>\nsuperclasses of the new classes. and this list of direct slot<br \/>\ndefinition objects is used to create the effective slot definition.<br \/>\nThe effective slot definition is the object that is used for the<br \/>\n&#8220;real&#8221; slots of the class. The information stored in the direct<br \/>\nslot definitions can be used to create this effective slot. Let&#8217;s<br \/>\nsee how this applies for our indexed objects.<\/p>\n<p>The indexes for a slot can be specified using a multitude of<br \/>\nkeywords in the slot definition. For example, you can specify the<br \/>\ntype of the index to be created, the name of various query<br \/>\nfunctions, as well as a list of initialisation arguments to be used<br \/>\nat index creation. All these parameters are stored in the direct<br \/>\nslot definition. Thus, we create a new metaclass for indexed<br \/>\nclasses classed INDEX-DIRECT-SLOT-DEFINITION (this definition is a<br \/>\nbit lengthy, but I will post it in full for the sake of<br \/>\ncompleteness):<\/p>\n<pre>\n(defclass index-direct-slot-definition (standard-direct-slot-definition)\n  ((index :initarg :index :initform nil\n      :reader index-direct-slot-definition-index\n      :documentation \"Slot keyword for an already existing index\")\n<\/pre>\n<pre>\n   (index-var :initarg :index-var :initform nil\n          :reader index-direct-slot-definition-index-var\n          :documentation \"Symbol that will be bound to the index\")\n   \n   (index-type :initarg :index-type :initform nil\n           :reader index-direct-slot-definition-index-type\n           :documentation \"Slot keyword to specify the class of a new slot index\")\n   (index-initargs :initarg :index-initargs :initform nil\n           :reader index-direct-slot-definition-index-initargs\n           :documentation \"Arguments that will be passed to\nINDEX-CREATE when creating a new slot index\")\n<\/pre>\n<pre>\n   (index-reader :initform nil\n         :initarg :index-reader\n         :accessor index-direct-slot-definition-index-reader\n         :documentation \"Name of a function that will be created to query the slot index\")\n   (index-values :initform nil\n         :initarg :index-values\n         :accessor index-direct-slot-definition-index-values\n         :documentation \"Name of a function that will be\ncreated to get the values stored in the index\")\n   (index-mapvalues :initform nil\n            :initarg :index-mapvalues\n            :accessor index-direct-slot-definition-index-mapvalues\n            :documentation \"Name of a function that will be\ncreated to map over the values stored in the index\")\n   (index-keys :initform nil\n           :initarg :index-keys\n           :accessor index-direct-slot-definition-index-keys\n           :documentation \"Name of a function that will be created\nto get the keys stored in the index\")\n<\/pre>\n<pre>\n   (index-subclasses :initarg :index-subclasses :initform t\n             :accessor index-direct-slot-definition-index-subclasses\n             :documentation \"Specify if the slot index will\nalso index subclasses of the class to which the slot belongs, default is T\")\n   \n   (class :initform nil\n      :accessor index-direct-slot-definition-class)))\n<\/pre>\n<p>When a new indexed class is created (to be precise, when a new<br \/>\nclass with the metaclass INDEXED-CLASS is created), the direct slot<br \/>\ndefinitions objects for each slot are combined into an object of<br \/>\nclass INDEX-EFFECTIVE-SLOT-DEFINITION. This object is a lot simpler<br \/>\nthan the direct slot definition. It only holds a list of indexes<br \/>\nthat apply to the slot (indexes that have to be updated when the<br \/>\nslot is changed).<\/p>\n<pre>\n(defclass index-effective-slot-definition (standard-effective-slot-definition)\n  ((indices :initarg :indices :initform nil\n        :accessor index-effective-slot-definition-indices)))\n<\/pre>\n<p>For example, when we create a class USER with the slot NAME, a<br \/>\ndirect slot definition is created for the slot NAME. This direct<br \/>\nslot definition object will be used to create the corresponding<br \/>\nindex, which will be stored in the effective slot definition, which<br \/>\nwill be used for slot accesses later on.<\/p>\n<pre>\nINDICES&gt; (defclass user ()\n  ((name :initarg :name\n         :reader user-name\n         :index-type string-unique-index))\n  (:metaclass indexed-class))\n#&lt;INDEXED-CLASS USER&gt;\n<\/pre>\n<pre>\nINDICES&gt; (mop:class-slots *)\n(#&lt;INDEX-EFFECTIVE-SLOT-DEFINITION DESTROYED-P @ #x6768912&gt;\n #&lt;INDEX-EFFECTIVE-SLOT-DEFINITION NAME @ #x676814a&gt;)\n<\/pre>\n<pre>\nINDICES&gt; (mop:class-direct-slots **)\n(#&lt;INDEX-DIRECT-SLOT-DEFINITION NAME @ #x6767ffa&gt;)\n<\/pre>\n<pre>\nINDICES&gt; (describe (first *))\n#&lt;INDEX-DIRECT-SLOT-DEFINITION NAME @ #x6767ffa&gt; is an instance of\n    #&lt;STANDARD-CLASS INDEX-DIRECT-SLOT-DEFINITION&gt;:\n The following slots have :INSTANCE allocation:\n  NAME               NAME\n  TYPE               T\n  DOCUMENTATION      NIL\n  INITFORM           NIL\n  INITFUNCTION       NIL\n  INITARGS           (:NAME)\n  ALLOCATION         :INSTANCE\n  READERS            (USER-NAME)\n  WRITERS            NIL\n  FIXED-INDEX        NIL\n  INDEX              NIL\n  INDEX-VAR          NIL\n  INDEX-TYPE         STRING-UNIQUE-INDEX\n  INDEX-INITARGS     NIL\n  INDEX-READER       NIL\n  INDEX-VALUES       NIL\n  INDEX-MAPVALUES    NIL\n  INDEX-KEYS         NIL\n  INDEX-SUBCLASSES   T\n  CLASS              #&lt;INDEXED-CLASS USER&gt;\n<\/pre>\n<pre>\nINDICES&gt; (describe (second (mop:class-slots (find-class 'user))))\n#&lt;INDEX-EFFECTIVE-SLOT-DEFINITION NAME @ #x676814a&gt; is an instance of\n    #&lt;STANDARD-CLASS INDEX-EFFECTIVE-SLOT-DEFINITION&gt;:\n The following slots have :INSTANCE allocation:\n  NAME            NAME\n  TYPE            T\n  DOCUMENTATION   NIL\n  INITFORM        NIL\n  INITFUNCTION    NIL\n  INITARGS        (:NAME)\n  ALLOCATION      :INSTANCE\n  INDICES         (#&lt;STRING-UNIQUE-INDEX SLOT: NAME SIZE: 0 @\n                     #x6768182&gt;)\n<\/pre>\n<p>We can then create a class KILLER-USER, and add an additional<br \/>\nindex on the NAME slot. Both direct slot definitions (for USER and<br \/>\nKILLER-USER) will be combined to form a new effective slot<br \/>\ndefinition.<\/p>\n<pre>\nINDICES&gt; (defclass killer-user (user)\n           ((name :index-type string-unique-index\n                  :index-reader killer-user-with-name))\n           (:metaclass indexed-class))\n#&lt;INDEXED-CLASS KILLER-USER&gt;\n<\/pre>\n<pre>\nINDICES&gt; (describe (second (mop:class-slots (find-class 'killer-user))))\n#&lt;INDEX-EFFECTIVE-SLOT-DEFINITION NAME @ #x67be082&gt; is an instance of\n    #&lt;STANDARD-CLASS INDEX-EFFECTIVE-SLOT-DEFINITION&gt;:\n The following slots have :INSTANCE allocation:\n  NAME            NAME\n  TYPE            T\n  DOCUMENTATION   NIL\n  INITFORM        NIL\n  INITFUNCTION    NIL\n  INITARGS        (:NAME)\n  ALLOCATION      :INSTANCE\n  INDICES         (#&lt;STRING-UNIQUE-INDEX SLOT: NAME SIZE: 0 @\n                     #x6768182&gt;\n                   #&lt;STRING-UNIQUE-INDEX SLOT: NAME SIZE: 0 @\n                     #x67be0d2&gt;)\n<\/pre>\n<pre>\nINDICES&gt; (mop:class-direct-slots (find-class 'killer-user))\n(#&lt;INDEX-DIRECT-SLOT-DEFINITION NAME @ #x67bdfda&gt;)\nINDICES&gt; (describe (first *))\n#&lt;INDEX-DIRECT-SLOT-DEFINITION NAME @ #x67bdfda&gt; is an instance of\n    #&lt;STANDARD-CLASS INDEX-DIRECT-SLOT-DEFINITION&gt;:\n The following slots have :INSTANCE allocation:\n  NAME               NAME\n  TYPE               T\n  DOCUMENTATION      NIL\n  INITFORM           NIL\n  INITFUNCTION       NIL\n  INITARGS           NIL\n  ALLOCATION         :INSTANCE\n  READERS            NIL\n  WRITERS            NIL\n  FIXED-INDEX        NIL\n  INDEX              NIL\n  INDEX-VAR          NIL\n  INDEX-TYPE         STRING-UNIQUE-INDEX\n  INDEX-INITARGS     NIL\n  INDEX-READER       KILLER-USER-WITH-NAME\n  INDEX-VALUES       NIL\n  INDEX-MAPVALUES    NIL\n  INDEX-KEYS         NIL\n  INDEX-SUBCLASSES   T\n  CLASS              #&lt;INDEXED-CLASS KILLER-USER&gt;\n<\/pre>\n<p>As for direct slot definitions, changing the class of the<br \/>\neffective slot definition can be done by overloading the method<br \/>\nEFFECTIVE-SLOT-DEFINITION-CLASS. The method that combines all the<br \/>\ndirect slots into a new effective slot definition object is called<br \/>\nCOMPUTE-EFFECTIVE-SLOT-DEFINITION. In addition, if we want to add<br \/>\nnew custom slots to a class (for example, you may have noticed the<br \/>\nnew slot DESTROYED-P in the USER class), we can do this in the<br \/>\nmethod COMPUTE-SLOTS. However, we have to be careful with the<br \/>\nCOMPUTE-SLOTS method. Its last step is to carry through the<br \/>\nallocation of the slots. This step is implementation specific, and<br \/>\noverloading the COMPUTE-SLOTS method has to be done with care so as<br \/>\nnot to jump over this last step for new slots.<\/p>\n<p><strong>The indices MOP code<\/strong><\/p>\n<p>I will now walk through the actual code of INDEXED-CLASS (which<br \/>\ncan be found in the bknr repository under<br \/>\nsvn:\/\/bknr.net\/trunk\/bknr\/src\/indices\/indexed-class.lisp). I will<br \/>\nnot discuss the indices functionality though (I will blog about<br \/>\nthis another day). First, we have to define our new metaclass,<br \/>\ncalled INDEXED-CLASS. It is customary to name new metaclasses<br \/>\nSOMETHING-CLASS.<\/p>\n<pre>\n(defclass indexed-class (standard-class)\n  ((indices :initarg :indices :initform nil\n        :accessor indexed-class-indices)\n   (old-indices :initarg :old-indices :initform nil\n        :accessor indexed-class-old-indices)\n   (index-definitions :initarg :class-indices :initform nil\n              :accessor indexed-class-index-definitions)))\n<\/pre>\n<p>The INDICES slot here holds the indices which apply to the class<br \/>\ndirectly, and which have to be updated on class modification for<br \/>\nexample. OLD-INDICES is only used when reinitializing the class to<br \/>\nhold some temporary values. I think this could be removed by using<br \/>\nsome clever :AROUND method. INDEX-DEFINITIONS holds the definitions<br \/>\nof class indices, which apply to all slots of an object. The<br \/>\ndefinitions are used to reinitialize the indices when the class is<br \/>\nchanged. The next step is to validate the new superclass with the<br \/>\nMOP by specifically overloading VALIDATE-SUPERCLASS (this is<br \/>\nnecessary for PCL-based MOPs).<\/p>\n<pre>\n(defclass indexed-class (standard-class)\n  ((indices :initarg :indices :initform nil\n        :accessor indexed-class-indices)\n   (old-indices :initarg :old-indices :initform nil\n        :accessor indexed-class-old-indices)\n   (index-definitions :initarg :class-indices :initform nil\n              :accessor indexed-class-index-definitions)))\n<\/pre>\n<pre>\n(defmethod validate-superclass ((sub indexed-class) (super standard-class))\n  t)\n<\/pre>\n<p>The next step is to overload the DIRECT-SLOT-DEFINITION-CLASS<br \/>\nand EFFECTIVE-SLOT_DEFINITION-CLASS methods. We will create an<br \/>\nINDEX-DIRECT-SLOT-DEFINITION object only if the initargs INDEX or<br \/>\nINDEX-TYPE are present. However, to simplify our handling code, we<br \/>\nwill always create INDEX-EFFECTIVE-SLOT-DEFINITIONS objects.<\/p>\n<pre>\n(defmethod direct-slot-definition-class ((class indexed-class) &amp;key index index-type &amp;allow-other-keys)\n  (if (or index index-type)\n      'index-direct-slot-definition\n      (call-next-method)))\n<\/pre>\n<pre>\n(defmethod effective-slot-definition-class ((class indexed-class) &amp;rest initargs)\n  (declare (ignore initargs))\n  'index-effective-slot-definition)\n<\/pre>\n<p>The direct slot definitions are used by<br \/>\nCOMPUTE-EFFECTIVE-SLOT-DEFINITION to compute the effective indices<br \/>\nfor a slot. Here it is (beware, this function is quite<br \/>\ncomplicated):<\/p>\n<pre>\n(defmethod compute-effective-slot-definition ((class indexed-class) name direct-slots)\n  (declare (ignore name))\n  (let* ((normal-slot (call-next-method))\n     (direct-slots (remove-if-not #'(lambda (slot)\n                      (typep slot 'index-direct-slot-definition))\n                      direct-slots))\n     (direct-slot (first direct-slots)))\n    (when (and direct-slot\n           (or (not (index-direct-slot-definition-class direct-slot))\n           (eql (index-direct-slot-definition-class direct-slot) class)))\n      (setf (index-direct-slot-definition-class direct-slot) class)\n      (with-slots (index index-type index-initargs index-subclasses index-keys\n             index-reader index-values index-mapvalues index-var) direct-slot\n    (when (or index index-type)\n      (let* ((name (slot-definition-name direct-slot))\n         (index-object (make-index-object :index index\n                          :type index-type\n                          :initargs index-initargs\n                          :reader index-reader\n                          :keys index-keys\n                          :values index-values\n                          :mapvalues index-mapvalues\n                          :var index-var\n                          :slots (list name))))\n        (when index-object\n          (push (make-index-holder :class class :slots (list name)\n                       :name name :index index-object\n                       :index-subclasses index-subclasses)\n            (indexed-class-indices class)))))))\n    normal-slot))\n<\/pre>\n<p>A few words of explanation&#8230; First, the function removes all<br \/>\nthe direct slot definitions that don&#8217;t relate to indices. Then, it<br \/>\nwill handle only the direct slot definition pertaining to the<br \/>\ncurrent class (direct slot definitions for superclasses have<br \/>\nalready been handled, and we just have to fetch the created indices<br \/>\nfrom the superclass). It then creates the new index by using<br \/>\nMAKE-INDEX-OBJECT and passing it all the initialization arguments<br \/>\nheld in the direct slot definition. The direct slot definition is<br \/>\nonly used as some kind of proxy between the slot definition written<br \/>\nby the programmer and the actual index object (which are documented<br \/>\nin INDICES.LISP in the same directory). Finally, an index-holder is<br \/>\ncreated and stored in the list of all the indices of the class. The<br \/>\nindices for superclasses are computed in another function called<br \/>\nCOMPUTE-CLASS-INDICES.<\/p>\n<p>I mentionned above that all MOP implementations are slightly<br \/>\ndifferent in some kind of awkward ways. The following code is there<br \/>\nto avoid late binding in CMUCL and AllegroCL (this is necessary to<br \/>\nbe able to compute the correct indices for a new class).<\/p>\n<pre>\n#+allegro\n(defmethod finalize-inheritance :after ((class indexed-class))\n  (compute-class-indices class (indexed-class-index-definitions class))\n  (reinitialize-class-indices class))\n<\/pre>\n<pre>\n;;; avoid late instantiation\n<\/pre>\n<pre>\n#+(or allegro cmu)\n(defmethod initialize-instance :after ((class indexed-class) &amp;key &amp;allow-other-keys)\n  (compute-class-indices class (indexed-class-index-definitions class))\n  (reinitialize-class-indices class))\n<\/pre>\n<pre>\n#+(or allegro cmu)\n(defmethod reinitialize-instance :after ((class indexed-class) &amp;key &amp;allow-other-keys)\n  (compute-class-indices class (indexed-class-index-definitions class))\n  (reinitialize-class-indices class))\n<\/pre>\n<p>Finally, we add the new slot DESTROYED-P, which is true if the<br \/>\nobejct has been destroyed (but has not been garbage collected yet).<br \/>\nThis slot is very important. When it is set, every slot access<br \/>\nthrows an error.<\/p>\n<pre>\n(defmethod compute-slots ((class indexed-class))\n  (let* ((normal-slots (call-next-method)))\n    (let ((destroyed-p-slot #.`(make-instance 'index-effective-slot-definition\n                :name 'destroyed-p\n                :initform nil\n                :class class\n                #+cmu\n                ,@'(:readers nil :writers nil)\n                :initfunction #'(lambda () nil))))\n      (cons destroyed-p-slot normal-slots))))\n<\/pre>\n<p><strong>To be continued&#8230;<\/strong><\/p>\n<p>This is the end of this first posting of which will be a longer<br \/>\nserie than expected. The next posting will continue going through<br \/>\nthe Indices code where we left it. We will then see how to handle<br \/>\nslot access, and how to reinitialize indexed objects and indexed<br \/>\nclasses.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ll try to write this blog entry in english. I&#8217;m not very used to writing in english though, so please bear with me \ud83d\ude42 I will try to show how to use the Metaobject Protocol for CLOS (the Lisp OO &hellip; <a href=\"https:\/\/blogs.bl0rg.net\/netzstaub\/2005\/03\/08\/a-short-practical-overview-of-the-metaobject-protocol\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19,3],"tags":[],"class_list":["post-118","post","type-post","status-publish","format-standard","hentry","category-english","category-lispstaub"],"_links":{"self":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts\/118","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/comments?post=118"}],"version-history":[{"count":0,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/posts\/118\/revisions"}],"wp:attachment":[{"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/media?parent=118"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/categories?post=118"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.bl0rg.net\/netzstaub\/wp-json\/wp\/v2\/tags?post=118"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}