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

Re: MAKE-METHOD-FUNCTION and APPLY-METHOD



>But compute-effective-method has to be generic!  That's the whole basis of
>the meta-objectification of method combination.  If this is another
>non-terminating circularity, then it's another defect in the meta-circular
>description of CLOS.  In an implementation all these circularities have to
>be broken by special-casing, as in any meta-circular interpreter.

OK, I'll buy that. I misunderstood the specification for 
compute-effective-method. Further comment on the implications below,
plus the special casing which is needed.

>What compute-effective-method returns is neither a function nor a method
>object.  It actually returns a Lisp form.  See 88-002 p.1-32.  This

Right. That explains what the undocumented function 
make-effective-method-function does in the example on pg. 3-59. It
takes the generic function and the effective method form, and
returns a function which can be applied to start the method
rolling. It also hides the implementation dependencies necessary
for setting up the environment so that call-next-method works.
And that is where the metacircular recursion bottoms out, since
it returns a function and not a generic function.

>I can't be sure your code is wrong since I have
>yet to see a satisfactory explanation of what compute-discriminator-code
>is for.

compute-discriminator-code does precisely the same thing for generic
dispatch that compute-effective-method does for method combination.
It is where code for customized generic dispatch gets generated.
It needs to work together with make-method-function to assure that
customized control flow changes like call-next-method get done correctly,
because the generic dispatch code has to know what sort of interface
the method functions require and what specialized features of the
execution environment need to be set up. In the default case,
make-effective-method-function takes care of most of that. Here's
what I'd expect compute-discriminator-code to look like in the 
default case:

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

      #'(lambda (&rest args)
          (let*
            (
              (applicable-methods 
	        (compute-applicable-methods generic-function args)
	      )
	      (effective-method-form
	        (compute-standard-effective-method-form 
		  generic-function
		  method-combination
		  applicable-methods
		)
              )
	      (effective-method-function
		(make-effective-method-function
		  generic-function
		  effective-method-form
	        )
              )	      
	    )

            (check-keyword-arguments generic-function methods args)
	    (apply effective-method-function args)
	  )
        ) ;lambda

) ; compute-discriminator-code


There are a couple things to note here:

1) Everything called by compute-discriminator-code is a function.
These are: compute-applicable-methods, compute-standard-effective-method-form,
and make-effective-method-function. If this is not the case,
the metacircularity does not bottom out.

2) New in this list is compute-standard-effective-method-form. It takes
the generic function, a standard-method-combination object, and the
list of applicable methods, and returns the effective method form.
The compute-effective-method method for standard-method-combination
would have the application of this function as its body. This is
the special casing. Side note: the method combination object
might not be needed as an argument, since the function already
knows the kind of method combination.

3) The creation of the dispatcher function is parameterized by both
the generic function and the method combination. There is really no
choice here, because the algorithm for calculating the effective
method depends on both the list of applicable methods (which the
generic function provides) and the method combination type.

4) Functions similar to compute-standard-effective-method-form would be
needed for other system provided method combination types, of course.

I think some of the problems in the discussion about bottoming out
of metacircularity come from the fact that we made method combination
metacircular without taking into account that calculating the
effective method, which is parameterized by method combination, 
is a fundamental part of generic dispatch, and therefore
we introduced an infinite loop. This proposal unrolls the loop.

As an example of how this could be used with make-method-function to
introduce a customized control flow primitive, consider the
following modification, which we had suggested in the specification
paper. It allows any more general method to be invoked, depending
on the arguments, rather than just the next most general method:

;;uses no method combination, and simply takes the first method
;;  on the list to start the ball rolling.

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


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

	    (apply (method-function (car applicable-methods))
		    (cdr applicable-methods)
		    args
	    )
	  )
        ) ;lambda

) ; compute-discriminator-code

(defmethod make-method-function 
  ((method my-method)
   (generic-function my-generic-function)
   specializers
   qualifiers
   lambda-exp
   &optional macroexpand-env
  )

  `(lambda (rest-applicable-methods &rest args)
      (flet 
	( 
	  (my-call-next-method (&rest call-args)
            (let*
	      (
  	        (matching-method 
		  (get-more-general-method rest-applicable-methods call-args)
   		)
		(next-rest-applicable-methods 
		  (member matching-method rest-applicable-methods)
		)
	      )

	      (if matching-method
		(apply (method-function matching-method)
		       (cdr next-rest-applicable-methods)
		       call-args
	        )
	        (apply #'no-applicable-method ,generic-function call-args)
              )

	    ) ;let*

          ) ;my-call-next-method
      )

      (apply (function ,lambda-exp) args)
    ) ;flet
  ) ;lambda

) ;make-method-function


Here, compute-discriminator-code and make-method-function implement
the communication of the applicable method list via a hidden argument.

The only thing I don't like about this is that compute-discriminator-code
returns a function object while make-method-function returns the code
for a function (maybe the names should be interchanged?). But, given
that function objects are atomic in Common Lisp, there probably isn't
any better way to do this, except by having compute-discriminator-code
return the code, so the caller would have to turn it into a function,
or have make-method-function call eval on the code to make the
function.

Does this make any sense? The fact that we're having such a hard time
converging on this is an indication to me that we're venturing into
little understood territory. However, as Gregor mentioned, we need
to come to convergence on this soon.

		jak