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

Re: generic dispatch doesn't work always



> Date: Thu, 23 Jan 1992 12:07:17 -0500
> From: bill@cambridge.apple.com
> 
> >I am programming no-applicable method to forward calls that cannot be
> >handled by some objects. I am in trouble, however, since the generic
> >dispatch doesn't work in the following case:
> >
> >(defclass z-class () ())
> >(defmethod z-method ((self integer) &key x-key)
> >  x-key)
> >(setq z (make-instance 'z-class))
> >(z-method z :my-key 'foo)
> >
> >> Error: Bad keyword :MY-KEY to #<Compiled Generic-function Z-METHOD
> >#x36BF7E>.
> >>        arglist: (#<Z-CLASS #x36D281> :MY-KEY FOO)
> >>        Allowable keys: #()
> >> While executing: CCL::%CHECK-KEYWORDS-BAD-KEY
> >> Type Command-. to abort.
> >
> >Instead of error "Bad keyword" it should call no-applicable-method. Because
> >it traps to an error before that I can't do anything. It happens if there
> >are keyword options in the call. I checked the same case on Symbolics where
> >it works as it should.
> >
> >Matti Karjalainen
> >Helsinki University of Technology
> 
> I believe MCL follows the letter of the law in this regard while Symbolics
> does not. CLtL2 p. 793 says, "If a generic function is passed a keyword
> argument that no applicable method accepts, an error is signaled." I can,
> however, understand that it is more useful to call no-applicable-method
> than to signal the bad keyword error. The fix is easy. Here's a patch
> (included as source as there are a number of versions out there with
> incompatible FASL formats):

Gack!  While Bill is correct in saying that MCL exhibits the required behavior, 
this is *by far* the worst of the five (!) ways I quickly thought of to fix the 
described problem.  Here are my proposed solutions, ordered from best to worst 
(my opinion of the ordering, of course).

1. Add the following defgeneric form to the front of the example:

    (defgeneric z-method (object &rest keys))

2. Do the delegation explicitly, by adding the following method definition:

    (defmethod z-method ((object t) &rest keys &key &allow-other-keys)
      (apply #'no-applicable-method #'z-method object keys))

[This is unaesthetic and also adds some obvious performance overhead.]

3. Add the following defgeneric form to the front of the example:

    (defgeneric z-method (object &key &allow-other-keys))

[This was the first solution I thought of after reading the message, but I 
think (1) is better since this one turns of keyword checking for all calls to 
z-method, not just the ones that are supposed to get delegated.  The only 
advantage this one has is if you wanted to name some keys in the defgeneric 
form, indicating that all methods defined for the function must accept those 
keys.]

4. Think about what's being done.  Errors like this sometimes indicate a bug in 
the design of the program.  Not always, but often enough that it doesn't hurt 
to consider the possibility.

5. Blow off the error checking for *all* generic functions by loading the 
patch.


Also a word of warning.  Some implementors have considered no-applicable-method 
and similar functions to be "error situations" and so haven't put much effort 
into making such situations fast.  The typical "bug" is to not record a "no 
applicable method" argument pattern in the generic function's cache, resulting 
in each such "miss" going through the full method lookup process.  Obviously 
the performance consequences are pretty dreadful if your use of this form of 
delegation is at all significant.  MCL got this right (I asked; I've used this 
technique quite a bit too), but its something to watch out for if you are 
porting your code around.