;;;--------------------------------------------------------------------------
;;; 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.
;; 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.
(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
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.
(export 'sod-subclass-p)