;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; File - dependents-in-lisp-answers.lisp
;; Description - Dependents in Lisp (requires Iterators in Lisp)
;; Author - Tim Bradshaw (tfb at lostwithiel.tfeb.org)
;; Created On - Thu Jun 29 15:02:25 2000
;; Last Modified On - Sat Jan 27 10:15:57 2001
;; Last Modified By - Gail Anderson (ga at lostwithiel)
;; Update Count - 3
;; Status - Unknown
;;
;; $Id: dependents-in-lisp-answers.lisp,v 1.1.1.1 2002/12/12 02:15:47 colin Exp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Copyright 2000,2001 Cley Limited
;;; Note that here we build on the collectors and iterators defined for the
;;; Iterator design pattern
;;;; * Dependents & dependencies.
;;;
;;; Using the objects and methods we defined for the iterator pattern,
;;; implementing objects with dependents, & objects with dependencies
;;; is very simple. The only trick is that we should work by having
;;; the collection object be a slot of the class rather than
;;; inheriting from it, which allows us to have objects which have
;;; both dependents and dependencies.
;;;
;;; We also paramaterise the kind of collection to use by defining a
;;; GF on the class which returns a new, empty collection, as an
;;; example of the technique.
;;;
;;; Things with dependents
;;;
;;; We dont implement the whole collection thing here, to save space.
(defgeneric add-dependent (dm dependent &optional recursivep)
;; see below for the optional args
(:documentation
"Add DEPENDENT as a dependent of DM. Return DM"))
(defgeneric delete-dependent (dm dependent &optional recursivep)
(:documentation
"Remove DEPENDENT from DM. Return DM"))
;;; No DELETE-DEPENDENT-IF
(defgeneric map-dependents (f dm)
(:documentation
"Map F over the dependents of DM. Return DM"))
;;; No cursors.
(defgeneric make-collection-for-dependent-mixin (dm))
(defclass dependent-mixin ()
;; something that has dependents. We expose the DEPENDENTS slot.
((dependents :reader dependents-of)))
(defmethod make-collection-for-dependent-mixin ((dm dependent-mixin))
(make-instance 'simple-childed-mixin))
(defmethod initialize-instance :after ((dm dependent-mixin) &key)
(setf (slot-value dm 'dependents)
(make-collection-for-dependent-mixin dm)))
(defmethod add-dependent ((dm dependent-mixin) dependee
&optional recursivep)
(declare (ignorable recursivep))
(add-child (dependents-of dm) dependee)
dm)
(defmethod delete-dependent ((dm dependent-mixin) dependee
&optional recursivep)
(declare (ignorable recursivep))
(delete-child (dependents-of dm) dependee)
dm)
(defmethod map-dependents (f (dm dependent-mixin))
(map-over f (dependents-of dm))
dm)
;;; Things with dependencies
;;;
;;; We don't implement the whole collection thing here, to save space.
(defgeneric add-dependency (dm dependency &optional recursivep)
(:documentation
"Add DEPENDENCY as a something DM depends on. Return DM"))
(defgeneric delete-dependency (dm dependency &optional recursivep)
(:documentation
"Remove DEPENDENCY from DM. Return DM"))
;;; No DELETE-DEPENDENCY-IF
(defgeneric map-dependencies (f dm)
(:documentation
"Map F over the things DM depends on. Return DM"))
;;; No cursors.
(defgeneric make-collection-for-dependency-mixin (dm))
(defclass dependency-mixin ()
;; something that depends on someone
((dependencies :reader dependencies-of)))
(defmethod make-collection-for-dependency-mixin ((dm dependency-mixin))
(make-instance 'simple-childed-mixin))
(defmethod initialize-instance :after ((dm dependency-mixin) &key)
(setf (slot-value dm 'dependencies)
(make-collection-for-dependency-mixin dm)))
(defmethod add-dependency ((dm dependency-mixin) dependency
&optional recursivep)
(declare (ignorable recursivep))
(add-child (dependencies-of dm) dependency)
dm)
(defmethod delete-dependency ((dm dependency-mixin) dependency
&optional recursivep)
(declare (ignorable recursivep))
(delete-child (dependencies-of dm) dependency)
dm)
(defmethod map-dependencies (f (dm dependency-mixin))
(map-over f (dependencies-of dm))
dm)
;;; And finally, we glue everything together! If we make x be a
;;; dependent of y, then, if y is a dependency-mixin, we need to tell
;;; it this. Similarly for deleting, and similarly the other way
;;; around. We do this by defining after methods which specialise on
;;; the *second* argument. We use the obscure RECURSIVEP flag to
;;; prevent uncontrolled recursion causing death.
(defmethod add-dependent :after (dm (dependee dependency-mixin)
&optional recursivep)
(unless recursivep
(add-dependency dependee dm t)))
(defmethod add-dependency :after (dm (dependency dependent-mixin)
&optional recursivep)
(unless recursivep
(add-dependent dependency dm t)))
(defmethod delete-dependent :after (dm (dependee dependency-mixin)
&optional recursivep)
(unless recursivep
(delete-dependency dependee dm t)))
(defmethod delete-dependency :after (dm (dependency dependent-mixin)
&optional recursivep)
(unless recursivep
(delete-dependent dependency dm t)))
;;; The end.