[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

call-next-method and arguments to :before and :after methods



[In response to Frank Halasz's query, I sent him a pointer to the definition of
standard method combination in the manual pages for DEFINE-METHOD-COMBINATION.
Here's the subsequent conversation.  I think the whole exchange may
point up an omission in the CLOS documentation.]

   Date: Wed, 14 Sep 88 18:48:41 CDT
   From: Frank Halasz <halasz%sw.MCC.COM@MCC.COM>

   Thanks, the code under DEFINE-METHOD-COMBINATION helps.  But its still seems
   a bit ambiguous due to the interpretation of the make-method clause
   in call-method.  Specifically, call-method passes on the "arguments that
   were supplied to the effective method form containing the invocation
   of call-method."  The effective method form in the example code boils
   down to the following:

   `(call-method ,(first around)
		 (,@(rest around)
		  (make-method 
		    (multiple-value-prog1
		      (progn ,@(call-methods before)
			     (call-method ,(first primary)
					  ,(rest primary)))
		      ,@(call-methods (reverse after))))))

   The issue is whether the call-method invocations inside the make-method
   are part of the (outer) effective method form.  Alternatively, make-method
   could be considered as bounding a "new" inner effective method form.
   The current PCL effectively takes the latter alternative.  It seems to
   me that the spec takes the former reading, in which case :before and :after
   methods ought to get the arguments passed down to the original call to
   the genric function.

   -- Frank

The "alternative" that CALL-METHODs are lexically scoped to the
innermost MAKE-METHOD seems to be the only viable alternative.
If, as you suggest, scoping were to the outer effective method
body, there would be in fact no way to deliver arguments as adjusted
by CALL-NEXT-METHOD to any method other than those created
implicitly by the system; any method you make with MAKE-METHOD
would be powerless to access the adjusted arguments, because
when it tried to use CALL-METHOD (as it must), the adjusted arguments
would be rebound to their original values.  So what you're suggesting
would probably be a serious design flaw.

While your suggested semantics would be unable to simulate the
current (and historically correct; see next paragraph) semantics,
the reverse is not the case.  In order to simulate your suggested
semantics, say for a special-purpose method combination type,
FLET bindings could be used to capture code at the outer scoping level,
which could then be invoked from a MAKE-METHOD at any level:
	`(flet ((do-before-around ()
	          ,@(call-methods before-around)))
	   (call-method ,(first around)
			(,@(rest around)
			 (make-method
			   (multiple-value-prog1
			     (progn (do-before-around)
				    ...))))))

By the way, I find it totally natural that :AROUND methods surround
primary, :BEFORE, and :AFTER methods, and are thus able to adjust
their actual arguments.  This is the way it worked in Flavors,
which is where this method combination stuff comes from, and
I've never heard anyone question it before.  The grouping is this:
	AROUND( BEFORE PRIMARY AFTER )
rather than this:
	BEFORE AROUND( PRIMARY ) AFTER

(Now, I don't find CLOS standard method combination as a whole very
natural, for larger reasons, and I wish that the Flavors influence
were less strong here, but that's a separate issue.)

It is perhaps a documentation bug that the scoping of CALL-METHOD
is not better spelled out.  Does it really say "arguments that were
supplied to the effective method form containing the invocation
of call-method"?  I can't find any statement one way or the other in my
version of the spec, which is an old version.  I would have expected
words like "innermost effective method or MAKE-METHOD form" instead of
"effective method form".

(This bug report is the reason for the CC to the Powers That Be.)

				-- John