This excerpt is from "Dynamic Productivity with Ruby", at
http://www.artima.com/intv/tuesday.html
>Yukihiro Matsumoto: First of all, you don't have to use that feature.
>The most useful application of dynamic features, such as adding methods
>to objects, is meta-programming. Such features allow you to create a
>library that adapts to the environment, but they are not for casual uses.
>
>Bill Venners: What's an example of a library that adapts to the environment?
>
>Yukihiro Matsumoto: One example is a proxy. Instead of designing individual
>proxy classes for each particular class, in Ruby you can create an all
>purpose proxy that can wrap any object. The proxy can probe the object
>inside of it and just morph into the proxy for that object. The proxy can
>add methods to itself so it has the same interface as the wrapped object,
>and each of those methods can delegate to the corresponding method in the
>wrapped object. So an all-purpose proxy, which can be used to wrap any
>object, is an example of how a library class can adapt to the environment.
I thought this was really cool, to have a proxy that can be handed an
arbitrary object, have the proxy probe the object to find out what
messages/methods it responds to, and create a wrapper object for just
that object, and not a class of objects.
How would this be done in Common Lisp? Would you have to make a custom
class & methods for each object, and if that is the case, how would
the custom classes get garbage collected when the instance is done?
······@bellsouth.net (Ralph Richard Cook) wrote in message news:<···············@newsgroups.bellsouth.net>...
> I thought this was really cool, to have a proxy that can be handed an
> arbitrary object, have the proxy probe the object to find out what
> messages/methods it responds to, and create a wrapper object for just
> that object, and not a class of objects.
>
> How would this be done in Common Lisp? Would you have to make a custom
> class & methods for each object, and if that is the case, how would
> the custom classes get garbage collected when the instance is done?
No, you would specialize the leftmost parameter of the methods to your
proxy class. Then invoke on the object from there.
(defmethod foo ((p proxy) arg2 arg3)
(foo (target-of p) arg2 arg3))
But I think the real point here is to have this automated.
You can't do this in Lisp because, firstly, there is no such thing as
a class with an interface. There are just methods, which are
specialized to classes in any of their parameters. Moreover, the list
of methods is open-ended. Even if you could iterate over all the
methods which are specialized on their leftmost parameter to a given
object's class, and generate wrappers for them, new methods could be
added after the fact, rendering your wrapping job obsolete.
The question remains, what is the proxy going to do with these
methods? There could be any number of them, having arbitrary parameter
lists. If this exercise has any point to it, there has to be a common
body that is executed for all of these calls: the proxy instruments
calls to the original object for a reason! And when you have such a
reason to instrument calls for purposes other than producing a debug
trace, you can no longer be that indiscriminate about *which* calls
you instrument. You are in the realm of aspect-oriented programming,
where it matters what ``join points'' you select to form a point cut.
From: Frode Vatvedt Fjeld
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <2had1gc29g.fsf@vserver.cs.uit.no>
···@ashi.footprints.net (Kaz Kylheku) writes:
> (defmethod foo ((p proxy) arg2 arg3)
> (foo (target-of p) arg2 arg3))
>
> But I think the real point here is to have this automated. [..]
I think it would be possible to do this using the MOP, e.g. by
specializing compute-applicable-methods-using-classes and maybe some
others, so as to derail gf calls involving proxy arguments to do
whatever it is one wishes to do with such calls. But also I can't
quite see this being useful, though.
--
Frode Vatvedt Fjeld
Here is another attempt at interpreting Matz's query.
When I think of where proxies are used, I usually think of network
proxies. How about considering the case where we want to move the
storage of class data to network server.
You would then create an object which instead of storing data locally,
transfers it to and from a network server. One might like to
create an object including any expected MAKE-INSTANCE keys:
(make-proxy CLASS-NAME (list :tcp SERVER PORT) ...)
Where any method performed on this new object will accesses/updates
data using some serial communication protocol with the port PORT on
the remote server SERVER, but otherwise behave like the class
CLASS-NAME.
It seems that only the slot access methods would need to be affected.
If this new proxy class is subclassed from CLASS-NAME, it should be able
to use any of the other methods defined on that class.
I have no idea how to go about this, but it does seem to only need
access to structure set up by CLASS-NAME's original DEFCLASS.
--
Barry Fishman
On Tue, 13 Apr 2004 16:04:38 +0000, Barry Fishman wrote:
> Here is another attempt at interpreting Matz's query.
>
> When I think of where proxies are used, I usually think of network
> proxies. How about considering the case where we want to move the storage
> of class data to network server.
CLOS wih MOP can do that. See
http://www-db.stanford.edu/~paepcke/shared-documents/mopintro.ps
Jakub Travnik
·············@jabber.com
ICQ: 66770334
GnuPG key: http://jtra.sh.cvut.cz/jtra_key.asc
Ralph Richard Cook wrote:
>>Yukihiro Matsumoto: One example is a proxy. Instead of designing individual
>>proxy classes for each particular class, in Ruby you can create an all
>>purpose proxy that can wrap any object. The proxy can probe the object
>>inside of it and just morph into the proxy for that object. The proxy can
>>add methods to itself so it has the same interface as the wrapped object,
>>and each of those methods can delegate to the corresponding method in the
>>wrapped object. So an all-purpose proxy, which can be used to wrap any
>>object, is an example of how a library class can adapt to the environment.
>
> I thought this was really cool, to have a proxy that can be handed an
> arbitrary object, have the proxy probe the object to find out what
> messages/methods it responds to, and create a wrapper object for just
> that object, and not a class of objects.
>
> How would this be done in Common Lisp? Would you have to make a custom
> class & methods for each object, and if that is the case, how would
> the custom classes get garbage collected when the instance is done?
It's not clear to me what he is exactly talking about. AFAIK, a proxy
object is an object that is handed around in place of another one, and
takes care of instantiating that other object as soon as is it is
needed. A wrapper is an object that intercepts messages to a wrapped
object in order to add more behavior.
It depends on what you actually need in order to implement these things
in Common Lisp. An important point is that Common Lisp does not revolve
around the notion of objects, as is usually the case in languages like
Smalltalk, Java and Ruby, but rather around the concept of generic
functions. Functions do not belong to objects, or their classes, but
rather functions operate on objects. So if you want to wrap functions
with additional behavior, you would just add before/after/around methods
to the functions that you want to wrap.
If you really would like to add behavior to a whole class, you would use
mixins - i.e. classes that are specifically designed to provide types
that additional before/after/around methods can refer to.
If you want to add behavior to a single object, you can use eql
specializers.
If you want to be more generic and want to wrap all functions that
operate on a certain class of objects, or on certain objects, you need
to use the metaobject protocol (MOP) that, although not part of the ANSI
standard, is part of most CLOS implementations, with some variations.
On the one hand, the MOP provides introspective capabilities. That is,
you can determine all the superclasses and subclasses of a class, and as
a next step, determine all the generic functions that specialize in one
or more parameters on the so determined classes. Then, you can define
additional before/after/around methods on the so determined generic
functions, with eql specializers if necessary.
On the other hand, the MOP also provides intercession. This is achieved
via metaclasses - each class is itself an object that is an instance of
a metaclass. All the CLOS classes are by default instances of
standard-class, but you can derive your own subclass of standard-class,
for example proxy-standard-class. Then you can use that new metaclass to
implement new behavior for objects of classes that are instances of
proxy-standard-class. In this context, it is important to note that slot
(field) accesses go through generic functions. [1] For proxy objects, it
is sufficient to wrap just the slot accesses - all other generic
functions can be left unchanged. (They have to use the slots at some
stage - otherwise there would not be a need for proxies.)
This means that you can add methods to slot-value-using-class and
implement the proxy behavior there. (For example, you can use
change-class to replace the proxy object with the real one.)
Sometimes, you don't need the full capabilities of the MOP, but can live
with a combination of mixins and modified slot accesses. In such cases,
you don't need the MOP at all, because CLOS already provides a
restricted way to modify slot accesses via the generic function
slot-missing.
Note that it is not easy to achieve a truly super-general way to wrap
all objects that might ever come up. However, it is very unlikely that
you really want that. For example, Common Lisp also still provides a
large number of functions that are not generic, because there are
situations in which genericity is counterproductive. Sooner or later,
you want to control in more detail where you want to allow, and where
you want to forbid, genericity anyway.
This is a very rough overview what is possible in CLOS + MOP. As I said,
the quoted passage above is too fuzzy to give more specific hints.
Otherwise, this would turn out in a complete tutorial. If you want more
information, I recommend the following texts:
- Andreas Paepcke, "User-level language crafting" - at his website
- Gregor Kiczales et al., "The Art of the Metaobject Protocol" (book)
- http://www.lisp.org/mop
Pascal
[1] There are some provisions in the MOP specification that ensure that
this doesn't imply a general performance penalty.
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Here's another crack at interpreting what Matz meant (at least it's
what I mean).
We have a class, Foo, with method foo-method and an instance of it,
my-foo. We also have a class, Bar with method bar-method, and an
instance my-bar.
Let's say we have a class LoggerWrapper. This is what Matz meant by
the Proxy, perhaps a Decorator is more accurate. When you make a
LoggerWrapper, you give it an object, for example my-foo, and what you
get is an object that can respond to foo-method, just like a Foo, but
it also logs that foo-method was called. If you made a LoggerWrapper
with my-bar, you'd get something that would respond to bar-method,
just like a Bar, logging calls to this instance's calls to bar-method.
A LoggerWrapper instance doesn't know ahead of time what methods it
would be logging, it determines that at runtime based on the object
passed to it at construction time.
What you're NOT doing is logging the calls to the methods for all
instances of Foo or Bar. You're just logging the calls to foo-method
made by my-foo, and the calls to bar-method made by my-bar.
In Common Lisp, it would be possible to construct a new foo-method
with an eql specifier on my-foo, but since the methods aren't
associated with instances, the method would stick around after my-foo
is garbage collected and you could wind up adding more and more
methods that couldn't be reached.
Ralph Richard Cook wrote:
> In Common Lisp, it would be possible to construct a new foo-method
> with an eql specifier on my-foo, but since the methods aren't
> associated with instances, the method would stick around after my-foo
> is garbage collected and you could wind up adding more and more
> methods that couldn't be reached.
Hmm, what prevents implementations from garbage collecting such methods?
In any case, if you're worried about this, you can associate a finalizer
with the involved object that removes the respective method as soon as
the object is gc'ed. Many CL implementations provide such finalizers.
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Pascal Costanza <········@web.de> wrote:
>
>Ralph Richard Cook wrote:
>
>> In Common Lisp, it would be possible to construct a new foo-method
>> with an eql specifier on my-foo, but since the methods aren't
>> associated with instances, the method would stick around after my-foo
>> is garbage collected and you could wind up adding more and more
>> methods that couldn't be reached.
>
>Hmm, what prevents implementations from garbage collecting such methods?
>
>In any case, if you're worried about this, you can associate a finalizer
>with the involved object that removes the respective method as soon as
>the object is gc'ed. Many CL implementations provide such finalizers.
>
>
>Pascal
I'm coming from Java, where classes are loaded and (mostly) stick
around, and objects are what's garbage collected. I wasn't aware that
methods could be automatically garbage collected; I assumed that
methods would have to stay around since an object could be
instantiated at any time that would want to use the method.
Anyway, a finalizer that removes a created method would work.
This has been very enlightening. Now all I need is a use for this.
When I come across one I'll let y'all know.
Ralph Richard Cook wrote:
> I'm coming from Java, where classes are loaded and (mostly) stick
> around, and objects are what's garbage collected. I wasn't aware that
> methods could be automatically garbage collected; I assumed that
> methods would have to stay around since an object could be
> instantiated at any time that would want to use the method.
Note that I don't know whether methods are actually garbage collected. I
was just speculating. Probably they aren't.
BTW in Java, classes are garbage collected when the class loaders to
which they belong become garbage.
> Anyway, a finalizer that removes a created method would work.
>
> This has been very enlightening. Now all I need is a use for this.
> When I come across one I'll let y'all know.
:)
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Marco Antoniotti
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <xrcfc.6$a5.3544@typhoon.nyu.edu>
Pascal Costanza wrote:
>
> Ralph Richard Cook wrote:
>
>> I'm coming from Java, where classes are loaded and (mostly) stick
>> around, and objects are what's garbage collected. I wasn't aware that
>> methods could be automatically garbage collected; I assumed that
>> methods would have to stay around since an object could be
>> instantiated at any time that would want to use the method.
>
>
> Note that I don't know whether methods are actually garbage collected. I
> was just speculating. Probably they aren't.
I suppose this would be implementation dependent and very hard to do.
If you have an EQL specializer an an instance, then, by definition, you
have a reference to that instance, hence it cannot become garbage as the
method is part of the generic function "trampoline" (or whatever you
want to call it).
Apart from that, I still have not understood the problem posed by the
OP. (I did not see the original message)
Cheers
marco
From: André Thieme
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <c5k45h$6dr$1@ulric.tng.de>
Pascal Costanza wrote:
>
> Ralph Richard Cook wrote:
>
>> I'm coming from Java, where classes are loaded and (mostly) stick
>> around, and objects are what's garbage collected. I wasn't aware that
>> methods could be automatically garbage collected; I assumed that
>> methods would have to stay around since an object could be
>> instantiated at any time that would want to use the method.
>
>
> Note that I don't know whether methods are actually garbage collected. I
> was just speculating. Probably they aren't.
A symbol can have several meanings if I understood it correctly.
It can stand for a value (like "5") or for a function when it is the
first element of a (not quoted) list.
Is there a way to access the table where Lisp saves its data? If so,
couldn't one then simply delete the entry for a function?
Or would this mean that we deleted the pointer to some piece of memory
which can in turn now never be freed?
And what happens in such a situation:
CL-USER 1 > (defun foo () 5)
FOO
CL-USER 2 > (foo)
5
CL-USER 3 > (defun foo())
FOO
CL-USER 4 > (foo)
NIL
Where is now my first definition of FOO after I tried to redefine it?
Oh, and another question:
when I try to do
CL-USER 5 > (defun length())
I get:
Error: Redefining function LENGTH visible from package COMMON-LISP.
1 (continue) Redefine it anyway.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Why can't I redefine LENGTH but FOO I can?
Andr�
--
From: Edi Weitz
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <m365c29u4i.fsf@bird.agharta.de>
On Wed, 14 Apr 2004 21:41:44 +0200, Andr� Thieme <······································@justmail.de> wrote:
> A symbol can have several meanings if I understood it correctly. It
> can stand for a value (like "5") or for a function when it is the
> first element of a (not quoted) list.
>
> Is there a way to access the table where Lisp saves its data? If so,
> couldn't one then simply delete the entry for a function?
See FMAKUNBOUND.
> Or would this mean that we deleted the pointer to some piece of
> memory which can in turn now never be freed?
No. It can be freed like any other object. A simple model to think
about this is that each symbol has a "value cell" and a "function
cell". If I evaluate
(defparameter foo 5)
(defun foo () 5)
then the value cell of the symbol FOO holds the number 5 while the
function cell holds a functional object like (lambda () 5). Both cells
are independent of each other.
> And what happens in such a situation:
>
> CL-USER 1 > (defun foo () 5)
> FOO
>
> CL-USER 2 > (foo)
> 5
>
> CL-USER 3 > (defun foo())
> FOO
>
> CL-USER 4 > (foo)
> NIL
>
> Where is now my first definition of FOO after I tried to redefine
> it?
It's gone (and will be garbage-collected in most implementations)
unless some other object has access to it:
* (defun foo () 5)
FOO
* (foo)
5
* (let ((f #'foo)) (defun bar () (funcall f)))
; Converted BAR.
BAR
* (bar)
5
* (defun foo () 42)
FOO
* (foo)
42
* (bar) ;; old functional object is still there
5
> Oh, and another question:
> when I try to do
> CL-USER 5 > (defun length())
>
> I get:
>
> Error: Redefining function LENGTH visible from package COMMON-LISP.
> 1 (continue) Redefine it anyway.
> 2 (abort) Return to level 0.
> 3 Return to top loop level 0.
>
>
> Why can't I redefine LENGTH but FOO I can?
Because redefining LENGTH is forbidden by the ANSI standard - see
11.1.2.1.2. This was most likely done for efficiency reasons - a form
like (LENGTH FOO) can be compiled directly to a call of the
corresponding standard function without the compiler having to care
whether LENGTH might be redefined later.
Edi.
From: Christopher C. Stacy
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <uwu4ithlg.fsf@news.dtpq.com>
>>>>> On Wed, 14 Apr 2004 22:34:05 +0200, Edi Weitz ("Edi") writes:
Andr� Thieme >> Why can't I redefine LENGTH but FOO I can?
Edi> Because redefining LENGTH is forbidden by the ANSI standard - see
Edi> 11.1.2.1.2. This was most likely done for efficiency reasons - a form
Edi> like (LENGTH FOO) can be compiled directly to a call of the
Edi> corresponding standard function without the compiler having to care
Edi> whether LENGTH might be redefined later.
Another resaon is that Lisp is that Lisp draws certain lines between
what are the core primitives of expression in the langauge, and does
not intend that you extend them. In those places, you're supposed to
make up your own name for your own operators, not overload the primitives
that Lisp provides. This way, people reading your code can understand
the meaning of every primitive that they see.
From: André Thieme
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <c5kaa3$ari$1@ulric.tng.de>
Edi Weitz wrote:
> On Wed, 14 Apr 2004 21:41:44 +0200, Andr� Thieme <······································@justmail.de> wrote:
>
>
>>A symbol can have several meanings if I understood it correctly. It
>>can stand for a value (like "5") or for a function when it is the
>>first element of a (not quoted) list.
>>
>>Is there a way to access the table where Lisp saves its data? If so,
>>couldn't one then simply delete the entry for a function?
>
>
> See FMAKUNBOUND.
Ok, then it is clear that one *can* let functions be GC'ed.
>>Or would this mean that we deleted the pointer to some piece of
>>memory which can in turn now never be freed?
>
>
> No. It can be freed like any other object. A simple model to think
> about this is that each symbol has a "value cell" and a "function
> cell". If I evaluate
>
> (defparameter foo 5)
> (defun foo () 5)
>
> then the value cell of the symbol FOO holds the number 5 while the
> function cell holds a functional object like (lambda () 5). Both cells
> are independent of each other.
Yes, this is how I understood it.
Are there even more cells that symbols do have?
> It's gone (and will be garbage-collected in most implementations)
> unless some other object has access to it:
A great, a nice example!
>>Oh, and another question:
>>when I try to do
>>CL-USER 5 > (defun length())
>>
>>I get:
>>
>>Error: Redefining function LENGTH visible from package COMMON-LISP.
>> 1 (continue) Redefine it anyway.
>> 2 (abort) Return to level 0.
>> 3 Return to top loop level 0.
>>
>>
>>Why can't I redefine LENGTH but FOO I can?
>
>
> Because redefining LENGTH is forbidden by the ANSI standard - see
> 11.1.2.1.2. This was most likely done for efficiency reasons - a form
> like (LENGTH FOO) can be compiled directly to a call of the
> corresponding standard function without the compiler having to care
> whether LENGTH might be redefined later.
For me it seems that one cannot redefine any standard function.
I tested it even with some exotic functions like user-homedir-pathname.
Thanks for these tips, they were very helpfull.
Andr�
--
From: Edi Weitz
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <m3wu4i8clo.fsf@bird.agharta.de>
On Wed, 14 Apr 2004 23:26:35 +0200, Andr� Thieme <······································@justmail.de> wrote:
> Ok, then it is clear that one *can* let functions be GC'ed.
Sure. But that was not the question IIRC. I think Pascal was wondering
whether some implementation would be able to GC a method which is
specialized on a particular object once this object itself is gone.
> Yes, this is how I understood it.
> Are there even more cells that symbols do have?
You can think of the symbol's property list as being stored in the
symbols "property list cell" if you want.
>> Because redefining LENGTH is forbidden by the ANSI standard - see
>> 11.1.2.1.2.
>
> For me it seems that one cannot redefine any standard function.
Yes, as I said - see the CLHS.
Edi.
From: Marco Antoniotti
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <wJzfc.21$a5.5274@typhoon.nyu.edu>
Andr� Thieme wrote:
>
> Yes, this is how I understood it.
> Are there even more cells that symbols do have?
You may consider the property list as such "cell", see SYMBOL-PLIST.
>> Because redefining LENGTH is forbidden by the ANSI standard - see
>> 11.1.2.1.2. This was most likely done for efficiency reasons - a form
>> like (LENGTH FOO) can be compiled directly to a call of the
>> corresponding standard function without the compiler having to care
>> whether LENGTH might be redefined later.
>
>
> For me it seems that one cannot redefine any standard function.
> I tested it even with some exotic functions like user-homedir-pathname.
Yes, you shouldn't redefine any of the standard functions. The way to
"redefine" them is to create your own package and do something like
(defpackage "MY-CL" (:use "COMMON-LISP")
(:shadow "LENGTH")
(:export "LENGTH"))
(in-package "MY-CL")
(defun length (x)
(typecase x
(sequence (cl:length x))
(t 42)))
then at the prompt you may do
cl-prompt> (my-cl:length #C(0 1))
42
cl-prompt> (my-cl:length "abc")
3
Cheers
marco
From: Julian Stecklina
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <86r7uqw94k.fsf@web.de>
Andr� Thieme <······································@justmail.de> writes:
> Is there a way to access the table where Lisp saves its data? If so,
> couldn't one then simply delete the entry for a function?
Take a look at symbol-function and fmakunbound.
> Or would this mean that we deleted the pointer to some piece of memory
> which can in turn now never be freed?
I seriously doubt that.
> Where is now my first definition of FOO after I tried to redefine it?
The old function will be garbage collected.
> Why can't I redefine LENGTH but FOO I can?
ANSI Common Lisp disallows redefining standard functions.
(Btw, try (defun length (x)) in SBCL and then do (quit). Amazing. *g* )
Regards,
--
Julian Stecklina
Signed and encrypted mail welcome.
Key-Server: pgp.mit.edu Key-ID: 0xD65B2AB5
FA38 DCD3 00EC 97B8 6DD8 D7CC 35D8 8D0E D65B 2AB5
Any sufficiently complicated C or Fortran program
contains an ad hoc informally-specified bug-ridden
slow implementation of half of Common Lisp.
- Greenspun's Tenth Rule of Programming
From: André Thieme
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <c5kah2$auo$1@ulric.tng.de>
Julian Stecklina wrote:
>>Why can't I redefine LENGTH but FOO I can?
>
> ANSI Common Lisp disallows redefining standard functions.
>
> (Btw, try (defun length (x)) in SBCL and then do (quit). Amazing. *g* )
Also with LispWorks Personal 4.3.6 under Windows XP I could see some
strange behaviour. LispWorks told me that I cannot redefine it and I do
a ":c 1" to redefine it anyway. It then tells me again that I can't
redefine the function and again I select option "1" (":c 1") to do it
anyway.
Andr�
--
From: Hannah Schroeter
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <c5jgl8$evp$1@c3po.use.schlund.de>
Hello!
Ralph Richard Cook <······@bellsouth.net> wrote:
>[...]
>I'm coming from Java, where classes are loaded and (mostly) stick
>around, and objects are what's garbage collected. I wasn't aware that
>methods could be automatically garbage collected;
In principle, methods are just some kind of objects in Lisp+MOP.
>I assumed that
>methods would have to stay around since an object could be
>instantiated at any time that would want to use the method.
An object doesn't use a method. Methods in CLOS are quite different,
as they aren't attached to classes but to generic functions.
(More precisely, you *may* attach a method to a generic function, in
which case, it is of course referenced, i.e. will not be GCed unless
the generic function object is GCed itself.)
>[...]
So the reference chain is usually something like this:
The global list of all packackes (which you get with (list-all-packages)),
a specific package, an interned symbol (the name of a generic function),
the generic function which is referred to from the function slot of
that symbol, the methods of the GF.
If you remove a method from a GF, e.g. by using the MOP or by redefining
that specific method, I'd quite expect that method to be GCed.
Kind regards,
Hannah.
From: Rahul Jain
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <87vfjy2j7e.fsf@nyct.net>
······@bellsouth.net (Ralph Richard Cook) writes:
> I'm coming from Java, where classes are loaded and (mostly) stick
> around, and objects are what's garbage collected. I wasn't aware that
> methods could be automatically garbage collected; I assumed that
> methods would have to stay around since an object could be
> instantiated at any time that would want to use the method.
An object can't be created that is the same object as another object. If
it were the same as the other object, you wouldn't be creating it, just
referencing it.
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Peter Seibel
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <m3vfk2m68b.fsf@javamonkey.com>
······@bellsouth.net (Ralph Richard Cook) writes:
> In Common Lisp, it would be possible to construct a new foo-method
> with an eql specifier on my-foo, but since the methods aren't
> associated with instances, the method would stick around after
> my-foo is garbage collected and you could wind up adding more and
> more methods that couldn't be reached.
Hmmm. Interesting. Of course there's nothing in the standard that
*requires* that memory-consuming behvior. (For that matter there's
nothing in the standard, IIRC, that requires a garbage collector at
all.) So it would be perfectly legitimate, it seems, for an
implementation to keep the references from generic functions to
methods in such a way (e.g. with weak references) so that methods that
can no longer possibly be included in the GF's effective method *can*
be GC'd.
-Peter
--
Peter Seibel ·····@javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
Peter Seibel wrote:
> ······@bellsouth.net (Ralph Richard Cook) writes:
>
>>In Common Lisp, it would be possible to construct a new foo-method
>>with an eql specifier on my-foo, but since the methods aren't
>>associated with instances, the method would stick around after
>>my-foo is garbage collected and you could wind up adding more and
>>more methods that couldn't be reached.
>
>
> Hmmm. Interesting. Of course there's nothing in the standard that
> *requires* that memory-consuming behvior.
Yes, there is.
> (For that matter there's
> nothing in the standard, IIRC, that requires a garbage collector at
> all.) So it would be perfectly legitimate, it seems, for an
> implementation to keep the references from generic functions to
> methods in such a way (e.g. with weak references) so that methods that
> can no longer possibly be included in the GF's effective method *can*
> be GC'd.
It is not true that a method "that can no longer possibly be included
in the GF's effective method" can be GC's. That method is a first
class object that can be retrieved by the MOP function
specializer-direct-methods and others. The MOP is in a sense a
database, and a database isn't allowed to forget things just because it
thinks they are no longer important. I could, for example, extract an
object from an eql-specializer and pass that object to a gf that
dicriminates on that object. Despite the fact that at some time the
only reference to the object might have been that eql-specializer,
it ain't garbage!
Not even remove-method is sufficient to cause a method to become
garbage. The method is still connected to its specializers via
specializer-direct-methods, and the specializer can be found via
method-specializersif the specializer is accessible via other gf's.
From: Tim Bradshaw
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <ey3oepss2r5.fsf@cley.com>
* Steven M Haflich wrote:
> Peter Seibel wrote:
>> Hmmm. Interesting. Of course there's nothing in the standard that
>> *requires* that memory-consuming behvior.
> Yes, there is.
> It is not true that a method "that can no longer possibly be included
> in the GF's effective method" can be GC's. That method is a first
> class object that can be retrieved by the MOP function
> specializer-direct-methods and others.
The MOP is not, of course, part of the standard. Perhaps you should
cite something that is?
--tim
Tim Bradshaw wrote:
> The MOP is not, of course, part of the standard. Perhaps you should
> cite something that is?
Yes, of course, I was sloppy in not noting this. But despite the MOP not
being part of the standard, many implementations nonetheless accept it as
binding (modulo minor warts) because it is widely accepted, and if one
doesn't have a MOP or something similar, it is very hard to reason about
CLOS. The portion of CLOS that is in the ANS itself is not quite
self contained.
Anyway, thanks for the clarification.
Steven M. Haflich wrote:
> It is not true that a method "that can no longer possibly be included
> in the GF's effective method" can be GC's. That method is a first
> class object that can be retrieved by the MOP function
> specializer-direct-methods and others. The MOP is in a sense a
> database, and a database isn't allowed to forget things just because it
> thinks they are no longer important. I could, for example, extract an
> object from an eql-specializer and pass that object to a gf that
> dicriminates on that object. Despite the fact that at some time the
> only reference to the object might have been that eql-specializer,
> it ain't garbage!
Are there usage scenarios in which this can be useful? (This is not a
rhetorical question.)
> Not even remove-method is sufficient to cause a method to become
> garbage. The method is still connected to its specializers via
> specializer-direct-methods, and the specializer can be found via
> method-specializersif the specializer is accessible via other gf's.
The MOP spec says this about specializer-direct-methods:
"This generic function returns the possibly empty set of those methods,
connected to generic functions, which have /specializer/ as a
specializer. The elements of this set are method metaobjects. This value
is maintained by the generic functions add-direct-method and
remove-direct-method."
Pascal
--
ECOOP 2004 Workshops - Oslo, Norway
*1st European Lisp and Scheme Workshop, June 13*
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
*2nd Post-Java Workshop, June 14*
http://prog.vub.ac.be/~wdmeuter/PostJava04/
From: Marco Antoniotti
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <MMUfc.31$a5.6795@typhoon.nyu.edu>
Apart from these issues of methods and GC, what exactly can be done in
Ruby that cannot be done in CL and why? I still have not understood this.
Cheers
marco
Pascal Costanza wrote:
> Steven M. Haflich wrote:
>
>> It is not true that a method "that can no longer possibly be included
>> in the GF's effective method" can be GC's. That method is a first
>> class object that can be retrieved by the MOP function
>> specializer-direct-methods and others. The MOP is in a sense a
>> database, and a database isn't allowed to forget things just because it
>> thinks they are no longer important. I could, for example, extract an
>> object from an eql-specializer and pass that object to a gf that
>> dicriminates on that object. Despite the fact that at some time the
>> only reference to the object might have been that eql-specializer,
>> it ain't garbage!
>
>
> Are there usage scenarios in which this can be useful? (This is not a
> rhetorical question.)
>
>> Not even remove-method is sufficient to cause a method to become
>> garbage. The method is still connected to its specializers via
>> specializer-direct-methods, and the specializer can be found via
>> method-specializersif the specializer is accessible via other gf's.
>
>
> The MOP spec says this about specializer-direct-methods:
>
> "This generic function returns the possibly empty set of those methods,
> connected to generic functions, which have /specializer/ as a
> specializer. The elements of this set are method metaobjects. This value
> is maintained by the generic functions add-direct-method and
> remove-direct-method."
>
>
> Pascal
>
Marco Antoniotti wrote:
> Apart from these issues of methods and GC, what exactly can be done in
> Ruby that cannot be done in CL and why? I still have not understood this.
In object-centric languages, like Ruby, Smalltalk, Self and Java, among
others, all method dispatch goes through a mapping from OID to a method
table (usually through a reference to a class, but this isn't
necessary). This allows you to wrap each and every message send to a
single object by repacing all methods for an object with what we would
call an around method. For example, in Smalltalk this is also known as a
method wrapper.
What's nice, in theory, is that you can have a single method wrapper
play the wrapper for all methods of an object, not unlike a single
slot-value-using-class method that can "wrap" all accesses to a single
object, instances of a class, or instances of the classes of a metaclass.
That's not as easy to achieve in CLOS because generic functions
typically do not belong to classes but have an independent existence. By
default, only accessors belong to classes. (In a sense, that's also the
case for initializers, make-instance, and other generic functions that
only require one parameter.)
Does this help?
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Marco Antoniotti
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <fMfgc.40$a5.6620@typhoon.nyu.edu>
Thank you Pascal, but that still does not answer my question.
What exactly can you do in Ruby that CLOS (modulo the GC vagaries of
CLOS+MOP) does not allow you to do in CL? I still have not understood
what Ruby really does in teh specific example cited by the OP.
All in all I agree that you need per-instance methods or samething akin
to it to achieve the effect I kinda surmise. But apart from that I do
not see anyhting particularly fancy about the Ruby solution.
Cheers
marco
Pascal Costanza wrote:
>
> Marco Antoniotti wrote:
>
>> Apart from these issues of methods and GC, what exactly can be done
>> in Ruby that cannot be done in CL and why? I still have not
>> understood this.
>
>
> In object-centric languages, like Ruby, Smalltalk, Self and Java, among
> others, all method dispatch goes through a mapping from OID to a method
> table (usually through a reference to a class, but this isn't
> necessary). This allows you to wrap each and every message send to a
> single object by repacing all methods for an object with what we would
> call an around method. For example, in Smalltalk this is also known as a
> method wrapper.
>
> What's nice, in theory, is that you can have a single method wrapper
> play the wrapper for all methods of an object, not unlike a single
> slot-value-using-class method that can "wrap" all accesses to a single
> object, instances of a class, or instances of the classes of a metaclass.
>
> That's not as easy to achieve in CLOS because generic functions
> typically do not belong to classes but have an independent existence. By
> default, only accessors belong to classes. (In a sense, that's also the
> case for initializers, make-instance, and other generic functions that
> only require one parameter.)
>
> Does this help?
>
> Pascal
>
Marco Antoniotti wrote:
> Thank you Pascal, but that still does not answer my question.
>
> What exactly can you do in Ruby that CLOS (modulo the GC vagaries of
> CLOS+MOP) does not allow you to do in CL? I still have not understood
> what Ruby really does in teh specific example cited by the OP.
>
> All in all I agree that you need per-instance methods or samething akin
> to it to achieve the effect I kinda surmise. But apart from that I do
> not see anyhting particularly fancy about the Ruby solution.
OK, I'll try change my approach. Imagine an OOP extension to Lisp that
is based on the notion of message sending, so that you have to say the
following:
(let ((object (send system 'make-instance 'my-class :x 5 :y 10)))
(send object 'print t)
(send object 'set-slot :x 20)
...)
...and so forth.
In such a system you wouldn't have multi-methods.
Now you can write a wrapper method for a single object like this:
(defmethod send ((object (eql *some-object*))
any-message ;; !!!
&rest args)
(format t "~&Message ~S has been sent to ~S.~%"
any-message object))
The important point is that you can intercept any message via such a
method, without any exception (even when multi-methods are emulated via
double dispatch).
This is more complicated to achieve in CLOS because you have to parse
the signatures of existing methods in order to create wrapper methods
with congruent argument lists.
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Marco Antoniotti
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <vWQgc.56$a5.11418@typhoon.nyu.edu>
Sorry for being obtuse, but I read your post and say: so?!?
I perfectly understand all of this. But I still do not see what Ruby
can do that I cannot with CLOS.
Marco
Pascal Costanza wrote:
>
>
> Marco Antoniotti wrote:
>
>> Thank you Pascal, but that still does not answer my question.
>>
>> What exactly can you do in Ruby that CLOS (modulo the GC vagaries of
>> CLOS+MOP) does not allow you to do in CL? I still have not understood
>> what Ruby really does in teh specific example cited by the OP.
>>
>> All in all I agree that you need per-instance methods or samething
>> akin to it to achieve the effect I kinda surmise. But apart from that
>> I do not see anyhting particularly fancy about the Ruby solution.
>
>
> OK, I'll try change my approach. Imagine an OOP extension to Lisp that
> is based on the notion of message sending, so that you have to say the
> following:
>
> (let ((object (send system 'make-instance 'my-class :x 5 :y 10)))
> (send object 'print t)
> (send object 'set-slot :x 20)
> ...)
>
> ...and so forth.
>
> In such a system you wouldn't have multi-methods.
>
> Now you can write a wrapper method for a single object like this:
>
> (defmethod send ((object (eql *some-object*))
> any-message ;; !!!
> &rest args)
> (format t "~&Message ~S has been sent to ~S.~%"
> any-message object))
>
> The important point is that you can intercept any message via such a
> method, without any exception (even when multi-methods are emulated via
> double dispatch).
>
> This is more complicated to achieve in CLOS because you have to parse
> the signatures of existing methods in order to create wrapper methods
> with congruent argument lists.
>
>
> Pascal
>
Marco Antoniotti wrote:
> Sorry for being obtuse, but I read your post and say: so?!?
>
> I perfectly understand all of this. But I still do not see what Ruby
> can do that I cannot with CLOS.
Nothing. ;)
In _theory_ (as in "ivory tower"), it is nice that you can capture each
and every message sent to an object in a single place. In _practice_ (as
in "CLOS"), you will need to distinguish between different messages anyway.
(Or it may just as well be that I am missing something...)
Pascal
--
ECOOP 2004 Workshops - Oslo, Norway
*1st European Lisp and Scheme Workshop, June 13*
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
*2nd Post-Java Workshop, June 14*
http://prog.vub.ac.be/~wdmeuter/PostJava04/
From: Rahul Jain
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <87r7um2iik.fsf@nyct.net>
Pascal Costanza <········@web.de> writes:
> That's not as easy to achieve in CLOS because generic functions
> typically do not belong to classes but have an independent existence. By
> default, only accessors belong to classes. (In a sense, that's also the
> case for initializers, make-instance, and other generic functions that
> only require one parameter.)
In effect, he's asking for a way to intercede whenever a certain object
is passed to a function. Of course, that object will need to be passed
to some function in order to determine if that object is the one in
question. This function, in turn, should be interceeded in the same way.
Therefore, he's asking for an infinite recursion. Such constructs are
easy to create. :)
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
Rahul Jain wrote:
> In effect, he's asking for a way to intercede whenever a certain object
> is passed to a function. Of course, that object will need to be passed
> to some function in order to determine if that object is the one in
> question. This function, in turn, should be interceeded in the same way.
> Therefore, he's asking for an infinite recursion. Such constructs are
> easy to create. :)
...and sometimes can't be avoided - see the specification for generic
function calls in the MOP. ;)
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Rahul Jain
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <87isfyynj0.fsf@nyct.net>
Pascal Costanza <········@web.de> writes:
> Rahul Jain wrote:
>
>> Therefore, he's asking for an infinite recursion. Such constructs are
>> easy to create. :)
>
> ...and sometimes can't be avoided - see the specification for generic
> function calls in the MOP. ;)
It says that they can be memoized. The behavior of the classes defined
by the MOP when operated on by the functions in the MOP can be
"pre-memoized".
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
Pascal Costanza <········@web.de> wrote:
+---------------
| That's not as easy to achieve in CLOS because generic functions
| typically do not belong to classes but have an independent existence.
| By default, only accessors belong to classes.
+---------------
Uh... But I thought that accessors *were* just GFs -- albeit
auto-specialized for each class definition that uses them --
and therefore not even accessors "belong" to classes. Did I
misunderstand?
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
From: Kalle Olavi Niemitalo
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <87brlpxzfk.fsf@Astalo.kon.iki.fi>
····@rpw3.org (Rob Warnock) writes:
> Uh... But I thought that accessors *were* just GFs -- albeit
> auto-specialized for each class definition that uses them --
> and therefore not even accessors "belong" to classes. Did I
> misunderstand?
I think you're right: the accessor GFs do not belong to classes.
However, their methods do; see section 4.3.6 (Redefining Classes).
Kalle Olavi Niemitalo wrote:
> ····@rpw3.org (Rob Warnock) writes:
>
>>Uh... But I thought that accessors *were* just GFs -- albeit
>>auto-specialized for each class definition that uses them --
>>and therefore not even accessors "belong" to classes. Did I
>>misunderstand?
>
> I think you're right: the accessor GFs do not belong to classes.
> However, their methods do; see section 4.3.6 (Redefining Classes).
Right, that's what I meant. Sorry for being a little bit too sloppy.
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Rahul Jain
Subject: Re: How would you do this cool Ruby trick in Lisp/CLOS?
Date:
Message-ID: <878ygtw939.fsf@nyct.net>
····@rpw3.org (Rob Warnock) writes:
> Uh... But I thought that accessors *were* just GFs -- albeit
> auto-specialized for each class definition that uses them --
> and therefore not even accessors "belong" to classes. Did I
> misunderstand?
To be most accurate, it's only the slot-definitions (both direct and
effective) that belong to the classes. The instance-allocated
slot-values belong to the instances, and the accessor methods belong to
the generic function with that name, and are specialized on the class in
question. Note that even the ones that are directly kept track of for
the class (specializer-direct-methods) don't include the ones that apply
to it by inheritance.
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
Rahul Jain wrote:
> ····@rpw3.org (Rob Warnock) writes:
>
>>Uh... But I thought that accessors *were* just GFs -- albeit
>>auto-specialized for each class definition that uses them --
>>and therefore not even accessors "belong" to classes. Did I
>>misunderstand?
>
> To be most accurate, it's only the slot-definitions (both direct and
> effective) that belong to the classes. The instance-allocated
> slot-values belong to the instances, and the accessor methods belong to
> the generic function with that name, and are specialized on the class in
> question.
To get back to the OP's issue: The fact that slot values belong to
classes makes it easy to capture each and every access to the slots of
an object like this:
(defmethod slot-value-using-class
((class t)
(object (eql some-object))
(slot t))
...)
This isn't as easy to achieve for other generic functions becaues there
is no send-message-using-class.
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Pascal Costanza wrote:
> Steven M. Haflich wrote:
> Are there usage scenarios in which this can be useful? (This is not a
> rhetorical question.)
Such scenarios would be rare, but imaginable. I have seen programmers
try to use gfs etc as a kind of database that can be reasoned about in
odd ways. For example, one might imagine a rule database where
assertions would be established by adding a method with an eql
specializer. If there were no other references to the eql-specializer
object in the Lisp world (and if the object were not something like
a bignum that could be eql recreated by computation) then Peter could
argure correctly that the method could never be invoked. and the
method could be eliminated. But that would destroy the ability to
introspect the database and ask what assertions are present in the
database (i.e. what eql methods exist on some particular gf).
Now I don't program this way, and I assume you don't either, but
someone might.
> The MOP spec says this about specializer-direct-methods:
>
> "This generic function returns the possibly empty set of those methods,
> connected to generic functions, which have /specializer/ as a
> specializer. The elements of this set are method metaobjects. This value
> is maintained by the generic functions add-direct-method and
> remove-direct-method."
I omitted the details that generic-function-methods, and then
method-specializers, and the eql-specilizer-object are necessary to
retrieve an otherwise-inaccessible object from CLOS, but the MOP
guarantees it will not be removed unless the programmer removes it.
Steven M. Haflich wrote:
> Pascal Costanza wrote:
>
>> Steven M. Haflich wrote:
>
>> Are there usage scenarios in which this can be useful? (This is not a
>> rhetorical question.)
[...]
> If there were no other references to the eql-specializer
> object in the Lisp world (and if the object were not something like
> a bignum that could be eql recreated by computation) then Peter could
> argure correctly that the method could never be invoked.
Oops, I have forgotten about the fact that numbers can also be
specialized on. Of course, numbers cannot be garbage collected in any
meaningful sense. ;)
Good point. (also wrt to the rest of your explanation)
> I omitted the details that generic-function-methods, and then
> method-specializers, and the eql-specilizer-object are necessary to
> retrieve an otherwise-inaccessible object from CLOS, but the MOP
> guarantees it will not be removed unless the programmer removes it.
OK.
Pascal
--
ECOOP 2004 Workshops - Oslo, Norway
*1st European Lisp and Scheme Workshop, June 13*
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
*2nd Post-Java Workshop, June 14*
http://prog.vub.ac.be/~wdmeuter/PostJava04/
> I thought this was really cool, to have a proxy that can be handed an
> arbitrary object, have the proxy probe the object to find out what
> messages/methods it responds to, and create a wrapper object for just
> that object, and not a class of objects.
>
> How would this be done in Common Lisp? Would you have to make a custom
> class & methods for each object, and if that is the case, how would
> the custom classes get garbage collected when the instance is done?
Forgive me if someone implied this elsewhere in the thread. I looked,
but I didn't see anything direclty relating to this. I am a Lisp
newbie, so maybe this is totally impractical, but it seems like it
would work. Then again, maybe it's such a callow suggestion everyone
realized it was a bad idea.
Lots of people have talked about why it can't be easily done, because
of the way CLOS does things. I have to wonder if maybe it's because
Ruby (and Smalltalk-ish) OO systems are doing a bit of extra work.
Just a bit though. Really, what they're doing is calling
doesNotImplement:. Why can't we do this in Lisp?
Have a generic function no-method-found-for (or does-not-implement)
that is called whenever a generic function doesn't have an applicable
specializer for its given class, passing the name of the function in
question as a symbol in the first argument. This function comes
pre-specialized to t and does what is already being done when you
can't find a specializer, throwing a condition signaling the case.
However, people can place eql specializers on this no-method-found-for
to enable specific handling.
This won't give you the funky arbitrary message passing that lets you
make a Ruby "roman numerals as methods" class. However, it will let
you do all the interesting inheritance-free-forwarding-proxy things
that you probably want to be able to do.
As far as I can tell, what it would take to do this is to change the
way defgeneric creates generics (but only slightly, which is very
doable since it's already a macro) and make sure that
no-method-found-for is defined to begin with (trivial).
Is there something terribly wrong with this approach?
·········@lensmen.net (Dave Fayram) writes:
> Have a generic function no-method-found-for (or does-not-implement)
> that is called whenever a generic function doesn't have an applicable
> specializer for its given class,
Like no-applicable-method?
http://www.lispworks.com/reference/HyperSpec/Body/f_no_app.htm
--
Alan Shutko <···@acm.org> - I am the rocks.
Dave Fayram wrote:
> Is there something terribly wrong with this approach?
Yes. Ruby and Smalltalk, etc., are object-centric, whereas Common Lisp
is function centric. In an object-centric language it's easy to put a
"message-not-understood" method in each object and have it called in
place of missing methods. In function-centric languages, methods do not
belong to objects or their classes, but to generic functions.
The notion of generic functions gives you multi-methods more or less for
free. In object-centric languages you have to jump through various hoops
to achieve the same effect (see, for example, the Visitor pattern).
Multi-methods are a real advantage, whereas it's not clear what
advantages you gain from the ability to wrap each and every method to an
object.
Can you give a concrete example what you want the method wrapping
facility to use for?
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
Pascal Costanza <········@web.de> wrote in message news:<············@newsreader2.netcologne.de>...
> Dave Fayram wrote:
>
> > Is there something terribly wrong with this approach?
>
> Yes. Ruby and Smalltalk, etc., are object-centric, whereas Common Lisp
> is function centric. In an object-centric language it's easy to put a
> "message-not-understood" method in each object and have it called in
> place of missing methods. In function-centric languages, methods do not
> belong to objects or their classes, but to generic functions.
>
It seems Lisp already does this, as Alan Shutko so generously
made this Lisp newbie aware of. I am confused as to this argument though
because in essence, generic functions express anything (and more) than
their less erudite cousins like Ruby.
All that this "autoproxy" class is saying is, "Try and dispatch on
me before you dispatch on my target."
This behavior is actually quite sensible in the world of generic functions,
it just brings more implications and a bit more work.
Instead of having to replace just the first specialized argument, you
probably meant to replace ALL instances of this proxy with the proxy's
target. I'll think more about it when I implement it. :)
> The notion of generic functions gives you multi-methods more or less for
> free. In object-centric languages you have to jump through various hoops
> to achieve the same effect (see, for example, the Visitor pattern).
> Multi-methods are a real advantage, whereas it's not clear what
> advantages you gain from the ability to wrap each and every method to an
> object.
I'm confused why you feel we have to have one or the other. Clearly we
can implement this RIGHT NOW with very little effort. Isn't this kind
of programming what macros in lisp are for?
Besides, I've written something like what Matz is describing in this
article. The primary benefit is that it's a nice way to reduce the
amount of code you write when you're, say, writing a security-check
proxy, or a logging proxy, or something that multiplexes method
calls. Instead of numerous subclasses, or brittle templating, a
succinct meta-programming syntax lets you make a truly generic and
simple solution.
It's a useful pattern. In its extreme case, the principle is applied
to implement the Delegate pattern (although there are cleaner ways,
if you're willing to fix the delegate's signature more tightly).
> Can you give a concrete example what you want the method wrapping
> facility to use for?
I can't say what the poster wants. I know what I used this for. I had
a Ruby program, a daemon, that ran for about 30 hours then quit on me.
I could never seem to pin down exactly why it died. A nil object crept
in at some point. I used this facility to quickly build a logging-forwarding
proxy and place it in front of some key objects to watch what went in and
out of the classes and write it to a file in a format I could analyze.
Sure enough, I found where my bug was (had to do with the way I was using
sockets, and an admin rebooting a nearby machine I talked to). I
wrote one class to proxy for 7 different objects, and it was small. To
me, that's one heck of a useful feature. I still have that code in my
Ruby library today.
But, why does anyone need to justify a feature to you, Pascal? Isn't the
mentality of Lisp to let the programmer do what they need to do? The You-
don't-need-this pattern of language design has led to hideous languages
like Java. Meta-programming features are what drew me to Lisp in the
first place.
The feature more-or-less exists in Lisp right now. There's no need to
perform any kind of paradigm shift.
Dave Fayram wrote:
> This behavior is actually quite sensible in the world of generic functions,
> it just brings more implications and a bit more work.
That's the only thing I have tried to get across in my posts on this topic.
>>The notion of generic functions gives you multi-methods more or less for
>>free. In object-centric languages you have to jump through various hoops
>>to achieve the same effect (see, for example, the Visitor pattern).
>>Multi-methods are a real advantage, whereas it's not clear what
>>advantages you gain from the ability to wrap each and every method to an
>>object.
>
> I'm confused why you feel we have to have one or the other. Clearly we
> can implement this RIGHT NOW with very little effort. Isn't this kind
> of programming what macros in lisp are for?
I am not quite sure why you mention macros here. The MOP is more
appropriate here, I think.
>>Can you give a concrete example what you want the method wrapping
>>facility to use for?
[...]
> But, why does anyone need to justify a feature to you, Pascal?
Sorry if I have given the impression that people should not program in a
way they prefer. This wasn't my intention. The only thing that bothered
me was that the discussion was a little too abstract. In my experience,
things become clearer when you have concrete examples to talk about.
> Isn't the
> mentality of Lisp to let the programmer do what they need to do? The You-
> don't-need-this pattern of language design has led to hideous languages
> like Java. Meta-programming features are what drew me to Lisp in the
> first place.
100% agreement here.
Sorry again,
Pascal
--
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/