From: sross
Subject: Alias GF and rotate arguments
Date: 
Message-ID: <5bb2a85f-5a50-43cc-9e85-326b1d1c32cb@m15g2000vbl.googlegroups.com>
Hi all,

I have a particularly sticky problem that I am trying to solve. I am
trying to create an alias for a generic function which, when methods
are added to it, has an appropriate method added to the GF it is
aliasing.
The catch is that the method it is aliasing has a different order of
arguments and I am effectively unable to shadow and change defmethod.
Oh yes, and it has to be portably across  Lispworks, SBCL, CMUCL,
CLISP, OpenMCL and Allegro

What I'm trying to do is add support for ASDF to Mudballs and, while a
large chunk of this is complete, I need to allow users to specialize
ASDF:PERFORM which will add appropriate behavior to  SYSDEF:EXECUTE
This problem is that the order of arguments is reversed.
PERFORM'slambda list is (OP-CLASS SYSTEM) while EXECUTE's is (SYSTEM
OP-CLASS)

Now I've tried using a custom generic-function-class and specializing
add-method but make-method-lambda isn't quite as portably as one would
hope (even with closer-mop). Next was just trying to eval a defmethod
form but I cannot see a way to evaluate the appropriate code while
preserving call-next-method behaviour. And now I'm stuck.


Any Ideas would be greatly appreciated.

From: Thomas A. Russ
Subject: Re: Alias GF and rotate arguments
Date: 
Message-ID: <ymi8wo9u79y.fsf@blackcat.isi.edu>
sross <······@gmail.com> writes:

> Hi all,
> 
> I have a particularly sticky problem that I am trying to solve. I am
> trying to create an alias for a generic function which, when methods
> are added to it, has an appropriate method added to the GF it is
> aliasing.
> The catch is that the method it is aliasing has a different order of
> arguments and I am effectively unable to shadow and change defmethod.
> Oh yes, and it has to be portably across  Lispworks, SBCL, CMUCL,
> CLISP, OpenMCL and Allegro

Could you solve this by just putting an :AROUND method on the original
function that then calls your generic function instead?  If you also
need (or want) to use the existing dispatch methods for the original
function, you could perhaps do something like the following hack:

(defvar *top-level-invocation-of-original-gf* t)

(defmethod original-gf :around (x y)
  (if *top-level-invocation-of-original-gf*
      (let ((*top-level-invocation-of-original-gf* nil))
         (my-gf y x))
      (call-next-method)))


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal Costanza
Subject: Re: Alias GF and rotate arguments
Date: 
Message-ID: <6vnnogFkvu7iU1@mid.individual.net>
Thomas A. Russ wrote:
> sross <······@gmail.com> writes:
> 
>> Hi all,
>>
>> I have a particularly sticky problem that I am trying to solve. I am
>> trying to create an alias for a generic function which, when methods
>> are added to it, has an appropriate method added to the GF it is
>> aliasing.
>> The catch is that the method it is aliasing has a different order of
>> arguments and I am effectively unable to shadow and change defmethod.
>> Oh yes, and it has to be portably across  Lispworks, SBCL, CMUCL,
>> CLISP, OpenMCL and Allegro
> 
> Could you solve this by just putting an :AROUND method on the original
> function that then calls your generic function instead?  If you also
> need (or want) to use the existing dispatch methods for the original
> function, you could perhaps do something like the following hack:
> 
> (defvar *top-level-invocation-of-original-gf* t)
> 
> (defmethod original-gf :around (x y)
>   (if *top-level-invocation-of-original-gf*
>       (let ((*top-level-invocation-of-original-gf* nil))
>          (my-gf y x))
>       (call-next-method)))

In AspectJ, they don't call this a hack. :-P


Pascal

-- 
ELS'09: http://www.european-lisp-symposium.org/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Pascal Costanza
Subject: Re: Alias GF and rotate arguments
Date: 
Message-ID: <6vm3ghFki01kU1@mid.individual.net>
sross wrote:
> Hi all,
> 
> I have a particularly sticky problem that I am trying to solve. I am
> trying to create an alias for a generic function which, when methods
> are added to it, has an appropriate method added to the GF it is
> aliasing.
> The catch is that the method it is aliasing has a different order of
> arguments and I am effectively unable to shadow and change defmethod.
> Oh yes, and it has to be portably across  Lispworks, SBCL, CMUCL,
> CLISP, OpenMCL and Allegro
> 
> What I'm trying to do is add support for ASDF to Mudballs and, while a
> large chunk of this is complete, I need to allow users to specialize
> ASDF:PERFORM which will add appropriate behavior to  SYSDEF:EXECUTE
> This problem is that the order of arguments is reversed.
> PERFORM'slambda list is (OP-CLASS SYSTEM) while EXECUTE's is (SYSTEM
> OP-CLASS)
> 
> Now I've tried using a custom generic-function-class and specializing
> add-method but make-method-lambda isn't quite as portably as one would
> hope (even with closer-mop). Next was just trying to eval a defmethod
> form but I cannot see a way to evaluate the appropriate code while
> preserving call-next-method behaviour. And now I'm stuck.
> 
> 
> Any Ideas would be greatly appreciated.

You're out of luck if you want to do this using the CLOS MOP. CL 
implementations are not consistent in support the argument lists for 
method functions, as specified in AMOP, and support for 
make-method-lambda is almost non-existent.

You may have better chances with defining your own method combination: 
In extended method combinations, you can ensure that methods are invoked 
with a list of next methods that you determined yourself. In principle, 
it should be possible to add arbitrary methods of your own here that are 
not actually defined for the generic function at hand, so it should be 
possible to add methods from asdf:perform to effective methods for 
sysdef:execute. sysdef:execute can contain their own wrapper methods 
that perform a swap of the arguments when they perform a 
call-next-method on the method 'inherited' from asdf:perform. The 
details will be hairy, though, I fear.

Don't dismiss shadowing defmethod. I think that's the cleanest way to 
deal with these things. You could ask for collaboration from the ASDF 
maintainers: Ask them to use their own asdf:defmethod that expands into 
a cl:defmethod. Then you can replace asdf:defmethod with your own macro 
that performs the necessary extra steps. Or alternatively, you have to 
ask library providers to change the defmethod macro to use.

Pascal

-- 
ELS'09: http://www.european-lisp-symposium.org/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: sross
Subject: Re: Alias GF and rotate arguments
Date: 
Message-ID: <69b80bb1-4c47-4825-ba42-c62c3e312195@v38g2000yqb.googlegroups.com>
On Feb 13, 8:29 pm, Pascal Costanza <····@p-cos.net> wrote:
> You're out of luck if you want to do this using the CLOS MOP. CL
> implementations are not consistent in support the argument lists for
> method functions, as specified in AMOP, and support for
> make-method-lambda is almost non-existent.
>

Thanks Pascal, thats what I was afraid of.

I've managed to get something non-portable working (or at least it
appears to work) in a few implementations without resorting to make-
method-lambda.


(defclass wrap-execute-gf (standard-generic-function)
  ()
  (:metaclass funcallable-standard-class))

(defgeneric perform (operation system)
  (:generic-function-class wrap-execute-gf)
  (:documentation "The perform method is the ASDF equivalent to
execute. Defining a method on perform will
add an appropriate method to execute."))


;; we can't use (eql (ensure-generic-function 'perform)) as it breaks
under ACL.
(defmethod add-method ((perform-gf wrap-execute-gf) (method method))
  (let ((gf (ensure-generic-function 'execute)))
    (add-method gf (make-instance (generic-function-method-class gf)
                                  :qualifiers (method-qualifiers
method)
                                  :specializers (reverse (method-
specializers method))
                                  :lambda-list (generic-function-
lambda-list gf)
                                  :function (create-argument-rotater
method)))))


(defun create-argument-rotater (method)
  (let ((method-function (method-function method)))
    #'(lambda  (x #+cmucl &optional y)
        #+(or lispworks allegro ccl openmcl)
        (funcall method-function y x)
        #+(or sbcl clisp cmucl)
        (funcall method-function (reverse x) y))))


It's ugly, but it just might work.


sean.
From: Pascal Costanza
Subject: Re: Alias GF and rotate arguments
Date: 
Message-ID: <6vp54fFl8ireU3@mid.individual.net>
sross wrote:
> On Feb 13, 8:29 pm, Pascal Costanza <····@p-cos.net> wrote:
>> You're out of luck if you want to do this using the CLOS MOP. CL
>> implementations are not consistent in support the argument lists for
>> method functions, as specified in AMOP, and support for
>> make-method-lambda is almost non-existent.
>>
> 
> Thanks Pascal, thats what I was afraid of.
> 
> I've managed to get something non-portable working (or at least it
> appears to work) in a few implementations without resorting to make-
> method-lambda.
> 
> 
> (defclass wrap-execute-gf (standard-generic-function)
>   ()
>   (:metaclass funcallable-standard-class))
> 
> (defgeneric perform (operation system)
>   (:generic-function-class wrap-execute-gf)
>   (:documentation "The perform method is the ASDF equivalent to
> execute. Defining a method on perform will
> add an appropriate method to execute."))
> 
> 
> ;; we can't use (eql (ensure-generic-function 'perform)) as it breaks
> under ACL.
> (defmethod add-method ((perform-gf wrap-execute-gf) (method method))
>   (let ((gf (ensure-generic-function 'execute)))
>     (add-method gf (make-instance (generic-function-method-class gf)
>                                   :qualifiers (method-qualifiers
> method)
>                                   :specializers (reverse (method-
> specializers method))
>                                   :lambda-list (generic-function-
> lambda-list gf)
>                                   :function (create-argument-rotater
> method)))))
> 
> 
> (defun create-argument-rotater (method)
>   (let ((method-function (method-function method)))
>     #'(lambda  (x #+cmucl &optional y)
>         #+(or lispworks allegro ccl openmcl)
>         (funcall method-function y x)
>         #+(or sbcl clisp cmucl)
>         (funcall method-function (reverse x) y))))
> 
> 
> It's ugly, but it just might work.

As long as those methods don't call call-next-method, this might work.

Notes:

+ ACL is right in 'rejecting' (eql (ensure-generic-function 'perform)) 
because it's an instance of a predefined class. The CLOS MOP 
specification grants implementations this right.

+ You are overusing ensure-generic-function. Ensure-generic-function is 
primarily for creating a generic function at runtime that didn't exist 
yet. In your case, I would recommend to define it with a defgeneric, and 
then just look it up with symbol-function or fdefinition.


Pascal


-- 
ELS'09: http://www.european-lisp-symposium.org/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Kenneth Tilton
Subject: Re: Alias GF and rotate arguments
Date: 
Message-ID: <499746e8$0$5899$607ed4bc@cv.net>
sross wrote:
> Hi all,
> 
> I have a particularly sticky problem that I am trying to solve. I am
> trying to create an alias for a generic function which, when methods
> are added to it, has an appropriate method added to the GF it is
> aliasing.
> The catch is that the method it is aliasing has a different order of
> arguments and I am effectively unable to shadow and change defmethod.
> Oh yes, and it has to be portably across  Lispworks, SBCL, CMUCL,
> CLISP, OpenMCL and Allegro
> 
> What I'm trying to do is add support for ASDF to Mudballs and, while a
> large chunk of this is complete, I need to allow users to specialize
> ASDF:PERFORM which will add appropriate behavior to  SYSDEF:EXECUTE
> This problem is that the order of arguments is reversed.
> PERFORM'slambda list is (OP-CLASS SYSTEM) while EXECUTE's is (SYSTEM
> OP-CLASS)
> 
> Now I've tried using a custom generic-function-class and specializing
> add-method but make-method-lambda isn't quite as portably as one would
> hope (even with closer-mop). Next was just trying to eval a defmethod
> form but I cannot see a way to evaluate the appropriate code while
> preserving call-next-method behaviour. And now I'm stuck.
> 
> 
> Any Ideas would be greatly appreciated.

Sure: stop doing such a good job.

And/or re-examine your mission statement. Does it say "extend ASDF 
unchanged" or "push ASDF into the sea"? Hopefully the latter. If so, 
sorry you have done all that work but just throw it out and write up a 
one-page "Guide to Converting From ASDF". That does not have to be 
complete, either. System definition is not exactly a hard problem, ASDF 
just made it look that way.

hth, kenny
From: Anagram
Subject: Re: Alias GF and rotate arguments
Date: 
Message-ID: <pGDml.13740$g63.3739@newsfe24.iad>
sross <······@gmail.com> wrote in news:5bb2a85f-5a50-43cc-9e85-
············@m15g2000vbl.googlegroups.com:

> Any Ideas would be greatly appreciated.

Would it help to define two methods at a time on one GF, with their 
arguments reversed, and with one of them calling the other?