CL-COMPONENT 0.9.3.X USERS'S MANUAL ~ WORKING DRAFT FOR INTRODUCTION OF DEF-COMPONENT-CLASS 1 INTODUCTION CL-COMPONENT is a "simple CLOS solution for directed acyclic graphs". It's goal is to make dealing with dags of CLOS instances trivial in Common Lisp. CL-Component provides a macro DEF-COMPONENT-CLASS, which defines a new component class and rolls the functions and methods necessary to create, operate on, and inspect dags of this new component class (a "component class" is a subclass of the CLOS class COMPONENT). CL-Component attempts to address some of the problems described in "The Description of Complex Systems"[Pitman 84] in order to provide a framework for designing better systems definition facilities. It is also intended as a useful utility for rolling object systems, inference engines, and scheduling utilities. [Pitman 84] Pitman, "The Description of Complex Systems", MIT, AI Memo 801, September, 1984 http://www.nhplace.com/kent/Papers/Large-Systems.html 2 TUTORIAL 2.1 the SIMPLE-COMPONENT class SIMPLE-COMPONENT is CL-Component's pre-packaged component-class. > (make-instance 'simple-component) # As you can see, instance of SIMPLE-COMPONENT print rather opaquely (like a vanilla instance of STANDARD-OBJECT). Let's create a subclass of SIMPLE-COMPONENT that's a little easier to work with: (defclass named-component (simple-component) ((name :initarg :name :initform 'anonymous :accessor name-of))) (defmethod print-object ((o named-component) s) (format s "#" (name-of o) (mapcar #'name-of (direct-subs o)))) 2.2 the NAMED-COMPONENT class Ok, now let's populate our lisp image with "named components": (setf x (make-instance 'named-component :name 'x) y (make-instance 'named-component :name 'y)) x => # y => # But what's the fun of making components if they don't have subcomponents? The generic function ADD-DIRECT-SUB establishes the component/subcomponent relationship between two simple components. It makes the second a "supcomponent" of the first, then returns the first. (add-direct-sub x y) => # DELETE-DIRECT-SUB removes this relationship (delete-direct-sub x y) => # (add-direct-super x y) is equivalent to (add-direct-sub y x). Likewise (delete-direct-super x y) is equivalent to (delete-direct-sub y x). ADD-DIRECT-SUB, ADD-DIRECT-SUPER, DELETE-DIRECT-SUB, and DELETE-DIRECT-SUPER can take a list for either argument. (setf z1 (make-instance 'named-component :name 'z1) z2 (make-instance 'named-component :name 'z2)) (add-direct-sub (list x y) (list z1 z2)) => (# #) 2.3 the inspectors Let's make a more intertangled population: (setf c (make-instance 'named-component :name 'c) b1 (make-instance 'named-component :name 'b1 :direct-subs (list c)) b2 (make-instance 'named-component :name 'b2) b3 (make-instance 'named-component :name 'b3) a (make-instance 'named-component :name 'a :direct-subs (list b1 b2 b3))) As you can see, we used a new keyword parameter to (MAKE-INSTANCE 'SIMPLE-COMPONENT...): :DIRECT-SUBS. The argument to :DIRECT-SUBS should be an instance of SIMPLE-COMPONENT or a list of instance of SIMPLE-COMPONENT. The newly instantiated component is now a supercomponent of the component (or components) supplied to :DIRECT-SUBS. So now we have a hierarchy of named components that looks like this: supercomponents A /|\ B1 B2 B3 | C subcomponents The function SUBS returns a list of all the subcomponents of a simple-component. (subs a) => (# # # # ()) DIRECT-SUBS returns a list of just the subcomponents *directly* inherited by the component: IE, those specifically declared by ADD-DIRECT-SUBS or the :DIRECT-SUBS. (direct-subs a) => (# # #) INDIRECT-SUBS returns a list of just the *indirectly* inherited subcomponents: IE, the cumulative "subs" of a component's "direct subs". (indirect-subs a) => (# The functions SUPERS, DIRECT-SUPERS, and INDIRECT-SUPERS all behave as expected, except going *up* the component hierarchy--towards the supercomponents--instead of down it towards the subcomponents. The list returned by DIRECT-SUBS and DIRECT-SUPERS is gauranteed to be in the order that the sub/supercomponents were last assigned to the component. There is no guarantee of the order of the list returned by SUBS, SUPERS, INDIRECT-SUBS, or INDIRECT-SUPERS. 2.4 The :EXTEND keyword All of the inspectors described in 2.3 take an :EXTEND keyword argument. :EXTEND must be a proper list, and defaults to NIL. The list returned by these functions is created by pushing components onto :EXTEND. (defun our-indirect-subs (component) (let (xx) (dolist (c (direct-subs component) xx) (setq xx (subs c :extend xx))))) 2.5 topological sorting The function TOPOLOGICAL-SORT takes a list and destructively reorders it so that no component ever appears before one of it's supercomponents. (topological-sort (list c b1 a)) => (# # #) RTOPOLOGICAL-SORT sorts the list so that no component ever appears before one of it's subcomponents. (rtopological-sort (list a b1 c)) => (# # #) The inspectors described in 2.3 all have topological and rtopological equivalents, which return lists sorted in topological or rtopological order, respectively. The following functions are the "topological inspectors": TOPOLOGICAL-SUBS TOPOLOGICAL-SUPERS TOPOLOGICAL-DIRECT-SUBS TOPOLOGICAL-DIRECT-SUPERS TOPOLOGICAL-INDIRECT-SUBS TOPOLOGICAL-INDIRECT-SUPERS (the following are the "reverse topological inspectors") RTOPOLOGICAL-SUBS RTOPOLOGICAL-SUPERS RTOPOLOGICAL-DIRECT-SUBS RTOPOLOGICAL-DIRECT-SUPERS RTOPOLOGICAL-INDIRECT-SUBS RTOPOLOGICAL-INDIRECT-SUPERS So (topological-subs a) Is equivalent to (topological-sort (subs a)) but (topological-subs x) is much cheaper than (topological-sort (subs x)) 2.6 the :MUTATE keyword The topological inspectors take a :MUTATE keyword argument instead of :EXTEND. :MUTATE is just like :EXTEND except that it destructively mutates it's argument during the sorting process. It should be noted that although the list returned by (topological-subs a) Is equivalent to the list returned by (topological-sort (subs a)) the list returned by (topological-subs c :mutate (list x)) will not always be equivalent to the list returned by (topological-sort (subs c :extend x)) because, although new components are added to :MUTATE in correct topological order, the contents of :MUTATE are not gauranteed to be re-ordered topologically (but that doesn't mean it won't be... think of the dirty HyperSpec word "undefined"...) 2.7 predicates The predicates SUBP SUPERP DIRECT-SUBP DIRECT-SUPERP INDIRECT-SUBP INDIRECT-SUPERP TOPOLOGICALP RTOPOLOGICALP all exist and behave as expected. The predicate SIMPLE-COMPONENT-P returns true if it's argument is an instance of SIMPLE-COMPONENT. 2.8 the :KEY keyword argument All the aforementioned predicates and inspectors take an optional :KEY keyword argument that behaves like the ubiquitus CL keyword parameter of the same name. 2.9 DEF-COMPONENT-CLASS None of the functions described in the previous sections were actually coded by hand. They were all rolled with the macro DEF-COMPONENT-CLASS. There are two different possible syntaxes for DEF-COMPONENT-CLASS, a short form and a long form. The short form takes the name of a new component class (a new subclass of COMPONENT), a CLOS superclass list, and a list of possible options. The DEF-COMPONENT-CLASS component-class options have a syntax similar to the slot- or class- options of a DEFCLASS form. 2.9.1 the "simple" component-class options The following component-class options :subs-fn :supers-fn :direct-subs-fn :direct-supers-fn :indirect-subs-fn :indirect-supers-fn :topological-subs-fn :topological-supers-fn :rtopological-subs-fn :rtopological-supers-fn :topological-direct-subs-fn :topological-direct-supers-fn :rtopological-direct-subs-fn :rtopological-direct-supers-fn :topological-indirect-subs-fn :topological-indirect-supers-fn :rtopological-indirect-subs-fn :rtopological-indirect-supers-fn :topological-sort-fn :rtopological-sort-fn :subs-pred :supers-pred :direct-subs-pred :direct-supers-pred :indirect-subs-pred :indirect-supers-pred :topological-subs-pred :topological-supers-pred :rtopological-subs-pred :rtopological-supers-pred :topological-direct-subs-pred :topological-direct-supers-pred :rtopological-direct-subs-pred :rtopological-direct-supers-pred :topological-indirect-subs-pred :topological-indirect-supers-pred :rtopological-indirect-subs-pred :rtopological-indirect-supers-pred :topological-sort-pred :rtopological-sort-pred :add-direct-sub-fn :add-direct-super-fn :delete-direct-sub-fn :delete-direct-super-fn :class-pred create functions that are equivalent to those described in previous sections, but operate on instance of the newly created component-class (instead of instances of SIMPLE-COMPONENT). These component-class options have the following syntax: (option name &optional lambda-list) OPTION is the component-class option keyword, NAME is the name of the new function, and LAMBDA-LIST is the lambda list of the new function (which must be congruent with the way the functions were described in the previous sections. 2.9.2 the :SHARED-INITIALIZE-INITARGS component-class option The component-class option :SHARED-INITIALIZE-INITARGS takes a plist of symbols that correspond alternatingly to (a) new keyword parameters added to SHARED-INITIALIZE when initializing instances of this class and (b) the function indicators of binary functions that take as arguments (1) the newly "initialized" instance and (2) the keyword parameter's argument when the instance is initialized. Thus in this example: (def-component-class... ... (:shared-initialize-initargs foo do-foo bar do-bar) ... new keyword parameters :FOO and :BAR are added to SHARED-INITIALIZE (and thus also to MAKE-INSTANCE, REINITIALIZE-INSTANCE, and CHANGE-CLASS) by an :AFTER method to SHARED-INITIALIZE for instance of this component-class. This :AFTER method [A] calls the binary funcion DO-FOO with (1) the "initialized" component and (2) the argument given to the new FOO keyword paramater [B] calls DO-BAR with (1) the "initialized" component and (2) the arguments given to the keyword parameter BAR. 2.9.3 the :VERBOSE component-class option If the class option :VERBOSE is given and is not null then DEF-COMPONENT-CLASS noisily announces it's actions to *STANDARD-OUTPUT*. 2.9.4 the :SUBS-ACCESSOR and :SUPERS-ACCESSOR component-class options The class options :SUBS-ACCESSOR and :SUPERS-ACCESSOR refer to the name of the slot-accessors for component class's `dirct sub' and `direct super' component slots. Any form given to the shortform that does not match these descriptions is assumed to be a class option and plugged into the resultant DEFCLASS form. All component-class options are optional in the short form. 2.9.5 the DEF-COMPONENT-CLASS long form The DEF-COMPONENT-CLASS long form looks just like the DEFCLASS with component-class options mixed in with the class options. The DEF-COMPONENT-CLASS short form (def-component-class my-component (foo) (:subs-fn my-subs) (:supers-fn my-supers)) expands to (def-component-class my-component (foo) ((#:g1 :accessor #:g2) (#:g3 :accessor #:g4)) (:subs-accessor #:g2) (:supers-accessor #:g4) (:subs-fn my-subs) (:supers-fn my-supers)) It is an error to call the long form without the :SUBS-ACCESSOR and :SUPERS-ACCESSOR component-class options. The short form populates these component-class options with gensyms when they are not supplied. 2.10 the "accessor" abstraction and the COMPONENT-*-XX functions All inspectors and predicates mentioned in previous sections are associated with a more general "component-*-xx" function that DEF-COMPONENT-CLASS uses to roll new functions. The *-xx inspectors are: component-xx component-direct-xx component-indirect-xx component-topological-xx component-topological-direct-xx component-topological-indirect-xx component-rtopological-xx component-rtopological-direct-xx component-rtopological-indirect-xx topological-xx and The *-xx predicates are: component-xxp component-direct-xxp component-indirect-xxp component-topological-xxp component-topological-direct-xxp component-topological-indirect-xxp component-rtopological-xxp component-rtopological-direct-xxp component-rtopological-indirect-xxp component-topological-push-xxp component-topological-enqueue-xxp The *-xx functions are identical to their SIMPLE-COMPONENT equivalents described in previous sections except that they have an additional parameter prepended to their lambda-lists: ACCESSOR. ACCESSOR is a unary function that should behave semantically as if it were a slot accessor responsible for finding the direct sub- or super-components of the component it is called on. (defun our-subs (component &key key extend) (component-xx #'simple-component-direct-subs component :key key :extend extend)) SIMPLE-COMPONENT-DIRECT-SUBS and SIMPLE-COMPONENT-DIRECT-SUPERS are the "direct subs" and "direct supers" accessor for the SIMPLE-COMPONENT class. caveat: SIMPLE-COMPONENT-DIRECT-SUBS and SIMPLE-COMPONENT-DIRECT-SUPER are provided as part of CL-COMPONENT's meta protocol. They are not intended to be called casually. The results of mutating the list returned by these functions is undefined and probably very bad. The *-XX functions are useful for rolling radical new inspectors for subclasses of SIMPLE-COMPONENT. (defun lazy-subs (component) (component-xx #'(lambda (c) (if (lazy-component-p c) (compute-lazy-direct-subs c) (simple-component-direct-subs c))) ccomponent)) 2.11 LOAD-DIRECT-SUBCOMPONENT, UNLOAD-DIRECT-SUBCOMPONENT, COMPONENT-COMPARE-WORLD, and the CIRCULAR-COMPONENT confition The functions created by the component-class options :add-direct-sub-fn and :add-direct-super-fn (for the component-class SIMPLE-COMPONENT: ADD-DIRECT-SUB and ADD-DIRECT-SUPER) call the following three generic functions: COMPONENT-COMPARE-WORLD, UNLOAD-DIRECT-SUBCOMPONENT, and LOAD-DIRECT-SUBCOMPONENT. COMPONENT-COMPARE-WORLD takes three arguments: COMPONENT, DIRECT-SUBCOMPONENT, and RELATIONSHIP. COMPONENT-COMPARE-WORLD will signal an error of the type CIRCULAR-COMPONENT if DIRECT-SUBCOMPONENT is a supercomponent of COMPONENT of the given RELATIONSHIP. RELATIONSHIP should be the name of the component class who's DEF-COMPONENT-CLASS form created the method. This is where the method dispatch is. The CIRCULAR-COMPONENT condition has three slots, :COMPONENT, :OFFENDING-COMPONENT (which are the first and second arguments to COMPONENT-COMPARE-WORLD, respectively), and RELATIONSHIP. They are accessible with the accessor CIRCULAR-COMPONENT-COMPONENT, CIRCULAR-COMPONENT-OFFENDING-COMPONENT, and CIRCULAR-COMPONENT-RELATIONSHIP. The value returned by COMPONENT-COMPARE-WORLD is undefined. Next, UNLOAD-DIRECT-SUBCOMPONENT is called, followed by LOAD-DIRECT-SUBCOMPONENT. LOAD-DIRECT-SUBCOMPONENT and UNLOAD-DIRECT-SUBCOMPONENT take the same three arguments: COMPONENT, SUBCOMPONENT, and RELATIONSHIP. LOAD-DIRECT-SUBCOMPONENT pushes COMPONENT to the supercomponent list of SUBCOMPONENT and pushes SUBCOMPONENT to the subcomponent list of COMPONENT. UNLOAD-DIRECT-SUBCOMPONENT is the opposite, it deleting COMPONENT from the supercomponent list of SUBCOMPONENT and deleting SUBCOMPONENT from the subcomponent list of COMPONENT. The functions created by :DELETE-DIRECT-SUB-FN and :DELETE-DIRECT-SUPER-FN simply call UNLOAD-DIRECT-SUBCOMPONENT 3 REFERENCE 3.1 systems definitions `bare-cl-component' is the name of the system that creates the :CL-COMPONENT package and the DEF-COMPONENT-CLASS form. The `cl-component' system expands the :CL-COMPONENT package with the SIMPLE-COMPONENT class and all it's utilities. `cl-component' depends on `bare-cl-component' `cl-component-test' creates a package :CL-COMPONENT.TEST that contains the unit tests for `cl-component' and `bare-cl-component'. `cl-component-test' depends on `cl-component' and ACL's Test-Harness or `ptester', the portable version of ACL's Test-Harness[1]. `cl-component-test' is uncluded with `cl-component' `ptester' is not. [1] See http://www.cliki.net/ptester 3.2 symbols exported from the :CL-COMPONENT package by the `bare-cl-component' system 3.2.1 conditions CIRCULAR-COMPONENT slots: COMPONENT ~ initarg: :CIRCULAR-COMPONENT, accessor: CIRCULAR-COMPONENT-COMPONENT OFFENDING-SUBCOMPONENT ~ initarg: :OFFENDING-SUBCOMPONENT: accessor CIRCULAR-COMPONENT-OFFENDING-COMPONENT RELATIONSHIP ~ initarg :RELATIONSHIP, accessor CIRCULAR-COMPONENT-RELATIONSHIP 3.2.2 classes COMPONENT slots: none 3.2.3 macros DEF-COMPONENT-CLASS (name &rest initargs) 3.2.4 functions COMPONENTP COMPONENT-XXP (accessor component xx-component &key key) COMPONENT-DIRECT-XX-P (accessor component xx-direct-component &key key) COMPONENT-INDIRECT-XXP (accessor component xx-indirect-component &key key) XX-TOPOLOGICALP (accessor list &key key) COMPONENT-XX (accessor component &key key extend) COMPONENT-DIRECT-XX (accessor component &key key extend) COMPONENT-INDIRECT-XX (accessor component &key key extend) COMPONENT-TOPOLOGICAL-XX (accessor component &key key mutate) COMPONENT-TOPOLOGICAL-DIRECT-XX (accessor component &key key mutate) COMPONENT-TOPOLOGICAL-INDIRECT-XX (accessor component &key key mutate) COMPONENT-RTOPOLOGICAL-XX (accessor component &key key mutate) COMPONENT-RTOPOLOGICAL-DIRECT-XX (accessor component &key key mutate) COMPONENT-RTOPOLOGICAL-INDIRECT-XX (accessor component &key key mutate) TOPOLOGICAL-SORT-XX (accessor list &key key) RTOPOLOGICAL-SORT-XX (accessor list &key key) 3.2.5 generic functions COMPONENT-COMPARE-WORLD (component direct-subcomponent relationship) LOAD-DIRECT-SUBCOMPONENT (component subcomponent relationship) UNLOAD-DIRECT-SUBCOMPONENT (component subcomponent relationship) 3.3 Symbols exported from the :CL-COMPONENT package by the `cl-component' systsm 3.3 .1 SIMPLE-COMPONENT slots: #:anonymous-subs-slot ~ initform: NIL, accessor SIMPLE-COMPONENT-DIRECT-SUBS #:anonymous-super-slot ~ initform: NIL, accessor SIMPLE-COMPONENT-DIRECT-SUPERS initarg-like behaviour from SHARED-INITIALIZE :AFTER method: :DIRECT-SUBS 3.3.2 functions SUBP (component subcomponent &key key) SUPERP (component supercomponent &key key) DIRECT-SUBP(component direct-subcomponent &key key) DIRECT-SUPERP (component direct-supercomponent &key key) INDIRECT-SUBP (component indirect-subcomponent &key key) INDIRECT-SUPERP (component indirect-subcomponent &key key) TOPOLOGICALP (list &key key) RTOPOLOGICALP (list &key key) SUBS (component &key key extend) SUPERS (component &key key extend) DIRECT-SUBS (component &key key extend) DIRECT-SUPERS (component &key key extend) INDIRECT-SUBS (component &key key extend) INDIRECT-SUPERS (component &key key extend) TOPOLOGICAL-SORT (component &key key) RTOPOLOGICAL-SORT (component &key key) TOPOLOGICAL-SUBS (component &key key mutate) TOPOLOGICAL-SUPERS (component &key key mutate) RTOPOLOGICAL-SUBS (component &key key mutate) RTOPOLOGICAL-SUPERS (component &key key mutate) TOPOLOGICAL-DIRECT-SUBS (component &key key mutate) TOPOLOGICAL-DIRECT-SUPERS (component &key key mutate) RTOPOLOGICAL-DIRECT-SUBS (component &key key mutate) RTOPOLOGICAL-DIRECT-SUPERS (component &key key mutate) TOPOLOGICAL-INDIRECT-SUBS (component &key key mutate) TOPOLOGICAL-INDIRECT-SUPERS (component &key key mutate) RTOPOLOGICAL-INDIRECT-SUBS (component &key key mutate) RTOPOLOGICAL-INDIRECT-SUPERS (component &key key mutate) 3.3.3 generic functions SIMPLE-COMPONENT-DIRECT-SUBS (simple-component) ;slot-accessor SIMPLE-COMPONENT-DIRECT-SUPERS (simple-component) ;slot-accessot ADD-DIRECT-SUB (component* subcomponent*) ADD-DIRECT-SUPER (component* supercomponent*) DELETE-DIRECT-SUB (component* subcomponent*) DELETE-DIRECT-SUPERS (component* supercomponent*) 3.3.4 methods ADD-DIRECT-SUB ((cs list) s) ADD-DIRECT-SUB (c (ss list)) ADD-DIRECT-SUB (c s) ADD-DIRECT-SUPER ((c list) s) ADD-DIRECT-SUPER (c (ss list)) ADD-DIRECT-SUPER (c s) DELETE-DIRECT-SUB ((cs list) s) DELETE-DIRECT-SUB (c (ss list)) DELETE-DIRECT-SUB (c s) DELETE-DIRECT-SUPER ((cs list) s) DELETE-DIRECT-SUPER (c (ss list)) DELETE-DIRECT-SUPER (c s) COMPONENT-COMPARE-WORLD (c s (% (eql 'SIMPLE-COMPONENT))) LOAD-DIRECT-SUBCOMPONENT (c s (% (eql 'SIMPLE-COMPONENT))) UNLOAD-DIRECT-SUBCOMPONENT (c s (% (eql 'SIMPLE-COMPONENT))) SHARED-INITIALIZE ((c simple-component) slotnames &key direct-subs &allow-other-keys) 4 EXAMPLES 4.1 SIMPLE-COMPONENT definition (DEF-COMPONENT-CLASS short form) (def-component-class simple-component (:subs-accessor simple-component-direct-subs) (:supers-accessor simple-component-direct-supers) (:subs-fn subs) (:direct-subs-fn direct-subs) (:indirect-subs-fn indirect-subs) (:topological-subs-fn topological-subs) (:topological-direct-subs-fn topological-direct-subs) (:topological-indirect-subs-fn topological-indirect-subs) (:rtopological-subs-fn rtopological-subs) (:rtopological-direct-subs-fn rtopological-direct-subs) (:rtopological-indirect-subs-fn rtopological-indirect-subs) (:supers-fn supers) (:direct-supers-fn direct-supers) (:indirect-supers-fn indirect-supers) (:topological-supers-fn topological-supers) (:topological-direct-supers-fn topological-direct-supers) (:topological-indirect-supers-fn topological-indirect-supers) (:rtopological-supers-fn rtopological-supers) (:rtopological-direct-supers-fn rtopological-direct-supers) (:rtopological-indirect-supers-fn rtopological-indirect-supers) (:subs-pred subp) (:direct-subs-pred direct-subp) (:indirect-subs-pred indirect-subp) (:topological-subs-pred topological-subp) (:topological-direct-subs-pred topological-direct-subp) (:topological-indirect-subs-pred topological-indirect-subp) (:rtopological-subs-pred rtopological-subp) (:rtopological-direct-subs-pred rtopological-direct-subp) (:rtopological-indirect-subs-pred rtopological-indirect-subp) (:supers-pred superp) (:direct-supers-pred direct-superp) (:indirect-supers-pred indirect-superp) (:topological-supers-pred topological-superp) (:topological-direct-supers-pred topological-direct-superp) (:topological-indirect-supers-pred topological-indirect-superp) (:rtopological-supers-pred rtopological-superp) (:rtopological-direct-supers-pred rtopological-direct-superp) (:rtopological-indirect-supers-pred rtopological-indirect-superp) (:topological-sort-fn topological-sort) (:rtopological-sort-fn rtopological-sort) (:topological-sort-pred topologicalp) (:rtopological-sort-pred rtopologicalp) (:add-direct-sub-fn add-direct-sub) (:delete-direct-sub-fn delete-direct-sub) (:add-direct-super-fn add-direct-super) (:delete-direct-super-fn delete-direct-super) (:shared-initialize-initargs direct-subs add-direct-sub) (:class-pred simple-component-p) (:verbose t)) 4.2 trivial example (defclass creature ()) (defclass mammal (creature) ((xx :initarg :mother :initform nil :accessor mother-of) (xy :initarg :father :initform nil :accessor father-of))) (defun parents-of (mammal) (list (mother-of x) (father-of x))) (def-component-class human (mammal) (:supers-accessor parents-of) (:direct-subs-fn children-of) ;direct-supers-fn is PARENTS (:subs-fn decendents) (:supers-fn ancestors) (:topological-subs-fn chronological-decendents) (:topological-supers-fn chronological-ancestors) (:add-direct-subs-fn add-decendent) (:verbose t)) (defun siblings-of (human) (let (xx) (dolist (p (parents-of human) xx) (setq xx (children-of p :extend xx))))) (defun grandparents-of (human) (let (xx) (dolist (p (parents-of human) xx) (setq xx (parents-of p :extend xx))))) (defun grandchildren-of (human) (let (xx) (dolist (c (children-of human) xx) (setq xx (children-of c :extend xx))))) (defclass male (human) ()) (defclass female (human) ()) (defclass citizen (human) ((name :initarg :name :initform nil :accessor name-of))) (defclass mr (male citizen) ()) (defclass ms (female citizen) ()) (defun procreate (xx xy) (let ((% (make-instance (nth (random 2) '(male female)) :mother xx :father xy))) (add-decendent xx %) (add-decendent xy %) %)) (setf adam (make-instance 'mr :name 'adam) eve (make-instance 'ms :name 'eve)) (let ((oops (procreate adam eve))) (change-class oops (typecase oops (female ms))) (setf (name-of oops) (typecase oops (mr 'steve) (ms 'shirly))) oops) 4.3 trivial example 2 ;;note: see 5.2 "upcoming changes to the API" (def-component-class pre-task (:rtopological-subs-fn ordered-subtasks) (:add-direct-sub-fn add-subtasks) (:shared-initialize-initarg subtasks add-subtasks)) (def-component-class post-task (:rtopological-subs-fn ordered-subtasks) (:add-direct-sub-fn add-subtasks) (:shared-initialize-initarg subtasks add-subtasks)) (defparameter *tasks* nil) (defun find-task (name) (values (gethash name *tasks*))) (defmacro def-task (name &optional subtasks &key then) `(progn (push (make-instance 'task :name ',name :subtasks (mapcar #'find-task '(,@subtasks))) *tasks*) name)) (def-task open-fridge) (def-task close-fridge) (def-task get-milk (open-fridge) :then (put-away-milk)) (def-task use-milk (get-milk) (def-task put-away-milk (open-fridge close-fridge)) (def-task politely-use-milk (use-milk put-away-milk)) (def-task get-eggs (open-fridge)) (def-task use-eggs (get-milk)) (def-task put-away-eggs (open-fridge close-fridge) (def-task politely-use-eggs (use-eggs put-away-eggs)) (def-task turn-on-stove) (def-task turn-off-stove) (def-task use-stove (turn-on-stove)) (def-task use-stove-safely (use-stove turn-off-stove)) (def-task make-pancakes (use-milk-politely use-eggs-politely use-stove-politely) (ordered-subtasks (find-task 'make-pancakes)) 4.4 trivial example 3 (defparameter *path* #"~/src/example-3/") (def-component-class src-file () ((name :initarg :name :accessor :name-of)) (:subs-fn depends-on) (:supers-fn depended-on) (:rtopological-subs-fn topological-depends-on) (:rtopological-subs-fn topological-depended-on) (:add-direct-sub-fn add-dependent) (:shared-initialize-initargs depends-on add-dependent)) (defparameter *src-files* nil) (defun get-src-file (name) (find name *src-files* :key #'name-of :test #'string-equal)) (defmacro def-src (name &optional depends-on) (make-instance 'src-file :name ',name :depends-on (mapcar #'get-src-file ',depends-on))) (defun compile-op (src) (flet ((fn (x) (compile-file (make-pathname :directory *path* :name (name-of x) :type "lisp")))) (mapc #'fn (topological-depends-on src)) (fn src))) (defun compile-op (src) (flet ((fn (x) (load (make-pathname :directory *path* :name (name-of x) :type "fasl")))) (mapc #'fn (topological-depends-on src)) (fn src))) (def-src "package") (def-src "utils" ("package")) (def-src "it" ("utils" "package")) (compile-op "it") (load-op "it") 5 UPCOMING CHANGES TO THE API CL-COMPONENT is an experamental library and will continue to undergo signifigant changes as it matures. It's releases, however, will be always be fully documented and unit tested. As always, input and suggestions are welcome. Feel free to just email me darcs patches (which I may or may not apply) Upcoming changes to look for in future releases: 1. Future releases will include a DEF-COMPONENT-CLASS-OPTION form, and this is how DEF-COMPONENT-CLASS's component-class options will be defined (as opposed to them being defined by a large, monolithic and unextendible macro). 2. More lambda-list keyword changes... There will be a _new_ :TEST keyword argument that will be used to determine if a component has already been pushed to :EXTEND or :MUTATE. (CL-Component is currently stuck with EQ). This functionality is aimed at supporting scheduling compilers/algorithms. The :KEY argument will be modified to support the new :TEST. 3. Ideally there will eventually be support for applying the DEF-COMPONENT-CLASS's utility-rolling functionality to pre-defined, non-component classes. This way you could use CL-COMPONENT to roll inspectors and predicates for your pre-existing dag of CLOS instances. Not sure exactly what this will look like... the first step will be to break (defmethod load-direct-subcomponent (component sub %) (push component (supers-accessor sub)) (push sub (subs-accessor component))) into (defmethod load-direct-subcomponent (component sub %) (push component (supers-accessor sub))) and (defmethod load-direct-supercomponent (component sub %) (push sub (subs-accessor component))) also, component-class option syntax will go from (option name &optional lambda-list) to something like (option name &optional lambda-list &key accessor) Also a DEF-COMPONENT-CLASS form will be able to roll more than one of a given component-class-option (ie a topological-subs-fn with accessor A and also a topological-subs-fn with accessor B) The idea is definitely there to have the functions rolled by a seperate form (instead of DEF-COMPONENT-CLASS) that is called by DEF-COMPONENT-CLASS... ...or by ENSURE-CLASS if :MOP is in the *features* and then the component-class options become class options when DEFCLASS is creating a subclass of COMPONENT... maybe there should be a seperate `cl-component-mop' system that depends on `bare-cl-component' and `closer'? 4. DEF-COMPONENT class will be able to generate accessors for slots that are "inherited" from other nodes based on their relationship in the dag, IE (defun think (topic) (or (king-thinks topic) (prince-thinks topic) (boss-thinks topic) (coworkers-think topic) (news-says topic) (bullshit))) 5. more better sorting. most importantly stable-sorting... 6. hopefully the API gets to the point where use in non-trivial scheduling systems/algorithms is appropriate and a SIMPLE-TASK component class will be included in addition to SIMPLE-COMPONENT