mop: Implement a class which automatically defines a predicate.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 30 Apr 2006 02:48:20 +0000 (03:48 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 30 Apr 2006 12:29:57 +0000 (13:29 +0100)
I've found myself typing

  (defgeneric foop (thing)
    (:method (((thing foo)) t))
    (:method (((thing t)) nil)))

too many times.  Now I just put `(:predicate foop)' in the class
definition and forget about it.

mdw-mop.lisp

index dee8ca4..0766e38 100644 (file)
@@ -33,6 +33,7 @@
           #:filtered-slot-class-mixin
             #:filtered-direct-slot-definition
             #:filtered-effective-slot-definition
+          #:predicate-class-mixin
           #:abstract-class-mixin #:instantiate-abstract-class
           #:mdw-class #:abstract-class
           #:print-object-with-slots))
                    class object slot))
 
 ;;;--------------------------------------------------------------------------
+;;; Predicates.
+
+(defclass predicate-class-mixin (compatible-class)
+  ((predicates :type list :initarg :predicate :initform nil
+              :documentation "Predicate generic function to create."))
+  (:documentation
+   "Class which can automatically generate a predicate generic function.
+    Adds the `:predicate' class option, which takes a single symbol argument
+    FUNC.  If specified, and non-nil, a generic function FUNC with one
+    argument will be defined (if it doesn't already exist) with a default
+    method returning nil, and a method added specialized on this class
+    returning a non-nil value."))
+
+(defmethod shared-initialize :after
+    ((class predicate-class-mixin) slot-names &key &allow-other-keys)
+  (declare (ignore slot-names))
+  (with-slots (predicates) class
+    (dolist (predicate predicates)
+      (let ((lambda-list '(thing)))
+       (let ((gf (if (fboundp predicate)
+                     (fdefinition predicate)
+                     (let ((gf (ensure-generic-function
+                                predicate :lambda-list lambda-list)))
+                       (add-method gf (make-instance
+                                       'standard-method
+                                       :specializers (list (find-class 't))
+                                       :lambda-list lambda-list
+                                       :function (constantly nil)))))))
+         (add-method gf (make-instance 'standard-method
+                                       :specializers (list class)
+                                       :lambda-list lambda-list
+                                       :function (constantly t))))))))
+
+;;;--------------------------------------------------------------------------
 ;;; Abstract classes.
 
 (defclass abstract-class-mixin (compatible-class)
 ;;; Useful classes.
 
 (defclass mdw-class (filtered-slot-class-mixin
+                    predicate-class-mixin
                     compatible-class)
-  ())
+  ()
+  (:documentation
+   "A generally useful metaclass with handy features.  If I've done the
+    hacking right, there shouldn't be a significant cost to using this
+    metaclass for all your classes if you don't use any of its fancy
+    features."))
 
 (defclass abstract-class (mdw-class abstract-class-mixin) ())