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

Re: Continuing comments on Draft 11



   Date: 	Sat, 3 Nov 1990 13:00:00 PST
   From: Scott Cyphers <Cyphers@JASPER.SCRC.Symbolics.COM>

   make-method-lambda already returns a second value, a plist.  I'm just
   defining one of the properties.

Oh, I see.
					 
					 November 15th is too soon for a
   description to be worked out (although I'd be glad to prototype one
   when I can find the time).

Actually, I think getting a facility like this really right is much
farther away than that.  It will require reworking large parts of the
method lookup protocol including method combination.  But, there is an
easy way to get it half right, using the method combination facility.
We may not want to do it now, but it pays to at least think about it for
a but now.  This idea is an upward compatible extension from my recent
method function proposal, which means its easy to go back and add it.
(I believe we have to put method functions in, without them there are
too many things people can't write (steppers, weird dynamic method
combinations, etc.)

The idea is to say that the 2nd, 3rd (and so on) `arguments' to
CALL-METHOD become the 2nd, 3rd (and so on) arguments to method
functions.  (An extension from the current scheme where the second
argument to CALL-METHOD is a list of next methods and the second
argument to method function is the list of next methods.)  Users who
want to pass more dynamic information to methods can do so by altering
MAKE-METHOD-LAMBDA and COMPUTE-EFFECTIVE-METHOD.

Given that, lets look at a form of the idea you suggested, where methods
could `backup' in the list of applicable methods as well as go forward.

(defclass backup-gf (standard-generic-function)
     ())

(defmethod make-method-lambda ((gf my-gf) m lambda env)
  ;;
  ;; Return a lambda of three (instead of two arguments).
  ;; The first two are as usual, the third is a list of
  ;; previous methods.  The first element of the list of
  ;; previous methods is the current method.
  ;;
  (flet ((add-bindings ()
           ;;
           ;; Add the lexical functions CALL-PREVIOUS-METHOD and
           ;; PREVIOUS-METHOD-P.  Also, override the implementation's
           ;; CALL-NEXT-METHOD and NEXT-METHOD-P.
           ;;
           `(lambda ,(cadr lambda)
              (flet ((call-next-method (&rest args)
                       (funcall (method-function (car next-methods))
                                args
                                (cdr next-methods)
                                (cons (car next-methods) previous-methods)))
                     (call-previous-method (&rest args)
                       (funcall (method-function (cadr previous-methods))
                                args
                                (cons (car previous) next-methods)
                                (cdr previous-methods)))
                     (next-method-p () next-methods)
                     (previous-method-p () (cdr previous-methods)))
                ,@(cddr lambda)))))

    `(lambda (args next-methods previous-methods)
       (funcall ,(call-next-method gf m (add-bindings) env)
                args next-methods))))

(defmethod compute-effective-method ((gf backup-gf) (combin t) methods)
  `(call-method ,(car methods) ,(cdr methods) ,(list (car methods))))


Now, if you say

(defclass a ()  ())
(defclass b (a) ())
(defclass c (b) ())

(defgeneric foo (x in/out) (:generic-function-class backup-gf))

(defmethod foo ((x a) &optional (in/out 'in))
  (print 'a)
  (if (eq in/out 'in)
      (call-next-method x 'in)
      (values)))

(defmethod foo ((x b) &optional (in/out 'in))
  (print 'b)
  (if (eq in/out 'in)
      (call-next-method x 'in)
      (call-previous-method x 'out)))

(defmethod foo ((x c) &optional (in/out 'in))
  (print 'c)
  (call-previous-method x 'out))


now, saying (foo (make-instance 'c)) does:

A
B
C
B
A

(At least that is what I hope it does!