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

call-method proposal prime prime



I take it we have agreed on this.  Here are the changes to the document.
In cases where the mail was unclear, I made some decisions.  I hope there
is time to make these changes before we have to ship the documents to
X3J13.  I apologize for the delay in getting this out; pulling it all
together has been a bigger job than I expected, and I had some other
things that had to be done first.

Chapter 1:  No changes.

Chapter 2:

2-5: In "Tools for declarative method combination", remove
make-method-call, add call-method and make-method.

2-30 long form syntax of define-method-combination changes to:
  \Defmac {define-method-combination} {\vtop{\hbox{name lambda-list }
  \hbox{\paren{\star{\curly{method-group-specifier}}} }
  \hbox{\brack{\paren {{\bf :arguments} \dot {\it lambda-list }}}}
  \hbox{\star{\curly {declaration $\vert$ doc-string}}}
  \hbox{\star{\curly{form}}}}}
or, in English, the list (:arguments . lambda-list) can optionally
appear before any declarations or documentation string.  I've almost
certainly gotten the TeX wrong.

2-33 fifth paragraph:  Remove the following sentences:
  The function {\bf make-method-call} is also used in constructing the
  Lisp form; it hides the implementation-dependent details of how
  methods are called.  Programmers always use {\bf make-method-call} to
  translate from the lists of method objects produced by the method-group
  specifiers to Lisp forms that invoke those methods.
Replace them with:
  The effective method uses the macros {\bf call-method} and
  {\bf make-method}.  They hide the implementation-dependent details
  of how methods are called.  These macros have lexical scope and are
  only available in an effective method Lisp form.
  Programmers always translate a method object, an element of one of the
  lists produced by the method-group specifiers, into a Lisp form that
  invokes the method by constructing a form that invokes the
  {\bf call-method} macro with the method object as its first subform
  and a list of next-method objects as its second subform.
  Programmers translate a Lisp form into a method object whose body
  is that form with the {\bf make-method} macro.  An invocation of
  {\bf make-method} can only be used inside of an invocation of
  {\bf call-method}.
Append the following new paragraph:
  When an effective method has no effect other than to call a single
  method, some implementations employ an optimization that uses the
  single method directly as the effective method, thus avoiding
  the need to create a new effective method.  This optimization is
  active when the effective method Lisp form consists entirely of
  an invocation of the {\bf call-method} macro whose first subform
  is a method object and whose second subform is {\bf nil}.  Each
  {\bf define-method-combination} body is responsible for stripping
  off redundant invocations of {\bf progn}, {\bf and},
  {\bf multiple-value-prog1}, and the like, if this optimization
  is desired.
Append the following paragraph:
  The list {\bf (:arguments . {\it lambda-list\/})} can optionally
  appear before any declarations or documentation string.  This is
  useful when the method combination type performs some specific
  behavior as part of the combined method and that behavior needs access
  to the arguments to the generic function.  Each parameter variable
  defined by {\it lambda-list\/} is bound to a form that can be inserted
  into the effective method.  When this form is evaluated during
  execution of the effective method, its value is the corresponding
  argument to the generic function.  If {\it lambda-list\/} is not
  congruent to the generic function's lambda-list, additional
  ignored parameters are automatically inserted until it is congruent.
  Thus it is permissible for {\it lambda-list\/} to receive fewer
  arguments than the number the generic function expects.

2-34 first paragraph: Replace the whole paragraph with:
  The functions {\bf method-combination-error} and
  {\bf invalid-method-error} can be called from the body
  {\it forms\/} or from functions called by the body {\it forms\/}.  The
  actions of these two functions can depend on implementation-dependent
  dynamic variables automatically bound before the generic function
  {\bf compute-effective-method} is called.

2-34 through 2-36: Replace the entire Examples section with the following:

Most examples of the long form of {\bf define-method-combination} also
illustrate the use of the related functions that are provided as part
of the declarative method combination facility.

\screen!
;;; Examples of the short form of define-method-combination

(define-method-combination and :identity-with-one-argument t) 

(defmethod func and ((x class1) y) ...)

;;; The equivalent of this example in the long form is:

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

;;; Examples of the long form of define-method-combination

;The default method-combination technique
(define-method-combination standard ()
        ((around (:around))
         (before (:before))
         (primary () :required t)
         (after (:after)))
  (flet ((call-methods (methods)
           (mapcar #'(lambda (method)
                       `(call-method ,method ()))
                   methods)))
    (let ((form (if (or before after (rest primary))
                    `(multiple-value-prog1
                       (progn ,@(call-methods before)
                              (call-method ,(first primary)
                                           ,(rest primary)))
                       ,@(call-methods (reverse after)))
                    `(call-method ,(first primary) ()))))
      (if around
          `(call-method ,(first around)
                        (,@(rest around)
                         (make-method ,form)))
          form))))

;A simple way to try several methods until one returns non-nil
(define-method-combination or ()
        ((methods (or)))
  `(or ,@(mapcar #'(lambda (method)
                     `(call-method ,method ()))
                 methods)))

;A more complete version of the preceding
(define-method-combination or 
        (&optional (order ':most-specific-first))
        ((around (:around))
         (primary (or)))
  ;; Process the order argument
  (case order
    (:most-specific-first)
    (:most-specific-last (setq primary (reverse primary)))
    (otherwise (method-combination-error "~S is an invalid order.~@
    :most-specific-first and :most-specific-last are the possible values."
                                         order)))
  ;; Must have a primary method
  (unless primary
    (method-combination-error "A primary method is required."))
  ;; Construct the form that calls the primary methods
  (let ((form (if (rest primary)
                  `(or ,@(mapcar #'(lambda (method)
                                     `(call-method ,method ()))
                                 primary))
                  `(call-method ,(first primary) ()))))
    ;; Wrap the around methods around that form
    (if around
        `(call-method ,(first around)
                      (,@(rest around)
                       (make-method ,form)))
        form)))

;The same thing, using the :order and :required keyword options
(define-method-combination or 
        (&optional (order ':most-specific-first))
        ((around (:around))
         (primary (or) :order order :required t))
  (let ((form (if (rest primary)
                  `(or ,@(mapcar #'(lambda (method)
                                     `(call-method ,method ()))
                                 primary))
                  `(call-method ,(first primary) ()))))
    (if around
        `(call-method ,(first around)
                      (,@(rest around)
                       (make-method ,form)))
        form)))

;This short-form call is behaviorally identical to the preceding
(define-method-combination or :identity-with-one-argument t)
 
;Order methods by positive integer qualifiers
;:around methods are disallowed to keep the example small
(define-method-combination example-method-combination ()
        ((methods positive-integer-qualifier-p))
  `(progn ,@(mapcar #'(lambda (method)
                        `(call-method ,method ()))
                    (stable-sort methods #'<
                      :key #'(lambda (method)
                               (first (method-qualifiers method)))))))

(defun positive-integer-qualifier-p (method-qualifiers)
  (and (= (length method-qualifiers) 1)
       (typep (first method-qualifiers) '(integer 0 *))))

;;; Example of the use of :arguments
(define-method-combination progn-with-lock ()
        ((methods ()))
  (:arguments object)
  `(unwind-protect
       (progn (lock (object-lock ,object))
              ,@(mapcar #'(lambda (method)
                            `(call-method ,method ()))
                        methods))
     (unlock (object-lock ,object))))

\endscreen!

2-36: In the See Also field, remove make-method-call, add
call-method and make-method.

2-58 through 2-59: Delete the writeup for make-method-call.
Replace it with the following two writeups:

\begincom{call-method}\ftype{Macro}

\label Purpose:

The macro {\bf call-method} is used in method combination.  It has
lexical scope and is only available within an effective method Lisp
form.  It hides the implementation-dependent details of how methods
are called.

Programmers always translate a method object into a Lisp form that
invokes the method by constructing a form that invokes the
{\bf call-method} macro with the method object as its first subform
and a list of next-method objects as its second subform.
The {\bf call-next-method} function available to the method that
is the first subform will call the first method in the list that
is the second subform.  The {\bf call-next-method} function
available in that method, in turn, will call the second
method in the list that is the second subform, and so on until
the list of next-methods is exhausted.

In place of a method object, as the first subform of {\bf call-method}
or as an element of the second subform of {\bf call-method},
a list can be used that is an invocation of the {\bf make-method}
macro.

\label Syntax:

\Defun {call-method} {method next-method-list}

\label See Also: 

{\bf call-next-method}

{\bf define-method-combination}

{\bf make-method}

\endcom

\begincom{make-method}\ftype{Macro}

\label Purpose:

The macro {\bf make-method} is used in method combination.  It has
lexical scope and is only available within an effective method Lisp
form.  An invocation of {\bf make-method} can only be used inside of an
invocation of {\bf call-method}.

Programmers translate a Lisp form into a method object whose body
is that form with the {\bf make-method} macro.  This is primarily
used when the next-method for {\bf call-next-method} is defined by
a form that calls several methods, rather than being a single
method object.

\label Syntax:

\Defun {make-method} {form}

\label See Also: 

{\bf call-method}

{\bf call-next-method}

{\bf define-method-combination}

\endcom