From: Scott L. Burson on
On Jun 28, 12:09 am, Pascal Costanza <p...(a)p-cos.net> wrote:
> On 28/06/2010 01:00, Scott L. Burson wrote:
>
> > For those with more MOP experience than I, what's the easiest way to
> > programmatically add a slot to an existing class?
>
> The situations were you may want to do this are usually extremely rare.
> So it may be useful to tell us what you want to achieve, so we can maybe
> come up with better solutions.

I just wanted to extend a class whose definition I don't control. I
could just load a patch containing the DEFCLASS form with my slot
added -- I do have access to the original DEFCLASS form -- but then if
it changed, I would have to update my patch. Doing it this way makes
it reasonably upgrade-proof.

Pascal B's code is working fine for me. Maybe something like this
belongs in Closer, or in a CLOS utilities library?

BTW I agree with Rainer -- these situations are not rare at all in my
experience. For years I used a language called Refine in which slot
definitions were not part of the class definition but were separate
top-level syntactic entities. It was therefore trivial, for example,
to declare a class with some of its slots in one source file, and to
add additional slots to it in another source file. (There was a way
to interactively request the entire list of slots, in case one needed
to know, but this need arose less often than one might expect.)

Like a lot of things, this flexibility could be abused, but used
intelligently it was quite handy.

-- Scott
From: Christophe Rhodes on
"Scott L. Burson" <gyro(a)zeta-soft.com> writes:

> On Jun 28, 12:09 am, Pascal Costanza <p...(a)p-cos.net> wrote:
>> On 28/06/2010 01:00, Scott L. Burson wrote:
>>
>> > For those with more MOP experience than I, what's the easiest way to
>> > programmatically add a slot to an existing class?
>>
>> The situations were you may want to do this are usually extremely rare.
>> So it may be useful to tell us what you want to achieve, so we can maybe
>> come up with better solutions.
>
> I just wanted to extend a class whose definition I don't control. I
> could just load a patch containing the DEFCLASS form with my slot
> added -- I do have access to the original DEFCLASS form -- but then if
> it changed, I would have to update my patch. Doing it this way makes
> it reasonably upgrade-proof.

For what it's worth: I'd be tempted to add in a superclass mixin
containing your slot, rather than patch in the slot directly; the effect
is basically the same, but maybe marginally more upgrade-proof?

However, is this not a sign that something somewhere else isn't quite
right? The "normal" way to do this would be to arrange that you use a
subclass of the original class, creating instances of that class and
passing them around, or (if necessary) change-classing the instances you
get given. Not that that's necessarily expedient, and of course you
might be doing something that's a long way from "normal" -- but maybe
the protocol you're using upstream is wrong?

Christophe
From: Pascal Costanza on
On 28/06/2010 15:01, joswig(a)corporate-world.lisp.de wrote:
> On 28 Jun., 14:47, Pascal Costanza<p...(a)p-cos.net> wrote:
>> On 28/06/2010 11:38, jos...(a)corporate-world.lisp.de wrote:
>>
>>
>>
>>
>>
>>> On 28 Jun., 09:09, Pascal Costanza<p...(a)p-cos.net> wrote:
>>>> On 28/06/2010 01:00, Scott L. Burson wrote:
>>
>>>>> For those with more MOP experience than I, what's the easiest way to
>>>>> programmatically add a slot to an existing class?
>>
>>>> The situations were you may want to do this are usually extremely rare.
>>>> So it may be useful to tell us what you want to achieve, so we can maybe
>>>> come up with better solutions.
>>
>>> I don't find that rare at all and would hope that adding a slot would
>>> be be simpler.
>>
>>> That's one of the areas where other object-oriented systems for Lisp
>>> were much easier to use.
>>> For example in Object Lisp one could write nice tools to edit objects
>>> graphically
>>> and add/remove slots in a simple way. Every GUI based editor of a
>>> class hierarchy
>>> wants to manipulate slots. For example in a CAD system where classes
>>> describe
>>> objects, one may want to edit the classes in a browser. In an
>>> interface designer,
>>> the triangle-button-class may need another slot.
>>
>>> Lisp was developed with a focus on interactive development and over
>>> time developers used
>>> it to create interactive GUI based applications. A reason that this
>>> tradition has been not revived as it should, in that CLOS is very
>>> dynamic, but it makes some operations a bit difficult (like dealing
>>> with slot information, adding slots, etc.). Garnet used a simple
>>> frame system, Sk8 too, the early user interface designer for MCL
>>> used a nice view system with prototype objects, ...
>>
>>> Another domain where this is heavily used is knowledge representation.
>>> CLOS could have been used more widely here. As much as I like CLOS,
>>> I think it lost some of the appeal of the earlier systesm -
>>> probably because it was less driven by applications and practicality
>>> , but slightly more
>>> by abstract design considerations.
>>
>>> The effect is that editing text based Lisp is easy, but for creating
>>> tools, one
>>> has to go through more complex code than one may want.
>>
>>> Am I a dinosaur with my wish for easy to use tools for interactive
>>> computation?
>>> Is that out of fashion forever? Maybe I am spoiled by tools
>>> that nobody anymore uses?
>>
>>> I guess somebody should demo things like Coral Common Lisp 1.0
>>> on a Mac SE/30 at some Lisp user conference to give others
>>> a feeling what was possible in Lisp with 2.5 MB of RAM
>>> on a 68030. I know the workd has moved on, but it would be nice
>>> if we have added capabilities and not lost them.
>>
>> I haven't been around when all these things happened, so I have my
>> knowledge only from indirect sources. However, as far as I can
>> understand, many of the things that you describe can relatively easily
>> be achieved by using your own custom metaclasses. The default
>> standard-class provides features that were deemed useful for a broad
>> range of scenarios, but more specific features were left for such custom
>> metaclasses.
>>
>
> Fortunately a lot of stuff is possible with the MOP
> and it is good to have a library that unifies
> the MOP over implementations. Thanks for that.
>
>
>> Adding/removing slots is maybe a bit harder than necessary, but
>> certainly possible, and writing a graphical user interface for
>> manipulating classes and their hierarchies doesn't happen that often,
>> does it? So I guess the compromise they made there is ok.
>
> I think it is absolute crucial if you want to
> design user interfaces. The first modern UI designer
> was written in Lisp and later reimplemented at Apple.
> This UI designer is at the heart of many of the
> nice applications for the Mac/iPhone/iPad.
> Basically in any application like this you
> put some object on the screen and give it some class.
> Often one would just subclass, say, a button, give that
> some slots and put a such a button on the screen.
> Later one might need another slot, add it to the
> class and the buttons on the screen are changed...
> I find these are quite common use cases in
> designing User Interfaces of all kinds.
>
> I fear that these use cases come up not that often,
> because some capabilities needed for that are not
> obvious in CLOS (though possible) and thus CLOS/CL
> is less used for that. I fear
> it is not that these use cases are rare and thus
> capabilities are not needed (in a straightforward) way.

Manipulation of CLOS classes was actually one of the ingredients of
AspectL, my first experiment at providing "crosscutting" functionality
for CLOS. AspectL provides a number of functions (like slot-add,
slot-remove, slot-set, class-add, class-remove and class-set) with which
you can do fine-grained manipulations of CLOS classes.

This functionality is quite problematic, because it is in some cases
hard to decide how the changes should be merged with the existing
information that's already in the class. Say, there are already readers
and writers defined on a given slot, and now you want to add another
reader - does that replace the old readers, or does that just add more
readers? Some of these decisions feel very ad hoc - you basically have
to go through each and every class option and slot option and decide
what good semantics could be. And then you still haven't covered the
cases for user-defined class and slot options from user-defined
metaclasses, so you would have to add another meta layer to be able to
integrate such extensions as well.

This functionality was also quite unstable: Depending on what you did,
it was very easy to crash the whole system. In the end I decided that
it's not worth the hassle of trying to deal with each and every possible
corner case.

I think an easier approach is to use eval here, so to redefine classes
by way of (eval `(defclass ...)), or (funcall (compile nil `(lambda ()
(defclass ...))))). You need to reconstruct the defclass form in order
to do so, by using the MOP facilities to inspect existing classes, but
at least then you have the responsibility of being precise how to merge
existing information with your new one. I know this is not ideal either,
but since there is no ideal solution...

It should be relatively easy for a GUI-based tool to construct defclass
forms from user input, right?


Pascal

--
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 on
On 28/06/2010 21:20, Scott L. Burson wrote:
> On Jun 28, 12:09 am, Pascal Costanza<p...(a)p-cos.net> wrote:
>> On 28/06/2010 01:00, Scott L. Burson wrote:
>>
>>> For those with more MOP experience than I, what's the easiest way to
>>> programmatically add a slot to an existing class?
>>
>> The situations were you may want to do this are usually extremely rare.
>> So it may be useful to tell us what you want to achieve, so we can maybe
>> come up with better solutions.
>
> I just wanted to extend a class whose definition I don't control. I
> could just load a patch containing the DEFCLASS form with my slot
> added -- I do have access to the original DEFCLASS form -- but then if
> it changed, I would have to update my patch. Doing it this way makes
> it reasonably upgrade-proof.

(defclass my-class (third-party-class)
((my-additional-slot ...)))

(defmethod make-instance ((class (eql 'third-party-class)) &rest args)
(apply #'make-instance 'my-class args))

This is not 100% fool-proof, but since your solution is not 100%
fool-proof either, I think I would prefer my solution.

> Pascal B's code is working fine for me. Maybe something like this
> belongs in Closer, or in a CLOS utilities library?

No, because it's really hard to come up with good solutions for the
really tough corner cases. I think it's better to use ad hoc solutions
for the concrete problems you actually have, and the MOP API is good
enough to accommodate this.

> BTW I agree with Rainer -- these situations are not rare at all in my
> experience. For years I used a language called Refine in which slot
> definitions were not part of the class definition but were separate
> top-level syntactic entities. It was therefore trivial, for example,
> to declare a class with some of its slots in one source file, and to
> add additional slots to it in another source file. (There was a way
> to interactively request the entire list of slots, in case one needed
> to know, but this need arose less often than one might expect.)
>
> Like a lot of things, this flexibility could be abused, but used
> intelligently it was quite handy.

Here is another way to achieve what you want:

(defvar *external-store* (make-weak-hash-table :test #'equal))

(defmethod slot-missing
((class standard-class) (object third-party-class)
slot-name operation &optional new-value)
(ecase operation
(slot-value (multiple-value-bind
(value foundp)
(gethash (list object slot-name) *external-store*)
(if foundp value
(slot-unbound class object slot-name))))
(setf (setf (gethash (list object slot-name) *external-store*)
new-value))
(slot-boundp (nth-value 1 (gethash (list object slot-name)
*external-store*))))
(slot-makunbound (remhash (list object slot-name) *external-store*)
object)))

Again, not 100% fool-proof, but ANSI-compliant, without any need for the
CLOS MOP. [untested]


Pascal

--
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 on
On 28/06/2010 15:09, Alessio Stalla wrote:
> On Jun 28, 1:00 am, "Scott L. Burson"<g...(a)zeta-soft.com> wrote:
>> For those with more MOP experience than I, what's the easiest way to
>> programmatically add a slot to an existing class?
>
> IIRC, one of the examples in AMOP was about adding some kind of
> "dynamic slots" to CLOS. They were implemented as a map stored in a
> "private" slot, so not as efficient as regular slots, but it can be a
> start.

Originally, such dynamic slots were actually intended for inclusion in
CLOS, as an :allocation :dynamic option. It's a pity that this was left
out. (This would have been much more useful than, say, :allocation :class.)


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/