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

Re: call-method ??????

>Date: Thu, 29 Oct 1992 10:42:32 +0100
>To: info-mcl@cambridge.apple.com
>From: lafourca@imag.fr (mathieu Lafourcade)
>Subject: call-method ??????
>Following my last request about a method dispatching, it appears that it
>could be nice to define a function (or macro) that could specify a call to
>a method of an arbitrary class.
>I have a big problem with CALL-METHOD method next-method-list.
>In CLtL p 817 it is said : 
>[...] It can be used only within an affetive method form, for the name
>call-method is defined only within the lexical scope of such a form.
>The macro call-method invokes the specified method, supplying it with
>arguments and with definitions for call-next-method and for next-method-p.
>The arguments are the arguments that were supplied to the effective method
>form containing the invocation of call-method. [...]
> call-method is defined as it in MCl, but as ccl::%call-method, 
>ccl::%%call-method,  ccl::%call-method*,  ccl::%%call-method*
> Which one to choose ?

>Date: 30 Oct 92 13:46:00 GMT
>From: Ranson <ranson@LANNION.cnet.fr>
>To: info-mcl@cambridge.apple.com, lafourca@imag.fr
>Subject: Re:  call-method ??????
>I don't have a direct answer to your question (though I wonder why you mention
>CALL-METHOD and don't use it...), but it seems to me that if you find yourself
>in such a tight corner, it means that your design does not fit in the OO model.
>Either change your design, or try to use ordinary functions instead of methods.
>     Daniel.

Daniel has a good point, though he appears to misunderstand CALL-METHOD.
CALL-METHOD is only defined inside the form returned from DEFINE-METHOD-COMBINATION.
Daniel's misunderstanding is very common.

Flame on.
I have always thought that DEFINE-METHOD-COMBINATION as defined by the
CLOS standard was a crock. I think it should return a function that
invokes the methods via a CALL-METHOD function rather than returning a
form which requires invoking the compiler at method combination time.
Yes, the former is slightly slower at run time, but the latter makes it
a lot slower to "warm up" your generic functions.
Flame off.


MCL has four functions for calling methods functionally:

(ccl::%call-method method next-methods &rest args)
(ccl::%call-method* method next-methods args)
(ccl::%%call-method method next-methods &rest args)
(ccl::%%call-method* method next-methods args)

All four apply the METHOD to the ARGS with the given NEXT-METHODS.
The "*" versions take the args as a list.
The %% versions do not type check the METHOD or NEXT-METHODS
arguments and will likely crash if METHOD is not a method or NEXT-METHODS
is not a proper list of methods.


The rest of this message is not in answer to your question, but may be
of interest to DEFINE-METHOD-COMBINATION fans. I am not particularly
happy with MCL's DEFINE-METHOD-COMBINATION extensions, mostly because
of the (make-instance 'standard-method ...) form in the third
DEFINE-METHOD-COMBINATION form below, but it's the start of an idea for
a functional DEFINE-METHOD-COMBINATION definition. These extensions are
part of MCL 2.0, but may change in a future release.


; Here are three equivalent ways of defining AND method-combination
; They illustrate MCL's two extensions to method-combination that
; allow you to eliminate calling the compiler at method combination
; time in exchange for being slightly slower at run-time.

; Short-form
; define-method-combination-evaluator eliminates compiling effective
; methods for short-form method combination
(define-method-combination my-and :identity-with-one-argument t)

(define-method-combination-evaluator my-and (methods args)
  (when methods
      (if (null (cdr methods))
        (return (%%call-method* (car methods) nil args)))
      (unless (%%call-method* (pop methods) nil args)
        (return nil)))))

; The standard form given in CLtL2

(define-method-combination my-and
                           (&optional (order :most-specific-first))
                           ((around (:around))
                            (primary (my-and) :order order :required t))
  (let ((form (if (rest primary)
                `(and ,@(mapcar #'(lambda (method) `(call-method ,method ()))
                `(call-method ,(first primary) ()))))
    (if around
      `(call-method ,(first around)
                    (,@(rest around)
                     (make-method ,form)))

; a version of the above that does not require the compiler
; at effective-method computation time.
; If the DEFINE-METHOD-COMBINATION form returns a function, the
; method-combination code will assume that it is an effective-method that
; can be applied to the args of the generic-function.
(define-method-combination my-and
                           (&optional (order :most-specific-first))
                           ((around (:around))
                            (primary (my-and) :order order :required t))
  (let ((f (if (rest primary)
             #'(lambda (&rest args)
                 (declare (dynamic-extent args))
                 (let ((methods primary))
                   (unless (null methods)
                       (if (cdr methods)
                         (unless (%call-method* (pop methods) nil args)
                           (return nil))
                         (return (%call-method* (car methods) nil args)))))))
             (if (call-next-method-p (setq primary (car primary)))
               #'(lambda (&rest args)
                   (declare (dynamic-extent args))
                   (%call-method* primary nil args))
               (method-function primary)))))
    (if around
      (let ((first (first around))
            (rest (nconc (rest around)
                         (list (make-instance 'standard-method
                                              :function f)))))
        #'(lambda (&rest args)
            (declare (dynamic-extent args))
            (%call-method* first rest args)))

; Some test code for the my-and method-combination
; If you reevaluate one of the method-combination defining forms above,
; make sure to reevaluate one of the defmethod forms below to cause
; the and-tester generic function to recompute its effective method.
(defgeneric and-tester (x)
  (:method-combination my-and))

(defmethod and-tester my-and (x)
  (print '(and-tester my-and (t)))
  (values x 't))

(defmethod and-tester my-and ((x integer))
  (print '(and-tester my-and (integer)))
  (unless (eql x 2)
    (values x 'integer)))

(defmethod and-tester my-and ((x fixnum))
  (print '(and-tester my-and (fixnum)))
  (unless (eql x 1)
    (values x 'fixnum)))

(defmethod and-tester :around ((x fixnum))
  (print '(and-tester :around (fixnum)))

(and-tester 1)
(and-tester 2)
(and-tester 3)
(and-tester (expt 2 32))
(and-tester 'foo)