2

I have the following two classes:

(defclass person () ())

(defmethod speak ((s person) string)
    (format t "-A" string))

(defmethod speak :before ((s person) string)
    (print "Hello! "))

(defmethod speak :after ((s person) string)
    (print "Have a nice day!"))


(defclass speaker (person) ())

(defmethod speak ((i speaker) string)
  (print "Bonjour!"))


(speak (make-instance 'speaker) "Can I help yoU?")

And the ouput of this is:

"Hello! "                                                                                                                                                                                                                                               
"Bonjour!"                                                                                                                                                                                                                                              
"Have a nice day!" 

What I'm trying to figure out is how these methods are executed in terms of "order." I cannot seem to grasp on what is happening and why. Supposedly there is a rule precedence to this but I'm not sure where to find it. For example, why doesn't "Hello!Can I help you" ever fire in this case?

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Dimitri
  • 1,906
  • 3
  • 25
  • 49

3 Answers3

9

When you don't have any around methods, the order of method application is: all before methods from most specific to least specific, then the most specific primary method, and then the after methods from least specific to most specific. In your case you have two primary methods (methods without :before or :after next to the name), one which specifies on person, and the other which specifies on speaker. Since speaker is more specific than person, only the speaker primary method is called. If you want to call multiple primary methods, look at call-next-method.

malisper
  • 1,671
  • 1
  • 12
  • 16
3

While I see that there's already an accepted answer, Common Lisp has some very nice documentation in the HyperSpec, and it's useful to know where to find the full description of what happens. In this case, it's 7.6.6.2 Standard Method Combination, which says (abbreviated):

The semantics of standard method combination is as follows:

  • If there are any around methods, the most specific around method is called. It supplies the value or values of the generic function.

  • Inside the body of an around method, call-next-method can be used to call the next method. When the next method returns, the around method can execute more code, perhaps based on the returned value or values. The generic function no-next-method is invoked if call-next-method is used and there is no applicable method to call. The function next-method-p may be used to determine whether a next method exists.

  • If an around method invokes call-next-method, the next most specific around method is called, if one is applicable. If there are no around methods or if call-next-method is called by the least specific around method, the other methods are called as follows:

    • All the before methods are called, in most-specific-first order. Their values are ignored. An error is signaled if call-next-method is used in a before method.

    • The most specific primary method is called. Inside the body of a primary method, call-next-method may be used to call the next most specific primary method. When that method returns, the previous primary method can execute more code, perhaps based on the returned value or values. The generic function no-next-method is invoked if call-next-method is used and there are no more applicable primary methods. The function next-method-p may be used to determine whether a next method exists. If call-next-method is not used, only the most specific primary method is called.

    • All the after methods are called in most-specific-last order. Their values are ignored. An error is signaled if call-next-method is used in an after method.

  • If no around methods were invoked, the most specific primary method supplies the value or values returned by the generic function. The value or values returned by the invocation of call-next-method in the least specific around method are those returned by the most specific primary method.

There's a particularly helpful illustration at the end of that page that describes the behavior and its motivation:

The before methods are run in most-specific-first order while the after methods are run in least-specific-first order. The design rationale for this difference can be illustrated with an example. Suppose class C1 modifies the behavior of its superclass, C2, by adding before methods and after methods. Whether the behavior of the class C2 is defined directly by methods on C2 or is inherited from its superclasses does not affect the relative order of invocation of methods on instances of the class C1. Class C1's before method runs before all of class C2's methods. Class C1's after method runs after all of class C2's methods.

By contrast, all around methods run before any other methods run. Thus a less specific around method runs before a more specific primary method.

If only primary methods are used and if call-next-method is not used, only the most specific method is invoked; that is, more specific methods shadow more general ones.

coredump
  • 37,664
  • 5
  • 43
  • 77
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
1

In addition to other answers, note that you can define custom method combination with the following macro: DEFINE-METHOD-COMBINATION. There are already ten existing method combinators so I don't think it is common to define custom ones. Of course, being able to do so can be very useful at times (see Joshua Taylor's comment).

Also, the way your methods are invoked is subject to class inheritance, which by default takes into account parent-child relationships, as well as order between superclasses. Please read "Fundamentals of CLOS". The class precedence list can be changed with the Meta-Object Protocol: see COMPUTE-CLASS-PRECEDENCE-LIST.

coredump
  • 37,664
  • 5
  • 43
  • 77
  • 1
    "I don't think this is commonly used in practice." It may not be *all* that common, especially since there are [a number of useful ones built in](http://www.lispworks.com/documentation/HyperSpec/Body/07_ffd.htm), but when one does has opportunity to use it, it is *very* useful. – Joshua Taylor Jun 07 '15 at 12:53