From: Matthew D Swank
Subject: writing a dynamic CLOS proxy
Date: 
Message-ID: <pan.2005.12.14.22.26.36.639354@c.net>
To over simplify a little, in Smalltalk style OO I can
dynamically handle unimplemented methods by doing something useful with
the #doesNotUnderstand message.  CLOS has the NO-APPLICABLE-METHOD generic
function, but the original generic function (with the unimplemented
method) has to at least exist.  

Let's say I want write a wrapper for something like gtk-server
(http://www.turtle.dds.nl/gtk-server/index.html: basically it provides a
character stream, interpretive interface to GTK).  

What's the most basic scaffolding I can build to provide a CLOS interface
to the GTK framework, assuming I want to do as much as possible at
runtime (looking up and providing proxies for classes, objects, methods,
etc.)

Matt


-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.

From: Wade Humeniuk
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <La7of.204425$Io.74108@clgrps13>
Matthew D Swank wrote:
> To over simplify a little, in Smalltalk style OO I can
> dynamically handle unimplemented methods by doing something useful with
> the #doesNotUnderstand message.  CLOS has the NO-APPLICABLE-METHOD generic
> function, but the original generic function (with the unimplemented
> method) has to at least exist.  
> 

This is not correct, all you need to attempt a method dispatch is the
symbol,

(defclass test () ())

(defmethod testit ((obj test)) 10)

CL-USER 1 > (defparameter test (make-instance 'test))
TEST

CL-USER 2 > (testit test)
10

CL-USER 3 > (funcall 'testit test)
10

CL-USER 4 > (handler-case
                 (funcall 'bogus-method test)
               (error (c) (type-of c)))
UNDEFINED-FUNCTION

CL-USER 5 >

> Let's say I want write a wrapper for something like gtk-server
> (http://www.turtle.dds.nl/gtk-server/index.html: basically it provides a
> character stream, interpretive interface to GTK).  
> 
> What's the most basic scaffolding I can build to provide a CLOS interface
> to the GTK framework, assuming I want to do as much as possible at
> runtime (looking up and providing proxies for classes, objects, methods,
> etc.)
> 
> Matt
> 
> 
From: Pascal Bourguignon
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <87mzj2dayq.fsf@thalassa.informatimago.com>
Wade Humeniuk <··················@telus.net> writes:

> Matthew D Swank wrote:
>> To over simplify a little, in Smalltalk style OO I can
>> dynamically handle unimplemented methods by doing something useful with
>> the #doesNotUnderstand message.  CLOS has the NO-APPLICABLE-METHOD generic
>> function, but the original generic function (with the unimplemented
>> method) has to at least exist.  
>
> This is not correct, all you need to attempt a method dispatch is the
> symbol,
>
> (defclass test () ())
>
> (defmethod testit ((obj test)) 10)
>
> CL-USER 1 > (defparameter test (make-instance 'test))
> TEST
>
> CL-USER 2 > (testit test)
> 10
>
> CL-USER 3 > (funcall 'testit test)
> 10
>
> CL-USER 4 > (handler-case
>                  (funcall 'bogus-method test)
>                (error (c) (type-of c)))
> UNDEFINED-FUNCTION

Which is to say that in lisp, you have a #doesNotUnderstand for ALL
function calls, not only for messages.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Cats meow out of angst
"Thumbs! If only we had thumbs!
We could break so much!"
From: Pascal Costanza
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <40codkF19rnrdU1@individual.net>
Pascal Bourguignon wrote:
> Wade Humeniuk <··················@telus.net> writes:
> 
> 
>>Matthew D Swank wrote:
>>
>>>To over simplify a little, in Smalltalk style OO I can
>>>dynamically handle unimplemented methods by doing something useful with
>>>the #doesNotUnderstand message.  CLOS has the NO-APPLICABLE-METHOD generic
>>>function, but the original generic function (with the unimplemented
>>>method) has to at least exist.  
>>
>>This is not correct, all you need to attempt a method dispatch is the
>>symbol,
>>
>>(defclass test () ())
>>
>>(defmethod testit ((obj test)) 10)
>>
>>CL-USER 1 > (defparameter test (make-instance 'test))
>>TEST
>>
>>CL-USER 2 > (testit test)
>>10
>>
>>CL-USER 3 > (funcall 'testit test)
>>10
>>
>>CL-USER 4 > (handler-case
>>                 (funcall 'bogus-method test)
>>               (error (c) (type-of c)))
>>UNDEFINED-FUNCTION
> 
> 
> Which is to say that in lisp, you have a #doesNotUnderstand for ALL
> function calls, not only for messages.

That's misleading. The error object doesn't contain enough information 
to do something useful with this error - it lacks the arguments that 
were passed to the function. Furthermore, you cannot provide 
class-specific behavior for handling this error.

Smalltalk's #doesNotUnderstand is more flexible in this regard because 
each class can provide its own method for #doesNotUnderstand. Since it 
is a single-dispatch language, it is always clear who is responsible for 
handling #doesNotUnderstand, and since the arguments are available, 
other methods can be called to resolve the lack of the method in question.


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: Matthew D Swank
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <pan.2005.12.15.15.23.24.323088@c.net>
On Thu, 15 Dec 2005 09:39:15 +0100, Pascal Costanza wrote:

> Pascal Bourguignon wrote:
>> Wade Humeniuk <··················@telus.net> writes:
>> 
>>>CL-USER 4 > (handler-case
>>>                 (funcall 'bogus-method test)
>>>               (error (c) (type-of c)))
>>>UNDEFINED-FUNCTION
>> 
>> 
>> Which is to say that in lisp, you have a #doesNotUnderstand for ALL
>> function calls, not only for messages.
> 
> That's misleading. The error object doesn't contain enough information 
> to do something useful with this error - it lacks the arguments that 
> were passed to the function. Furthermore, you cannot provide 
> class-specific behavior for handling this error.

This brings up an interesting point though.  Are there any hooks into the
default function dispatch that would allow more information about the
function application to be returned as a condition (w/o shadowing
funcall/apply)?

Matt

-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.
From: Pascal Costanza
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <40dlltF19ohocU1@individual.net>
Matthew D Swank wrote:
> On Thu, 15 Dec 2005 09:39:15 +0100, Pascal Costanza wrote:
> 
> 
>>Pascal Bourguignon wrote:
>>
>>>Wade Humeniuk <··················@telus.net> writes:
>>>
>>>
>>>>CL-USER 4 > (handler-case
>>>>                (funcall 'bogus-method test)
>>>>              (error (c) (type-of c)))
>>>>UNDEFINED-FUNCTION
>>>
>>>
>>>Which is to say that in lisp, you have a #doesNotUnderstand for ALL
>>>function calls, not only for messages.
>>
>>That's misleading. The error object doesn't contain enough information 
>>to do something useful with this error - it lacks the arguments that 
>>were passed to the function. Furthermore, you cannot provide 
>>class-specific behavior for handling this error.
> 
> This brings up an interesting point though.  Are there any hooks into the
> default function dispatch that would allow more information about the
> function application to be returned as a condition (w/o shadowing
> funcall/apply)?

Not in ANSI CL, AFAICT. There's only no-applicable-method. 
Interestingly, according to 11.1.2.1.2 Constraints on the COMMON-LISP 
Package for Conforming Programs, you are not allowed to define any 
method on that function.

So you have to define your own subclass of standard-generic-function in 
order to be able to hook into the dispatch mechanism. (or you hack your 
own version of CL from one of the open source implementations ;)


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: Nikodemus Siivola
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <1134685678.914138.92480@g47g2000cwa.googlegroups.com>
> Interestingly, according to 11.1.2.1.2 Constraints on the COMMON-LISP
> Package for Conforming Programs, you are not allowed to define any

Interestingly NO-APPLICABLE-METHOD says that programmers _are_ allowed
to define methods for it, and given that you can't subclass
STANDARD-GENERIC-FUNCTION with plain ANSI (I think/hope) it "obviously"
implies a weaker restriction then that... ;-)

Oodles of fun.

Cheers,

  -- Nikodemus Siivola
From: Raffael Cavallaro
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <2005121522270250073-raffaelcavallaro@pasdespamsilvousplaitmaccom>
On 2005-12-15 17:27:58 -0500, "Nikodemus Siivola" 
<·········@random-state.net> said:

>> 
> 
> Interestingly NO-APPLICABLE-METHOD says that programmers _are_ allowed
> to define methods for it, and given that you can't subclass
> STANDARD-GENERIC-FUNCTION with plain ANSI (I think/hope) it "obviously"
> implies a weaker restriction then that... ;-)
> 
> Oodles of fun.

From the hyperspec (emphasis added of course):

"11.1.2.1.2 Constraints on the COMMON-LISP Package for Conforming Programs
Except where explicitly allowed, the consequences are undefined if any 
of the following actions are performed on an external symbol of the
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
COMMON-LISP package:"

and, as you point out, no-applicable-method explicitly allows methods 
to be defined.


With regard to Matt's original question, I know nothing about gtk, but 
this seems to work in lispworks, openmcl, sbcl and abcl...


(defclass class-with-no-gtk-methods-specialized-on-it () ())

(defvar *my-instance-with-no-gtk-methods* (make-instance 
'class-with-no-gtk-methods-specialized-on-it))

(defgeneric some-gtk-wrapper (an-object))

(defmethod no-applicable-method ((a-generic-function (eql 
#'some-gtk-wrapper)) &rest gf-args)
  (look-up-appropriate-gtk-method-for-instance (first gf-args)))

(defun look-up-appropriate-gtk-method-for-instance (an-object)
  (format t "I'm looking up the right gtk method for the object ~a, an 
instance of the class ~a, really I am." an-object (class-of an-object)))

CL-USER 199 > (some-gtk-wrapper *my-instance-with-no-gtk-methods*)
I'm looking up the right gtk method for the object 
#<CLASS-WITH-NO-GTK-METHODS-SPECIALIZED-ON-IT 10C65FB3>, an instance of 
the class #<STANDARD-CLASS CLASS-WITH-NO-GTK-METHODS-SPECIALIZED-ON-IT 
10C4A003>, really I am.
NIL


...but breaks in ecl:
> (load "/raflisp/no-applicable-method.lisp")
;;; Loading "/raflisp/no-applicable-method.lisp"
;;; Warning: NO-APPLICABLE-METHOD is being redefined.
"/raflisp/no-applicable-method.lisp"
> (some-gtk-wrapper *my-instance-with-no-gtk-methods*)
I'm looking up the right gtk method for the object NIL, an instance of 
the class #<The BUILT-IN-CLASS NULL>, really I am.The function NIL is 
undefined.
Broken at EVAL.
>>

regards,

Ralph
From: Matthew D Swank
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <pan.2005.12.16.07.22.40.547487@c.net>
On Fri, 16 Dec 2005 03:27:02 +0000, Raffael Cavallaro wrote:
> With regard to Matt's original question,
...
> (defclass class-with-no-gtk-methods-specialized-on-it () ())
> 
> (defvar *my-instance-with-no-gtk-methods* (make-instance 
> 'class-with-no-gtk-methods-specialized-on-it))
> 
> (defgeneric some-gtk-wrapper (an-object))
> 
> (defmethod no-applicable-method ((a-generic-function (eql 
> #'some-gtk-wrapper)) &rest gf-args)
>   (look-up-appropriate-gtk-method-for-instance (first gf-args)))
> 
> (defun look-up-appropriate-gtk-method-for-instance (an-object)
>   (format t "I'm looking up the right gtk method for the object ~a, an 
> instance of the class ~a, really I am." an-object (class-of an-object)))
> 



Yes, this is the standard CLOS-y way of doing this, but I either need a
wrapper generic function for every library function I wrap, or
make the wrapper function a "send" or "apply" verb, with the first
argument being the target function name.

btw thanks to you and everyone else for their comments.  Common Lisp
never disappoints in the multiplicity of different ways to do something
dynamic.

Matt       


-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.
From: Raffael Cavallaro
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <2005121608342643658-raffaelcavallaro@pasdespamsilvousplaitmaccom>
On 2005-12-16 02:22:46 -0500, Matthew D Swank 
<·······································@c.net> said:

> 
> or
> make the wrapper function a "send" or "apply" verb, with the first
> argument being the target function name.

This would be the way to go imho, unless there's a way to auto-generate 
the many wrapper gfs from headers or somesuch.

regards,

Ralph
From: Juanjo
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <1137056888.494264.236000@g14g2000cwa.googlegroups.com>
Raffael Cavallaro schrieb:
> On 2005-12-15 17:27:58 -0500, "Nikodemus Siivola"
> <·········@random-state.net> said:
>
> >>
> >
> > Interestingly NO-APPLICABLE-METHOD says that programmers _are_ allowed
> > to define methods for it, and given that you can't subclass
> > STANDARD-GENERIC-FUNCTION with plain ANSI (I think/hope) it "obviously"
> > implies a weaker restriction then that... ;-)
> >
> With regard to Matt's original question, I know nothing about gtk, but
> this seems to work in lispworks, openmcl, sbcl and abcl...
> [...]
> ...but breaks in ecl:

Contrary to the standard, ECL did not expect NO-APPLICABLE-METHOD to
return. This has been fixed in the CVS tree.

Juanjo
From: Matthew D Swank
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <pan.2005.12.15.20.29.45.966115@c.net>
On Thu, 15 Dec 2005 17:58:36 +0100, Pascal Costanza wrote:

> Matthew D Swank wrote:
>> Are there any hooks into the default function dispatch that would allow
>> more information about the function application to be returned as a
>> condition (w/o shadowing funcall/apply)?
> 
> Not in ANSI CL, AFAICT. There's only no-applicable-method.
> 

Well this works in CLISP, but it doesn't seem very portable (it would
require an implementation to implement a default use-value restart for
function-cells).

(defun silly-dispatch (name args)
  (list name (apply #'+ args)))

(defmacro with-alt-dispatch (&body forms)
  `(handler-bind ((undefined-function 
                   #'(lambda (c)
                       (use-value #'(lambda (&rest args)
                                      (silly-dispatch (cell-error-name c)
                                                       args))))))
     ,@forms))

(with-alt-dispatch (list (* 2 2) (- 7 3) (unbound-fun 2 3 4 5)))

==> (4 4 (unbound-fun 14))

-- 
"You do not really understand something unless you can
 explain it to your grandmother." — Albert Einstein.
From: Pascal Costanza
Subject: Re: writing a dynamic CLOS proxy
Date: 
Message-ID: <40co1bF19p4o9U1@individual.net>
Matthew D Swank wrote:
> To over simplify a little, in Smalltalk style OO I can
> dynamically handle unimplemented methods by doing something useful with
> the #doesNotUnderstand message.  CLOS has the NO-APPLICABLE-METHOD generic
> function, but the original generic function (with the unimplemented
> method) has to at least exist.

I am convinced that something like this isn't possible in plain CLOS. 
This is the one downside of the fact that methods belong to generic 
functions and not to classes.

> Let's say I want write a wrapper for something like gtk-server
> (http://www.turtle.dds.nl/gtk-server/index.html: basically it provides a
> character stream, interpretive interface to GTK).  
> 
> What's the most basic scaffolding I can build to provide a CLOS interface
> to the GTK framework, assuming I want to do as much as possible at
> runtime (looking up and providing proxies for classes, objects, methods,
> etc.)

I have sketched an implementation of delegation (i.e., objects 
forwarding generic function calls to other objects with proper rebinding 
of the arguments) using the CLOS MOP, but this fails on a number of CLOS 
MOP implementations. It's also quite tricky.

I can imagine the following workaround for your case:

- define your own generic function class:

(defclass forwarding-generic-function (standard-generic-function)
   ())

- Override no-applicable-method:

(defmethod no-applicable-method ((gf forwarding-generic-function) ...)
   ...)

-> There you can decide how to forward function calls to other objects.

- Declare all your generic functions to be forward-generic-functions. 
This means that all generic functions have to be introduced via defgeneric.

- This means that you cannot affect generic functions of other 
libraries. However, I guess that shadowing and redefining them 
appropriately should do the job.


Of course, this is all not tested.

Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/