Re: Question about call-next-method
"pc (as pc at p-cos dot net)" <[email protected]>
| Newsgroups | gmane.lisp.lispworks.general |
|---|---|
| Message-ID | <[email protected]> |
Ah, I forgot that LW also uses a dynamically scoped function to pass next methods around. Anyway, you can use Closer to MOP to get the semantics specified by AMOP - you have to use the standard-generic-function metaclass provided by that library, though, not the one that comes with LW (in the common-lisp package). Pascal > On 16 Nov 2025, at 16:20, David McClain (as dbm at refined-audiometrics dot com) <[email protected]> wrote: > > 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 >> >