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

Re: CLOS lambda-list congruence

    Date: Tue, 20 Dec 88 11:25:38 CST
    From: David N Gray <Gray@DSG.csc.ti.com>

I should have commented on this sooner, sorry for the delay.  Note that
this message is cc'd to the CommonLoops list since it will be of
interest to the people there.

    >    > If you want to preserve the old semantics, and alternative
    >    > is to implement your own generic function type using the
    >    > metaobject protocol, which relaxes the restriction on lambda
    >    > list congruence.
    >    Looking at document 88-003, I can't find any provision for being
    >    able to do that.  Is that a new feature in the forthcoming draft?
    Do you mean write your own ADD-METHOD method?  There aren't enough
    lower level primitives defined to be able to do that in a portable
    way, and if you need to modify internals of the implementation, then
    what is the point of a standard metaclass protocol?

    I think there must be something seriously wrong here somewhere if
    everyone believes that it is possible and reasonable to use the
    metaclass protocol to relax the congruence rules, but nobody can
    explain how to do it.

Let me start by making to important points about the metaobject
protocol.  I will then present a solution to your particular problem.

The first, and most important point, is that you shouldn't look to the
metaobject protocol to provide special purpose "hooks" for customizing
particular aspects of the CLOS behavior.  In the case of your problem,
there is an easy solution, but no canned hook.

The second point is that any particular capability in the metaobject
protocol always costs.  It makes that part of the protocol more complex,
and can make other parts more complex as well.  In addition, anytime
part of the CLOS behavior is controlled by a protocol (rather than being
hardwired), it complicates implementation.  The current design is a
balance between providing flexibility to the user without specifying a
large and difficult to implement protocol.

There is a way to solve your problem short of defining a completely new
kind of generic function.  The basic idea behind this solution is to
transform the lambda list of each method of a generic function in a
uniform way, so that the methods appear to be following a more relaxed
congruence rule, but are still satisfying the rules required by the
underlying CLOS.

For each method function, the user supplied lambda list will be reduced
to a most general common form using &rest.  The body of the method
function will then pull values out of the &rest argument into whatever
&optional or &key arguments may have been supplied.

This is done by defining a new generic function class.  For any generic
function of that class all methods will be of a corresponding new method
class.  The method functions for that generic function and method class
will be automatically transformed to use reduced lambda lists.

What follows is a sketch of an implementation.  A real implementation
would want to do more error checking and have fewer performance
bogosities in it.

I have checked this code, but I cannot test it completely since PCL does
not yet implement the metaobject protocol specified in the most recent
draft of the MOP.

;;; Given a lambda list with any &mumble args in it, replace it with
;;; &rest .rest.. This could use other reduction rules too depending
;;; on your needs.
;;;   (a b c)                 ==>  (A B C)
;;;   (a b &optional c)       ==>  (A B &REST .REST.)
;;;   (a b &key c)            ==>  (A B &REST .REST.)
(defun reduce-ll (ll)

;;; If the lambda list has no &mumble keywords in it, this returns nil,
;;; otherwise it returns the tail of the lambda list starting with
;;; the first &mumble keyword.
(defun ll-&mumble-part (ll)
  (member lambda-list-keywords ll :test #'(lambda (x y) (member y x))))

(defclass reduced-generic-function (standard-generic-function)
  (:metaclass funcallable-standard-class))

(defmethod generic-function-default-method-class
	   ((gf reduced-generic-function))
  (find-class 'reduced-method))

(defclass reduced-method (standard-method)

(defmethod shared-initialize 
          ((method standard-method) &rest all-keys &key lambda-list)
  (setf (slot-value method 'unreduced-ll) lambda-list)
  (apply #'call-next-method method
			    (reduce-ll lambda-list)

;;; This implementation ignores processing of declarations to be more
;;; clear.  They aren't hard to add.
(defmethod make-method-lambda ((gf standard-generic-function)
			       (method reduced-method)
  (let ((&mumble (ll-&mumble-part (cadr lambda))))
      (if &mumble
	  `(lambda ,(reduce-ll (cadr lambda))
	     (apply #'(lambda ,&mumble . ,(cddr lambda)) .REST.))