;; Superclass Linearization for Dylan' for more detail.
;; http://www.webcom.com/haahr/dylan/linearization-oopsla96.html
+;;; Utilities.
+
+(export 'merge-class-lists)
+(defun merge-class-lists (lists pick)
+ "Merge the LISTS of classes, using PICK to break ties.
+
+ This is a convenience wrapper around the main `merge-lists' function.
+ Given that class linearizations (almost?) always specify a custom
+ tiebreaker function, this isn't a keyword argument. Also, this wrapper
+ provides a standard presentation function so that any errors are presented
+ properly."
+ (merge-lists lists
+ :pick pick
+ :present (lambda (class)
+ (format nil "`~A'" (sod-class-name class)))))
+
;;; Tiebreaker functions.
(defun clos-tiebreaker (candidates so-far)
(remove-duplicates (cons class
(mappend #'superclasses
direct-supers))))))
- (merge-lists (mapcar (lambda (class)
- (cons class
- (sod-class-direct-superclasses class)))
- (superclasses class))
- :pick #'clos-tiebreaker)))
+ (merge-class-lists
+ (mapcar (lambda (class)
+ (cons class (sod-class-direct-superclasses class)))
+ (superclasses class))
+ #'clos-tiebreaker)))
(export 'dylan-cpl)
(defun dylan-cpl (class)
you're going to lose anyway."
(let ((direct-supers (sod-class-direct-superclasses class)))
- (merge-lists (cons (cons class direct-supers)
- (mapcar #'sod-class-precedence-list direct-supers))
- :pick #'clos-tiebreaker)))
+ (merge-class-lists
+ (cons (cons class direct-supers)
+ (mapcar #'sod-class-precedence-list direct-supers))
+ #'clos-tiebreaker)))
(export 'c3-cpl)
(defun c3-cpl (class)
(let* ((direct-supers (sod-class-direct-superclasses class))
(cpls (mapcar #'sod-class-precedence-list direct-supers)))
- (merge-lists (cons (cons class direct-supers) cpls)
- :pick (lambda (candidates so-far)
+ (merge-class-lists (cons (cons class direct-supers) cpls)
+ (lambda (candidates so-far)
(declare (ignore so-far))
(c3-tiebreaker candidates cpls)))))
precedence order i.e., the direct-superclasses list orderings."
(let ((dfs (flavors-cpl class)))
- (cons class (merge-lists (mapcar #'sod-class-precedence-list
+ (cons class
+ (merge-class-lists (mapcar #'sod-class-precedence-list
(sod-class-direct-superclasses class))
- :pick (lambda (candidates so-far)
- (declare (ignore so-far))
- (dolist (class dfs)
- (when (member class candidates)
- (return class))))))))
+ (lambda (candidates so-far)
+ (declare (ignore so-far))
+ (dolist (class dfs)
+ (when (member class candidates)
+ (return class))))))))
;;; Default function.
(:documentation
"Reports an inconsistency in the arguments passed to `merge-lists'.")
(:report (lambda (condition stream)
- (format stream "Merge inconsistency: failed to decide among ~A"
+ (format stream "Merge inconsistency: failed to decide between ~
+ ~{~#[~;~A~;~A and ~A~:;~
+ ~@{~A, ~#[~;and ~A~]~}~]~}"
(merge-error-candidates condition)))))
(export 'merge-lists)
-(defun merge-lists (lists &key pick (test #'eql))
+(defun merge-lists (lists &key pick (test #'eql) (present #'identity))
"Return a merge of the given LISTS.
The resulting list contains the items of the given LISTS, with duplicates
the input LISTS in the sense that if A precedes B in some input list then
A will also precede B in the output list. If the lists aren't consistent
(e.g., some list contains A followed by B, and another contains B followed
- by A) then an error of type `inconsistent-merge-error' is signalled.
+ by A) then an error of type `inconsistent-merge-error' is signalled. The
+ offending items are filtered for presentation through the PRESENT function
+ before being attached to the condition, so as to produce a more useful
+ diagnostic message.
Item equality is determined by TEST.
candidates))
(winner (cond ((null leasts)
(error 'inconsistent-merge-error
- :candidates candidates))
+ :candidates (mapcar present candidates)))
((null (cdr leasts))
(car leasts))
(pick