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]> |
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