Continuing with some less familiar parts of Lisp from the C++ programmer’s perspective, we come to compute-applicable-methods. For the programmer, this is most commonly going to be used for insight into the method combination mechanism. Recall our discussion of call-next-method, there is a set of rules that describes how the method to be invoked is chosen. There is a default set of rules, but the programmer can also write his or her own combination rules for certain classes. When the code encounters a generic function invocation, it will choose a method to execute on the arguments, using compute-applicable-methods.
The beginner programmer can use this function to understand how the method is chosen. The function returns a list of methods, in precedence order. Here is a demonstration of this use:
;; Demonstrate compute-applicable-methods (defclass base-class () ()) (defclass derived-class (base-class) ()) (defgeneric myprint (stream object) (:documentation "Prints to 'stream' some information about 'object'.")) (defmethod myprint (stream (object base-class)) (format t "Entering base-class primary method.~%") (format t "Leaving base-class primary method.~%")) (defmethod myprint :around (stream (object base-class)) (format t "Entering base-class :around method.~%") (format t "Calling next method.~%") (call-next-method) (format t "Leaving base-class :around method.~%")) (defmethod myprint (stream (object derived-class)) (format t "Entering derived-class primary method.~%") (format t "Leaving derived-class primary method.~%")) (defun demonstrate () (let ((base (make-instance 'base-class)) (derived (make-instance 'derived-class))) (format t "The applicable methods list on base-class for 'myprint is:~%") (format t "~A~%~%" (compute-applicable-methods #'myprint (list t base))) (format t "And now we run 'myprint on a base-class object:~%") (myprint t base) (format t "~%~%") (format t "The applicable methods list on derived-class for 'myprint is:~%") (format t "~A~%~%" (compute-applicable-methods #'myprint (list t derived))) (format t "And now we run 'myprint on a derived-class object:~%") (myprint t derived)))
Producing output as follows:
CL-USER> (demonstrate) The applicable methods list on base-class for 'myprint is: (#<STANDARD-METHOD MYPRINT :AROUND (T BASE-CLASS) {100CEC1243}> #<STANDARD-METHOD MYPRINT (T BASE-CLASS) {100CD87083}>) And now we run 'myprint on a base-class object: Entering base-class :around method. Calling next method. Entering base-class primary method. Leaving base-class primary method. Leaving base-class :around method. The applicable methods list on derived-class for 'myprint is: (#<STANDARD-METHOD MYPRINT (T DERIVED-CLASS) {100CF3AB63}> #<STANDARD-METHOD MYPRINT :AROUND (T BASE-CLASS) {100CEC1243}> #<STANDARD-METHOD MYPRINT (T BASE-CLASS) {100CD87083}>) And now we run 'myprint on a derived-class object: Entering base-class :around method. Calling next method. Entering derived-class primary method. Leaving derived-class primary method. Leaving base-class :around method. NIL
You’ll notice that, for the derived class, it looks as if the order is wrong, because the base-class :around method was called, not the derived-class primary method. Under method combination rules, if there are any :around methods, then the most specific :around method is invoked instead of any primary methods. If that :around method invokes call-next-method, it then goes on through the rules we detailed before, under call-next-method. So, the :around method, though second in the list of returned methods, overrides the more specific primary method. Then, because of the call-next-method form, the more specific primary method is called.