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
>> 
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.