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


I read back over the mail and did some further thinking on this, and
I've got some additional thoughts on the subject, and a miniproposal.

First to respond to the immediate nature of Gregor's message, I agree
with his point that the code for generic dispatch and the method
function interface are intertwined. Since the code for generic 
dispatch has the job of calculating the list of applicable methods
and the effective method (according to Chapter 1), it also has the
job of arranging where that information appears in the environment
of the method function. Some of the possibilities for where to
put it are a "hidden" argument to the method function, a "hidden"
lexical variable in the method function's environment which the
generic dispatch code accesses via some implementation dependent
interface, etc. So, for purposes of arranging for changes in
control flow such as call-next-method, it is not possible to
seperate the code doing the dispatching from the method function

I think it might be helpful at this point to briefly review the concepts,
functions and macros in Chapters 1, 2, and 3 related to this topic to
see how they fit together. First some definitions from Chapter 1:

Applicable Methods-Given a generic function invocation, the list of
applicable methods are all methods defined on the generic function
whose parameter specializers are satisfied by their corresponding
arguments. The definition of "satisfy" is given in Chapter 1, pg. 1-28.

Effective Method-The effective method is a combination of the
applicable methods according to their precedence order. It is
the code actually executed during the generic function invocation.
Calculating it is the subject of Chapter 1, pp. 1-31-1-35.

Note that these definitions make no mention of dispatcher functions
(or handlers, as they are called in Flavors) nor of method functions.
Generic dispatch is a process, which results in the calculation of
the list of applicable methods and the effective method, and the
application of the effective method to the arguments. Dispatcher
functions, precalculation of the list of applicable methods, etc.
are all optimizations, which must preserve the semantics of this
model in order to be valid. In practice, a dispatcher function
is needed to bundle the control flow associated with generic
dispatch into one place, however.

Now how does this all fit into the metaobject protocol of Chapter 3?
In Chapter 3, pp. 3-59-3-61 list the generic functions. There
are three of interest:

compute-applicable-methods <generic-function> <arguments>

	<generic-function> <method-combination> <applicable-methods>

These compute the two quantities defined in Chapter 1. 

compute-discriminator-code <generic-function>

What does this do? Well, the description in Chapter 3 isn't very clear.
The example uses make-effective-method-function, which isn't documented
anywhere else, and in the invocation of compute-effective-method 
the actual parameter list does not match the formal parameter interface
described below the example. Here's my guess as to how the default
method should look:

(defmethod compute-discriminator-code 
  ((generic-function standard-generic-function))
	  (generic-function-method-combination generic-function)

      #'(lambda (&rest args)
	        (compute-applicable-methods generic-function args)

            (check-keyword-arguments generic-function methods args)
        ) ;lambda
    ) ;let
) ; compute-discriminator-code

In order for this to work, none of the functions from the metaobject
protocol can be generic, otherwise the metacircularity doesn't terminate.
So Gregor is correct in this point, and Chapter 3 is incorrect, since
compute-effective-method can't be generic. The appropriate place for
specializations is in compute-dispatcher-code. Compute-effective-method
and compute-applicable-methods bundle whatever implementation dependent
mechanism is required to make sure that the environment of method functions
has the necessary information for call-next-method to happen correctly.

Minor niggle: compute-effective-method should probably be called
compute-effective-method-function, since what it is returning is
a function not a method object.

However, Patrick and Dave also have a point, in that there needs to be some
way for creating method functions which installs the special processing
needed to implement call-next-method into the user supplied code. 
Perusing Chapter 1 for a moment, we find two macros which look as if
they might help:

make-method <form> 
call-method <form>

From the description, these look like hooks into the innards of 
compute-effective-method, however, precisely how they might fit
in, I can't figure out.

So we are, indeed, left with a hole in the metaobject protocol which
Patrick's proposed make-method-function would fill. Here is the
interface again:

make-method-function <method> <generic-function> <qualifiers> <specializers>
                     <lambda-exp> &optional <macroexpand-environment>

This should return a perfectly vaild function which is compilable and
funcallable, with the exception that it may need to be funcalled by
the function returned by compute-discriminator-code to work properly.

The fundamental idea here is that compute-discriminator-code and
make-method-function scheme together to make sure the right code
is generated for unusual changes in control flow like call-next-method.
Both are generic functions, with compute-discriminator-code
specialized on its first argument and make-method-function specialized
on its first and second. 

Of course, this analysis overlooks one fundamental point. There are
no primitives in Common Lisp for arranging the transfer of information
needed for things like call-next-method because Common Lisp doesn't
have first class environments. I've got some ideas about simulating
this, but they're not firm so I won't make this lengthy message
even more lengthy by including them.

The miniproposal is this:

1) compute-effective-method, compute-applicable-methods, and 
check-keyword-arguments are all functions.

2) make-method-function is implemented as Dave and Patrick have outlined.

3) We figure out how call-method and make-method from Chapter 1 fits
into the metaobject protocol.