#

## Issue METHOD-COMBINATION-ARGUMENTS Writeup

    Issue:         METHOD-COMBINATION-ARGUMENTS

    References:    Draft ANSI CL specification p.6-71
                   88-002R p.2-34

    Category:      CLARIFICATION

    Edit history:  29-Apr-90, Version 1 by Moon
                   30-Apr-90, Version 2 by Moon (rewrite more clearly)
                   1-May-90, Version 3 by Moon (minor wording improvements)
                   4-May-90, Version 4 by Moon (update current practice)
                   6-Jun-90, Version 5 by Moon (add &WHOLE, update current practice)
                   8-Jun-90, Version 6 by Moon (include one-word amendment to &WHOLE
                                                that was passed by X3J13 meeting)

    Problem description:

      The :ARGUMENTS option to DEFINE-METHOD-COMBINATION is not specified very
      clearly.  In particular, different generic functions using the type of
      method combination being defined might accept different argument
      patterns, so the lambda-list in the :ARGUMENTS option is unlikely to be
      congruent to the generic function's lambda-list; the behavior when they
      are not congruent should be clearly specified.  Such mismatches often
      occur in practice, as in the example on p.6-74 where the generic function
      would typically have more than one argument.

      88-002R says:

        If 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 lambda-list to receive fewer
        arguments than the number that the generic function expects.

      The current ANSI CL draft says:

        If the arguments supplied to the generic function do not match
        lambda-list, extra arguments are ignored and missing arguments are
        defaulted to nil.  Thus it is permissible for lambda-list to receive
        fewer arguments than the number of required arguments for the generic
        function.

      This is Symbolics issue #10.

    Proposal (METHOD-COMBINATION-ARGUMENTS:CLARIFY):

      Replace the sentences quoted above with the following sentences.  Note
      that these sentences immediately follow "When this form is evaluated
      during execution of the effective method, its value is the corresponding
      argument to the generic function."

        This correspondence is computed by dividing the :ARGUMENTS lambda-list
        and the generic function lambda-list into three sections: the required
        parameters, the optional parameters, and the keyword/rest parameters.
        The arguments supplied to the generic function for a particular call
        are also divided into three sections; the required arguments section
        contains as many arguments as the generic function has required
        parameters, the optional arguments section contains as many arguments
        as the generic function has optional parameters, and the keyword/rest
        arguments section contains the remaining arguments.  Each parameter in
        the required and optional sections of the :ARGUMENTS lambda-list
        accesses the argument at the same position in the corresponding section
        of the arguments.  If the section of the :ARGUMENTS lambda-list is
        shorter, extra arguments are ignored.  If the section of the :ARGUMENTS
        lambda-list is longer, excess required parameters are bound to forms
        that evaluate to NIL and excess optional parameters are bound to their
        initforms.  The keyword/rest parameters in the :ARGUMENTS lambda-list
        access the keyword/rest section of the arguments.  If the :ARGUMENTS
        lambda-list contains &KEY, it behaves as if it also contained
        &ALLOW-OTHER-KEYS.

        In addition, &WHOLE <var> can be placed first in the :ARGUMENTS
        lambda-list.  It causes <var> to be bound to a form that evaluates to
        a list of all of the arguments supplied to the generic function.  This
        is different from &REST because it accesses all of the arguments, not
        just the keyword/rest arguments.

    Examples:

      The example in both documents is:

        ;;; 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))))

      This would be used as follows:

        (defgeneric send (channel buffer &optional (start 0) end)
          (:method-combination progn-with-lock))

        where each channel class has an object-lock method.

      A variation that uses non-required arguments in :arguments is:

        (define-method-combination progn-with-lock-2 ()
                ((methods ()))
          (:arguments &key lock)
          `(unwind-protect
              (progn (lock ,lock)
                     ,@(mapcar #'(lambda (method)
                                   `(call-method ,method))
                               methods))
             (unlock ,lock)))

      This would be used as follows:

        (defgeneric send (channel buffer
                          &optional (start 0) end
                          &key lock character-set-translation)
          (:method-combination progn-with-lock-2))

      The :lock keyword argument comes from the third section of the arguments,
      which for this generic function starts at the fourth argument.

      To show how lambda-list mismatch works:

        If (:ARGUMENTS a b c) meets DEFGENERIC (x y &optional z), the value of
        the value of C will be NIL, not the value of Z.

      To show the use of &WHOLE:

        (define-method-combination progn-with-gf-lock ()
                ((methods ()))
          (:arguments &whole args)
          (:generic-function gf)
          `(unwind-protect
               (progn (lock-gf-args ,gf ,args)
                      ,@(mapcar #'(lambda (method)
                                    `(call-method ,method))
                                methods))
             (unlock (unlock-gf-args ,gf ,args))))

    Rationale:

      This seems the only useful way to allow lambda-list-keywords in the
      :ARGUMENTS lambda-list while solving the congruency problems.  It's likely
      that this is not a change from what was originally intended, but just a
      more precise way of describing it.  The examples show why both &WHOLE and
      &REST are needed; using &REST in the progn-with-gf-lock example is not
      consistent with using &KEY in the progn-with-lock-2 example.

    Current practice:

      Symbolics Genera 8.0.1 (to be released in a few months) will implement the
      proposal, possibly omitting &WHOLE.  In Symbolics Genera 8.0 the :ARGUMENTS
      option to DEFINE-METHOD-COMBINATION does not work at all (it produces
      incorrect code that does not compile).

      TI Explorer release 6 has :ARGUMENTS in DEFINE-METHOD-COMBINATION, but
      its status relative to the proposed clarification is unknown at present.

      Recent versions of PCL support :ARGUMENTS in DEFINE-METHOD-COMBINATION
      but do not seem to conform to the proposal -- I think PCL just
      effectively appends "&rest ignore" to the :arguments lambda-list.

      I did not discover any other CLOS implementations that support
      :ARGUMENTS.

    Cost to Implementors:

      This does not make supporting :ARGUMENTS more difficult.

    Cost to Users:

      None.

    Cost of non-adoption:

      :ARGUMENTS will be specified in a way that can't be understood.

    Performance impact:

      None.

    Benefits:

      :ARGUMENTS will be usable in the relatively rare circumstances where it
      is needed.

    Esthetics:

      A specification that can be understood is more esthetic than one that
      cannot be.

    Discussion:

      None.

Copyright 1996-2005, LispWorks Ltd. All rights reserved.