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

DEFINE-METHOD-COMBINATION issues



Here are some questions about unclarities in 88-002R revolving around
method combination, along with my suggested answers.  Any comments?
I think I will try to use the eventual resolution of this to improve
the writeup in the ANSI Common Lisp draft specification.

    Can CALL-NEXT-METHOD be called lexically within a MAKE-METHOD?

p.2-12 says CALL-NEXT-METHOD can be used in the body of a method defined
by a method-defining form, and 1-22 defines "method-defining form" and
does not include MAKE-METHOD, so I think that CALL-NEXT-METHOD is not
specified to work in MAKE-METHOD.  The 2-12 text may have been meant to
exclude methods created with the chapter 3 function MAKE-METHOD-LAMBDA,
but in fact it's clear from the draft of chapter 3 that CALL-NEXT-METHOD
does work in such methods.  So I don't really know what the intent of
2-12 was, but I think it would be safe to assume that CALL-NEXT-METHOD
need not be supported in MAKE-METHOD.  Example:

(call-method (make-method (or (frob) (call-next-method)))
             ,remaining-methods)

could be rewritten:

(or (frob) (call-method ,(first remaining-methods)
                        ,(rest remaining-methods)))

See additional discussion below.

    Does CALL-METHOD use the arguments of the effective method, or does it
    use the arguments of the MAKE-METHOD that it was a part of?  For
    example,
    
       (DEFMETHOD CHANGE :AROUND (X &OPTIONAL (N 1))
         (CALL-NEXT-METHOD X (MIN N 0)))
       
       (DEFMETHOD CHANGE (X &OPTIONAL (N 2))
         N)
    
    What does (CHANGE T 5) return?  A strict reading of the spec says 5,
    but my intuition says 0, and 0 is a lot easier to implement.

If this is standard method combination, and there are no other methods,
the effective method is

  (call-method <:around method>
               ((make-method (call-method <primary method>))))

and the target of the CALL-NEXT-METHOD is the MAKE-METHOD.
p.2-11: "The arguments are the arguments that were supplied to the
effective method form containing the invocation of CALL-METHOD."
But this is ambiguous, does "the effective method form" mean the whole
thing, or just the part containing the invocation of CALL-METHOD?  If it
had meant the whole thing, I think it would have said "the arguments
that were supplied to the generic function".  Also it makes more sense
that once call-next-method changes the arguments, they don't "randomly"
change back to the original arguments.  So I think the example returns
0.  I propose to change the text on p.2-11 to say "The arguments are the
arguments that were supplied to the innermost MAKE-METHOD-created
method enclosing the invocation of CALL-METHOD, or if there is no
MAKE-METHOD, the arguments that were supplied to the generic function."
I'd like to say this in fewer words, but couldn't think of a way.

    Is the effective method code a real Lisp form, i.e. can it contain
    forms which require closures, like:
    
       (BLOCK TOO-DEEP
         (CALL-METHOD ,(FIRST PRE-METHODS)
                      (,@(REST PRE-METHODS)
                       (MAKE-METHOD (RETURN-FROM TOO-DEEP NIL))))
         (CALL-METHOD ,(FIRST POST-METHODS)
                      (,@(REST POST-METHODS)
                       (MAKE-METHOD T))))

Nothing in chapter 1 or 2 mentions any restrictions on what the
effective method form can do, so the answer should be yes.  The
problem is that the description of MAKE-METHOD (p.2-11) fails to
specify the lexical environment of the form its given.  Nor does
anything specify the lexical environment of the overall effective
method form.  After thinking about it a bit, I believe the intention
was that both of these forms are in the null lexical environment.
Thus the answer is that this example code is not valid.

Unless there are further comments, I propose to change the ANSI
Common Lisp specification to say both effective method forms and
MAKE-METHOD forms are in the null lexical environment.  Note that
this implies no CALL-NEXT-METHOD inside of MAKE-METHOD.  And no
CONTINUE-WHOPPER inside of a DEFWRAPPER, to translate back to
Flavors terminology.

    In 12/15/88 MOP Draft number 10, some places seem to think that a
    METHOD-COMBINATION object consists of the information supplied to
    DEFGENERIC (with a mysterious "Documentation" slot), and other places
    seem to think that it consists of the information supplied to
    DEFINE-METHOD-COMBINATION, but the slots only make sense for the short
    form.

The MOP draft is not part of the spec.  The relevant text from 89-002R is
  
  A method for {\bf compute-effective-method} can be defined directly by
  using {\bf defmethod} or indirectly by using {\bf
  define-method-combination}.  A {\bit method combination object} is an
  object that encapsulates the method combination type and options
  specified by the {\bf :method-combination} option to forms that
  specify generic function options.
  
  \item{\bull} Every method combination object is an instance of a
  subclass of the class {\bf method-combination}.

  The {\bf :method-combination} argument [to ensure-generic-function] is a
  method combination object.

  The first argument of {\bf documentation} is either a symbol, a
  function specifier list of the form {\tt (setf {\it symbol\/})}, a
  method object, a class object, a generic function object, a method
  combination object, or a slot description object.

  The form {\tt (documentation {\it symbol\/} 'method-combination)} returns the
  documentation string of the method combination type named by the
  symbol.  
  
Note: method combination type, not method combination object.

I think this makes it clear that the role of a method combination object
is to hold the information obtained by parsing the :METHOD-COMBINATION
option to DEFGENERIC.  The division of labor between slots in the method
combination object, methods for compute-effective-method applicable to
the method combination object, and methods for other generic functions
applicable to the method combination object, is not specified in 88-002R.
It might be specified in chapter 3 (but probably won't be).

The documentation mentioned on p.3-8 is the same documentation you would
get by first extracting the method combination type symbol and then
calling the function DOCUMENTATION with two arguments, that symbol and
the symbol METHOD-COMBINATION.  Chapter 3 is not supposed to specify
the existence of any slots for any meta objects.  I couldn't find 
anything about slots of method combination objects, but I didn't look
real hard since the MOP draft is not part of the language specification.

I'm not sure that any documentation change is required to clarify this.

    If a method gets redefined, do the effective methods get destructively
    modified or do new ones get created to take their place?  For example,
    
       (DEFMETHOD STRANGE (X)
         (LOOP (CALL-NEXT-METHOD) (REMOVE-METHOD ..some strange method..)))
    
    does the CALL-NEXT-METHOD continue to see the next methods as they were
    when strange was entered, or does each time around the loop see
    something different, or is this undefined?

Common Lisp rarely specifies what happens if programs modify themselves.
I think the results should be undefined.  I would expect that most
implementations would capture the next methods at the time the generic
function was called, so modifications would not be seen, but some
implementations might let the modifications be seen, either because they
are not optimized at all, or because they are highly optimized and
reuse some storage.

I'm not sure that any documentation change is required to clarify this.

    When the :ARGUMENTS option is used with DEFINE-METHOD-COMBINATION,
    how do the arguments get matched up with the actual arguments of the
    generic function?

The issue here is that p.2-34 says "If lambda-list is not congruent to
the generic function's lambda-list, additional ignored parameters are
automatically inserted until it is congruent", but this doesn't say
where they are inserted nor what happens if inserting additional
parameters can't make it congruent (e.g. too many required parameters
to begin with).  Are they inserted at the end?  At the end of each
group (required, optional, keyword)?  At the beginning??

We thought about simplifying this to just require the two lambda-lists
to be congruent, but that doesn't work well since a single method
combination type should be useable with many different generic functions
with different lambda-lists.

I think the best answer is to delete the stuff about congruence (the
last two sentences of the first paragraph on p.2-34) and simply say what
happens if the arguments supplied to the generic function don't match
the lambda-list: if there are too few arguments, NIL is assumed for
missing arguments.  If there are too many arguments, the extra arguments
are ignored.  If there are unhandled keyword arguments, they are
ignored.  Supplied-p parameters work in the normal fashion.  Default
value forms are evaluated in the null lexical environment (except for
bindings of :ARGUMENTS parameters to their left, of course).  This is
more or less what the equivalent Flavors feature does.  Any objections?

Also, what happens if the effective method form returned by your
body forms includes (setq ,variable ...) or (setf ,variable ...),
where variable is one of the :ARGUMENTS parameters?  I think
the consequences should be undefined.

                                Does CALL-METHOD take the original
    arguments, the way CALL-NEXT-METHOD with no arguments does, or does the
    method combiner get a chance to change things around, perhaps supplying
    a default for an optional or something?

CALL-METHOD uses the original arguments, as clearly documented.  The
extension to CALL-METHOD to allow specifying arguments, as in
CALL-NEXT-METHOD, was rejected by the committee, I don't remember if it
was for a good reason.  No way is provided for the effective method form
to change the original arguments.  Perhaps the first paragraph in the
arguments section of CALL-NEXT-METHOD, which says you can't change the
original arguments, should be repeated under CALL-METHOD or under
DEFINE-METHOD-COMBINATION.