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
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)
(progn (lock (object-lock ,object))
`(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)
(progn (lock ,lock)
`(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)
(progn (lock-gf-args ,gf ,args)
`(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.