Re: Question about call-next-method
"David McClain (as dbm at refined-audiometrics dot com)" <[email protected]>
| Newsgroups | gmane.lisp.lispworks.general |
|---|---|
| Message-ID | <[email protected]> |
So it appears that CLOS (in LW) provides a collection of next methods, CLOS::*NEXT-METHODS*, as a dynamic binding, before calling your method. So if you were to redefine some more specific method anywhere along the current call-chain, you would likely not discover the new construction during this same method call. I have run into this kind of situation in my own code, in the past. Some things can be altered and take effect, but often times you cannot perform a CHANGE-CLASS on an object being used in a method call, and also have the change take effect during that call. The object does get its class changed, but the selection of next-methods has already been done by the time you change the class, and so CALL-NEXT-METHOD won’t appear to do the correct thing. > On Nov 16, 2025, at 08:12, David McClain (as dbm at refined-audiometrics dot com) <[email protected]> wrote: > > You can clear up a lot of the mystery about how LW is doing things by simply doing a Macroexpand: > > Take this dummy definition for a Method: > > (defmethod xxx ((a integer)) (doit a)) > > Just a nonsense definition for use in Macroexpand. Now macroexpand it to see: (orange highlights show how and where CALL-NEXT-METHOD arises…) > > (DSPEC:DEF #3=(METHOD XXX #1=(INTEGER)) > (EVAL-WHEN (:COMPILE-TOPLEVEL) > (CLOS::RECORD-CLASS-POTENTIAL-ARGUMENTS 'XXX '#2=(A) '#1#) > (COMPILER::SET-ARG-SPEC-FROM-LAMBDA-LIST 'XXX '#2#) > (COMPILER::NOTE-FUNCTION-DEFINED-FN 'XXX)) > (DSPEC:CALL-WITH-DEFINING-LOCATION CLOS::LOAD-DEFMETHOD-M > '(XXX NIL . #2#) > '#1# > #'(LAMBDA #4=(A) > (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 801015EFFB>) > (LAMBDA-NAME #3#) > (LAMBDA-LIST . #4#) > (SYSTEM::FLAG :METHOD) > (SYSTEM::BOUND-TYPE INTEGER A) > (IGNORABLE A)) > (LET ((#6=#:G9529 > (LOCALLY (DECLARE (OPTIMIZE (SAFETY 0))) CLOS::*NEXT-METHODS*))) > (LET ((#7=#:G9530 A)) > (FLET ((CALL-NEXT-METHOD (&OPTIONAL . #5=(#:G9531)) > (DECLARE (SYSTEM::FLAG :CALL-NEXT-METHOD) > (OPTIMIZE > (SAFETY 0) > (DEBUG 0) > (FIXNUM-SAFETY 0) > (SPEED 3)) > (IGNORABLE . #5#)) > (SYSTEM::INTERNAL-IF (ZEROP (SYSTEM::ARGUMENTS-COUNT)) > (CLOS::CALL-NEXT-METHOD-WITH-ORIGINAL NIL > '#3# > #6# > NIL > #7#) > (CLOS::CALL-NEXT-METHOD-WITH-ARGS (SYSTEM::ARGUMENTS-COUNT) > '#3# > #6# > 1 > NIL > NIL > . #5#)))) > (DECLARE (INLINE CALL-NEXT-METHOD)) > (FLET ((NEXT-METHOD-P () (NOT (NOT #6#)))) > (DECLARE (INLINE NEXT-METHOD-P)) > (PROGN (BLOCK XXX (DOIT A)))))))))) > >> On Nov 15, 2025, at 19:31, Edward Kiser (as edkiser at gmail dot com) <[email protected]> wrote: >> >> Hello, >> >> Can call-next-method be safely captured in a lambda and returned, to >> be replayed later? Will it retain the meaning it had when it was >> captured? >> >> My testing in LispWorks suggests that it can be, but I want to know if >> I can always expect that to be the case or if I am accidentally >> relying on undefined behavior. >> >> The Common Lisp HyperSpec says, "The function call-next-method has >> lexical scope and indefinite extent, and can only be used within the >> body of a method defined by a method-defining form." >> >> There are various ways to interpret this. For example, it's possible >> that call-next-method is a global function with indefinite extent but >> that its meaning varies depending on which method is currently >> running, and if no method is running, it has no meaning. This would >> mean that if you wrote a method (call it "A") that returned (lambda () >> (call-next-method)), or even just #'call-next-method, and later called >> that procedure when no method was running, it would fail -- and if you >> called it inside method "B" it would call B's next method instead of >> A's. >> >> When it says "can only be used within the body," does that mean >> lexically within the body, or dynamically when the body is running, or >> both? >> >> I am sort of hoping that (lambda () (call-next-method)) would capture >> whatever meaning call-next-method had when "lambda" created the >> function, and use that same meaning whenever the function was called. >> >> An implementation could ensure this by using symbol-macrolet >> internally to define a different meaning for call-next-method for each >> method, because call-next-method would be replaced by the macro >> expander with whatever meaning it is supposed to have in that lexical >> environment. >> >> (The AMOP book uses an "add-function-bindings" function which >> apparently adds the bindings to the lexical environment, but it >> doesn't say how "add-function-bindings" works.) >> >> The reason I want to save the meaning of call-next-method and use it >> later is because I am doing continuation-passing-style with a >> trampoline, and I wanted to use the long form of >> define-method-combination to combine methods, and each method might >> want to bounce off the trampoline several times. >> >> Also I am using "amb" and I would like it if I could use the saved >> lambdas more than once. >> >> Sincerely, >> >> Ed Kiser >> >> ---- >> >> Experiment: >> >> CL-USER 1 > (defclass jikken1 () ()) >> #<STANDARD-CLASS JIKKEN1 40202ABAB3> >> >> CL-USER 2 > (defclass jikken2 () ()) >> #<STANDARD-CLASS JIKKEN2 40202AE86B> >> >> CL-USER 3 > (defgeneric try-something (j)) >> #<STANDARD-GENERIC-FUNCTION TRY-SOMETHING 4030031B24> >> >> CL-USER 4 > (defmethod try-something ((j jikken1)) (format t "Hello 1 ~s!~%" j)) >> #<STANDARD-METHOD TRY-SOMETHING NIL (JIKKEN1) 402037294B> >> >> CL-USER 5 > (defmethod try-something :around ((j jikken1)) (lambda () >> (call-next-method))) >> #<STANDARD-METHOD TRY-SOMETHING (:AROUND) (JIKKEN1) 4020374B93> >> >> CL-USER 6 > (defmethod try-something ((j jikken2)) (format t "Hello 2 ~s!~%" j)) >> #<STANDARD-METHOD TRY-SOMETHING NIL (JIKKEN2) 4020375A4B> >> >> CL-USER 7 > (defmethod try-something :around ((j jikken2)) (lambda () >> (call-next-method))) >> #<STANDARD-METHOD TRY-SOMETHING (:AROUND) (JIKKEN2) 40203768FB> >> >> CL-USER 8 > (setq aa1 (try-something (make-instance 'jikken1))) >> #<anonymous interpreted function 4030000ABC> >> >> CL-USER 9 > (setq aa2 (try-something (make-instance 'jikken2))) >> #<anonymous interpreted function 4030000D1C> >> >> CL-USER 10 > (funcall aa1) >> Hello 1 #<JIKKEN1 4020019FA3>! >> NIL >> >> CL-USER 11 > (funcall aa2) >> Hello 2 #<JIKKEN2 402001DFC3>! >> NIL >> >> CL-USER 12 > (funcall aa1) >> Hello 1 #<JIKKEN1 4020019FA3>! >> NIL >> >> _______________________________________________ >> Lisp Hug - the mailing list for LispWorks users >> [email protected] >> http://www.lispworks.com/support/lisp-hug.html >