The structure of tinyclos.scm Tony Sidaway, November 6, 2009. Draft. This is a code walkthrough of tinyclos.scm as found in the Chicken 4 egg in early November, 2009. 1. Syntactic sugar is declared up front: User macros slot@, define-class, define-generic, define-method System macros define-primitive-class, define-structure-class, define-tagged-pointer-class, define-extended-procedure-class 2. Then the main documentation comment appears, almost identical to Gregor Kiczales' original. Describes tinyclos as "A very simple CLOS-like language, embedded in Scheme, with a simple MOP." This version ends with the list of methods and omits some chatty stuff from the end that is now obsolete. 3. "Aliases for sys stuff". define-inline statements for basic structure access primitives: %car, %cdr, %cadr, %cddr, %set-car! %set-cdr! %string-length %vector-ref %vector-set!, %vector-length, %structure? %structure-ref, %structure-set! %structure-length, %structure-tag, %closure-ref, %closure-length, %tagged-pointer-data Then more define-inlines for datatype predicates: %null-pointer?, %blob?, %immediate-value?, %undefined?, %unbound?, %pointer?, %tagged-pointer?, %swig-pointer?, %locative? A random number generator: %random-fixnum 4. "Support code". Macro define-unique-object (assigns a gensym symbol to a named identifier) filter-in: like SRFI-1 filter but doesn't share undeleted longest tail fast-getl: getl from the Xerox PARC version, rewritten in C. (define-unique-object not-found-object) getl: a wrapper around fast-getl 5. "A simple topological sort." compute-std-cpl defined here is functionally identical to the one defined in the Xerox PARC version. The main difference are some encapsulation and some inlines. 6. "Method cache support code" method-cache-size, method-caching-enabled, method-cache-tag, method-cache-lookup 7. Another documentation comment from the original. "Then, we need to build what, in a more real implementation, would be the interface to the memory subsystem: instances and entities..." Here it is trimmed to the first paragraph and some comments about implementation choices are added. This change doesn't come from the Barzillay code, which retains the original Kiczales comment in full. Presumably this new item is of Chicken provenance. 8. "Instance" the-slots-of-a-class is defined as: '(direct-supers direct-slots cpl slots nfields field-initializers getters-n-setters name) (8 elements) In the Xerox code, this is initialized much later, and has the value: '(direct-supers direct-slots cpl slots nfields field-initializers getters-n-setters) (7 elements) So no name slot in the Xerox version (which is a real pain) The constant basic-class-instance-size is set to 11. This is the 8 class slot elements plus tag, class and cache. %allocate-instance is defined to allocate the number of slots plus 3 initialized to #f as a structure. Fields 0 and 1 of the new structure are set to 'instance (the structure tag) and the class. %instance? x is defined inline to see if it's a structure with the tag 'instance %instance-class is defined inline to look at field 1 of the structure (the class) %set-instance-class! pushes a value into field 1 of the structure %instance-cache-ref looks at field 2 of the structure (the cache) %instance-cache-set! pushes a value into field 2 of the structure %instance-ref looks at a numbered slot of the structure, adding 3 to get past the tag, the class and the cache %instance-set! pushes a value into a numbered slot of the structure, adding 3 to get past the tag, the class and the cache A record printer is set up: (define-record-printer (instance x out) (print-object x out)) 9. "Entity" (define-unique-object entity-tag) %allocate-entity is defined. It makes a structure of the number of slots plus 5. The 5 extra fields are: tag ('entity), procedure (which is stocked with a default proc that raises an error), class, name and cache. The closure is copied byte-by-byte into a new closure. This is pretty freaky code. %entity? predicate is defined to check for the characteristics created by the above weird stuff. %entity-def is defined to look at the closure pointer of the entity %set-entity-proc! pushes a procedure into field 1 of the entity %entity-class looks at field 2 of the entity %entity-name looks at field 3 of the entity %set-entity-name! pushes a value into field 3 of the entity %entity-cache-ref looks at field 4 of the entity %entity-cache-set! pushes a value into field 4 of the entity %entity-ref looks at a numbered slot of the entity, adding 5 to get past tag, procedure, class name and cache. %entity-set! pushes a value into a numbered slot of the entity, adding 5 to get past tag, procedure, class name and cache. A record printer is set up: (define-record-printer (entity x out) (print-object x out)) 10. "Instance/Entity field accessors" get-field and set-field! do the right thing, depending on whether they're given an instance or an entity to deal with. 11. Definition of two procedures, delete1!, any1 which are used in the class helper routines 12. Inline definitions of clsmapelm-tst, clsmapelm-cls, clsmapelm-tst-set!, clsmapelm-cls-set!, clsmapelm-tst? clsmapelm-cls-of/prd?, clamapelm-cls-of/eql?, clsmapelm-cls-of/prd, clsmapelm-cls-of/eql, clsmap-add, clsmap-update, clsmap-cls-of/prd clsmapelm-cls-of-eql, clsmap-del/tst, clsmap-del/cls 13. "Primitive class-of extensions", "Structure class-of extensions", "Tagged-pointer class-of extensions" and "Extended-procedure class-of extensions" define-inlines for primitive-class-of, delete-primitive-class-of, update-primitive-class-of define-inlines for structur-class-of, delete-structure-class-of, update-structure-class-of define-inlines for tagged-pointer-class-of, deleted-tagged-pointer-class-of, update-tagged-pointer-class-of define-inlines for extended-procedure-lambda-decoration, procedure-class-of, delete-extended-procedure-class-of, update-extended-procedure-class-of 14. class-of definition. 15. Documentation comment: "Now we can get down to business. First, we initialize the braid...." This is from the Xerox version. It introduces the bootstrap version of make. 16. make, bootstrap version. Substantially similar to the original Xerox version, no essential change. getters-n-setters produces procedures that handle multiple values (a R5RS change). Along with the other Xerox fields, name is set, and then the instance cache is set to a random value. In the generic initialization, a possible name is taken account of. An else clause has been added to signal an error if the class is not recognised. 17. slot-ref and slot-set! These are introduced by exactly the same documentation comment as in the Xerox version. slot-set! is the same, but slot-ref has been hacked to provide access to the class-related slots--direct-supers, cpl, name, etc, and it may also perform a cache lookup using the %instance-cache-ref procedure. slot-ref is also turned into a "getter-with-setter" in the Chicken version. lookup-slot-into is also defined, and is identical to the Xerox version except for use of inlined cdr. 18. A Xerox documentation comment block: " Given that the early version of MAKE is allowed to call accessors on class metaobjects, the definitions for them come here, before the actual class definitions, which are coming up right afterwards." This is followed (as in the original) by definitions of the procedures class-direct-slots, class-direct-supers, etc. An eval-when (compile) is used to define some inline accessors class-direct-slots, class-direct-supers, etc. 19. Definition of the class the-slots-of-a-class was originally defined here (see note 8 above). getters-and-setters-for-class, , , are defined. Changes: getters-and-setters-for-class totally rewritten (the original version was annotated to comment that it was a silly workaround for a bug in MIT Scheme). 20. Both and are now given names (unsurprisingly, "top" and "object"). 21. The slots of the class are set. The code to do this is different from that in the Xerox version (it uses slot-set! on named slots, this code uses %instance-set on numbered slots). The instance cache of is set to a random value. 22. , , and are defined. They are identical to the Xerox code except for the initialization of the new "name" slot. 23. Some user procedures are added: "These are the convenient syntax we expose to the base-level user." make-class, make-generic, make-method. They differ from the Xerox code only in permitting classes and generics to be given an optional name. 24. "The initialization protocol" The following generics are defined: initialize instances: allocate-instance, compute-getter-and-setter classes: compute-cpl, compute-slots generics: compute-apply-generic, compute-methods, compute-method-more-specific?, compute-apply-methods These are identical to the Xerox code except for the addition of names. 25. "The next thing to do is bootstrap generic functions." This takes the form of the following steps: i. generic-invocation-generics is defined as a list of methods used for invoking generics ii. add-method is defined iii. A dummy entity procedure is set up for compute-apply-generic iv. The following methods are defined for the class : compute-apply-generic, compute-methods, compute-method-more-specific?, compute-apply-methods v. The predicates applicable? and more-specific? are defined vi. initialize methods are defined for classes , , , , vii. allocate-instance methods are defined for the classes and viii. compute-cpl, compute-slots, and compute-getter-and-setter are defined for the class ix. The real make is defined. Considerable hacking has taken place in the above since the Xerox versions. 26. "Now define what CLOS calls `built in' classes." The primitive classes are defined. The primitive classes in Chicken are much broader than those in the Xerox code, and some effort is made to provide sensible initializers and some utility procedures such as make-structure-class, make-port-class, etc. 27. At this point the original Xerox code ended. The bootstrapping process was complete. 28. The flag method-caching-enabled is switched to #t 29. "Utilities" The procedure initialize-slots is defined, and two generics are added: print-object and define-object. Methods for these generics are added for the classes , , and , and a further print-object method is added for . The following procedures are defined: ensure-generic, add-global-method, instance?, subclass?, instance-of?, make-instance-from-pointer, make/copy 30. "Procedural interface for extending the "builtin" class system" The following procedures are defined: new-primitive-class, delete-primitive-class, new-structure-class, delete-structure-class, new-tagged-pointer-class, delete-tagged-pointer-class, new-extended-procedure-class, delete-extended-procedure-class 31. That's all folks!