chiark / gitweb /
src/class-*.lisp: Improve metaclass selection.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 26 Mar 2017 13:41:40 +0000 (14:41 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Fri, 8 Jun 2018 18:58:28 +0000 (19:58 +0100)
Replace the slightly wobbly (and poorly named) `maximum' function (in
`class-finalize-impl.lisp') with the much more capable
`select-minimal-class-property' (in `class-utilities.lisp').

Use this new function as appropriate.

doc/SYMBOLS
doc/meta.tex
src/class-finalize-impl.lisp
src/class-make-proto.lisp
src/class-utilities.lisp

index f78c310508c781f141aa58a4d70913d402e0accb..17eb597b316b9decff8db46b42de9bbbf5ea975c 100644 (file)
@@ -312,6 +312,7 @@ class-utilities.lisp
   make-inheritance-path-reporter-state          function
   message-macro-name                            function
   report-inheritance-path                       function
+  select-minimal-class-property                 function
   sod-subclass-p                                function
   valid-name-p                                  function
   vtable-name                                   function
index 92fac6dd8f066499591ed7dde3a292127ee80dc8..25236427555e4c8a18344e9ef08fc66c4ea018e7 100644 (file)
 \begin{describe}{fun}{report-inheritance-path @<state> @<super>}
 \end{describe}
 
+\begin{describe}{fun}
+    {select-minimal-class-property
+        \=@<supers> @<key> @<order> @<default> @<what> \\
+        \>\&key :present :allow-empty
+      \nlret @<object>}
+\end{describe}
+
 \begin{describe}{fun}
     {sod-subclass-p @<class-a> @<class-b> @> @<generalized-boolean>}
 \end{describe}
index 32bc29b812037e8663fc19fb67b89e5177da8248..d5dd60d15ca2063e56e3d98e10609c2b883381bb 100644 (file)
@@ -296,14 +296,6 @@ (defmethod compute-chains ((class sod-class))
 ;;;--------------------------------------------------------------------------
 ;;; Metaclasses.
 
-(defun maximum (items order what)
-  "Return a maximum item according to the non-strict partial ORDER."
-  (reduce (lambda (best this)
-           (cond ((funcall order best this) best)
-                 ((funcall order this best) this)
-                 (t (error "Unable to choose best ~A" what))))
-         items))
-
 (defmethod guess-metaclass ((class sod-class))
   "Default metaclass-guessing function for classes.
 
@@ -314,13 +306,12 @@ (defmethod guess-metaclass ((class sod-class))
   ;; metaclasses resolved yet.  If we find this, then throw `bootstrapping'
   ;; so that `shared-initialize' on `sod-class' can catch it (or as a shot
   ;; across the bows of anyone else who calls us).
-  (maximum (mapcar (lambda (super)
-                    (if (slot-boundp super 'metaclass)
-                        (slot-value super 'metaclass)
-                        (throw 'bootstrapping nil)))
-                  (sod-class-direct-superclasses class))
-          #'sod-subclass-p
-          (format nil "metaclass for `~A'" class)))
+  (select-minimal-class-property (sod-class-direct-superclasses class)
+                                (lambda (super)
+                                  (if (slot-boundp super 'metaclass)
+                                      (slot-value super 'metaclass)
+                                      (throw 'bootstrapping nil)))
+                                #'sod-subclass-p class "metaclass"))
 
 ;;;--------------------------------------------------------------------------
 ;;; Sanity checking.
index d075304787914bd94055251078a470cb34c61013..0e3c5d74f2c91f245265a31c0f0c6177669171fa 100644 (file)
@@ -46,12 +46,13 @@ (defun make-sod-class (name superclasses pset &optional location)
   (with-default-error-location (location)
     (let* ((pset (property-set pset))
           (best-class (or (get-property pset :lisp-metaclass :symbol nil)
-                          (if superclasses
-                              (maximum (mapcar #'class-of superclasses)
-                                       #'subtypep
-                                       (format nil "Lisp metaclass for ~A"
-                                               name))
-                              'sod-class)))
+                          (select-minimal-class-property
+                           superclasses #'class-of #'subtypep 'sod-class
+                           "Lisp metaclass"
+                           :present (lambda (class)
+                                      (format nil "`~S'"
+                                              (class-name class)))
+                           :allow-empty t)))
           (class (make-instance best-class
                                 :name name
                                 :superclasses superclasses
index 35c6d1781a6f23c8e0d85e858dd47ce7f3757ace..38cb75ef073f422c82fb26ecb2d89e5433da9e02 100644 (file)
@@ -105,6 +105,51 @@ (defun report-inheritance-path (state super)
                             of `~A', defined here"
                            super sub)))))
 
+;;;--------------------------------------------------------------------------
+;;; Metaclass inference.
+
+(export 'select-minimal-class-property)
+(defun select-minimal-class-property (supers key order default what
+                                     &key (present (lambda (x)
+                                                     (format nil "`~A'" x)))
+                                          allow-empty)
+  "Return the minimal partially-ordered key from the SUPERS.
+
+   KEY is a function of one argument which returns some interesting property
+   of a class.  The keys are assumed to be partially ordered by ORDER, a
+   function of two arguments which returns non-nil if its first argument
+   precedes its second.  If there is a unique minimal key then return it;
+   otherwise report a useful error and pick some candidate in an arbitrary
+   way; the DEFAULT may be chosen if no better choices are available.  If
+   ALLOW-EMPTY is non-nil, then no error is reported if there are no SUPERS,
+   and the DEFAULT choice is returned immediately.
+
+   In an error message, the keys are described as WHAT, which should be a
+   noun phrase; keys are filtered through PRESENT, a function of one
+   argument, before presentation.
+
+   The function returns two values: the chosen value, and a flag which is
+   non-nil if it was chosen without errors."
+
+  (let ((candidates (partial-order-minima (mapcar key supers) order)))
+    (cond ((and (null candidates) allow-empty)
+          (values default t))
+         ((and candidates (null (cdr candidates)))
+          (values (car candidates) t))
+         (t
+          (cerror* "No obvious choice for implicit ~A: ~
+                    ~{~#[root classes must specify explicitly~:;~
+                         candidates are ~
+                         ~#[~;~A~;~A and ~A~:;~@{~A, ~#[~;and ~A~]~}~]~]~:}"
+                   what (mapcar present candidates))
+          (dolist (candidate candidates)
+            (let ((super (find candidate supers :key key)))
+              (info-with-location super
+                                  "Direct superclass `~A' defined here ~
+                                   has ~A ~A"
+                                  super what (funcall present candidate))))
+          (values (if candidates (car candidates) default) nil)))))
+
 ;;;--------------------------------------------------------------------------
 ;;; Miscellaneous useful functions.