Вы не сможете легко выполнить эту задачу, используя только диспетчеризацию CLOS.
Прежде чем продолжить, я думаю, что некоторые краткие замечания по терминологии важны.
Глоссарий Common Lisp HyperSpec определяет «подкласс» следующим образом:
класс, который наследуется от другого класса, называемого суперклассом. (Нет
класс является подклассом самого себя.)
Это определение, хотя и интуитивно понятное, кажется мне странным, так как я ожидаю, что это будет определение "правильного подкласса" Однако все классы являются типами, и он определяет «подтип» как:
тип, принадлежность к которому совпадает или является надлежащим подмножеством членства другого типа, называемого супертипом. (Каждый тип является подтипом самого себя.)
Обратите внимание на круглые скобки: «Каждый тип является подтипом самого себя».
Также определяет «правильный подтип»:
(типа) подтип типа, который не совпадает с типом (т. Е. Его элементы являются "правильным подмножеством" типа).
Итак, в вашем примере B и C являются подклассами A, а также подтипами. С другой стороны, B, C, и A являются подтипами A.
В defmethod добавляется «имя специалиста параметров» . Это может быть символ, класс (который немного сложно набрать) или список, начинающийся с eql. Если вы предоставляете символ, он указывает класс, названный этим символом (который, конечно, является типом). Список eql указывает тип, состоящий из объектов, которые соответствуют объекту в списке.
Метод будет соответствовать любому объекту, который является членом типа, указанного специализатором. И, конечно же, член подтипа X также является членом X.
Итак, ваша первая проблема в том, что вы передаете символьные объекты в ваш метод; каждый символ имеет тип SYMBOL
. Символ, который случается с именем класса, ничем не отличается в этом отношении; единственное отношение к классу состоит в том, что это имя класса, которое не является отношением подтипа.
Существуют объекты класса (возвращаемые find-class
), но они не лучше, чем символы для специализации метода, потому что тип объекта класса обычно совпадает с типом объектов класса его подклассов.
Итак, вы остались использовать экземпляры или читать AMOP , чтобы научиться создавать собственные типы универсальных функций.
Если у вас есть экземпляр, вы можете написать метод следующим образом:
(defmethod fun ((param a))
(if (eq (type-of param) 'a)
(call-next-method)
(format t "~a is a subclass of A~%" (type-of param))))
Если у вас есть простой способ получить экземпляры ваших классов, вы можете написать эту обертку:
(defmethod fun ((param symbol))
(fun (retrieve-instance param)))
Тогда вы сможете передавать символы в шутку и получать желаемые результаты.
Если вы хотите использовать функции AMOP (которые не были определены стандартом, но широко доступны, см. Closer Project ), вы можете определить retrieve-instance
следующим образом:
(defun retrieve-instance (name)
(let ((class (find-class name)))
(unless (class-finalized-p class)
(finalize-inheritance class))
(class-prototype class)))
Обратите внимание, что диспетчеризация метода - единственное, для чего хорош результат class-prototype
; не пытайтесь изменить это или что-то в этом роде.