From: Jacek Generowicz
Subject: Adding slots to an instance.
Date: 
Message-ID: <tyffzpzta87.fsf@pcepsft001.cern.ch>
I have some CLOS class instances.

I have two contexts.

By default, the slot-values should be the same in both contexts, but I
want to make it possible to specify that a given slot of a given
instance should be different in the second context.

One approach that comes to mind is to provide readers for the second
context, which default to simply reading the slot, and specializing
them on specific instances:

(defclass foo ()
  ((bar :reader read-bar)))

(defmethod read-bar-in-second-context ((object foo))
  (read-bar object))

(defmethod read-bar-in-second-context ((object (eql some-instance-of-foo)))
  the-overriding-value)


Is it possible to add slots to a single instance? If it were, then it
could be done by making the reader check for the existence of an
overriding slot, before deciding what value to return:

(defmethod read-bar-in-secod-context ((object foo))
  (if (slot-exists-p 'override-bar object)
      (slot-value 'override-bar object)
    (read-bar object)))


Is there some completely different approach I should consider ?

From: Barry Margolin
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <gI4aa.11$LF5.1087@paloalto-snr1.gtei.net>
In article <···············@pcepsft001.cern.ch>,
Jacek Generowicz  <················@cern.ch> wrote:
>I have some CLOS class instances.
>
>I have two contexts.
>
>By default, the slot-values should be the same in both contexts, but I
>want to make it possible to specify that a given slot of a given
>instance should be different in the second context.

This doesn't seem to be what you do in the example.  Did you mean to use
:ALLOCATION :SHARED?

>One approach that comes to mind is to provide readers for the second
>context, which default to simply reading the slot, and specializing
>them on specific instances:
>
>(defclass foo ()
>  ((bar :reader read-bar)))
>
>(defmethod read-bar-in-second-context ((object foo))
>  (read-bar object))
>
>(defmethod read-bar-in-second-context ((object (eql some-instance-of-foo)))
>  the-overriding-value)
>
>
>Is it possible to add slots to a single instance? If it were, then it
>could be done by making the reader check for the existence of an
>overriding slot, before deciding what value to return:

No, the list of slots is associated with the class, not the instance.

>(defmethod read-bar-in-secod-context ((object foo))
>  (if (slot-exists-p 'override-bar object)
>      (slot-value 'override-bar object)
>    (read-bar object)))
>
>Is there some completely different approach I should consider ?

I can think of a couple of ways:

1. Define a subclass of FOO that has an OVERRIDE-BAR slot, and use this for
the special instances:

(defclass overriding-foo (foo)
  ((override-bar :reader read-override-bar)))

(defmethod read-bar-in-second-context ((object overriding-foo))
  (read-override-bar object))

2. Add slots to FOO:

(defclass foo ()
  ((bar :reader read-bar)
   (override-bar-p :default-initform 'nil)
   (override-bar :reader read-override-bar)))

(defmethod read-bar-in-second-context ((object foo))
  (if (override-bar-p object)
      (read-override-bar object)
      (read-bar object)))

-- 
Barry Margolin, ··············@level3.com
Genuity Managed Services, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Pascal Costanza
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <b4alrh$12cs$1@f1node01.rhrz.uni-bonn.de>
Barry Margolin wrote:
> In article <···············@pcepsft001.cern.ch>,
> Jacek Generowicz  <················@cern.ch> wrote:
> 
>>I have some CLOS class instances.
>>
>>I have two contexts.
>>
>>By default, the slot-values should be the same in both contexts, but I
>>want to make it possible to specify that a given slot of a given
>>instance should be different in the second context.

> I can think of a couple of ways:
> 
> 1. Define a subclass of FOO that has an OVERRIDE-BAR slot, and use this for
> the special instances:
> 
> (defclass overriding-foo (foo)
>   ((override-bar :reader read-override-bar)))
> 
> (defmethod read-bar-in-second-context ((object overriding-foo))
>   (read-override-bar object))

Note that you can change the class of an object at runtime:

CL-USER 1 > (defclass c ()
               ((a :accessor a :initform 0)))
#<STANDARD-CLASS C 205FE90C>

CL-USER 2 > (defclass d (c)
               ((b :accessor a :initform -1)))
#<STANDARD-CLASS D 21339C24>

CL-USER 3 > (setf inst (make-instance 'c))
#<C 21319644>

CL-USER 4 > (a inst)
0

CL-USER 5 > (change-class inst 'd)
#<D 21319644>

CL-USER 6 > (a inst)
-1

Pascal

-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)
From: Jacek Generowicz
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <tyfu1ed8ymd.fsf@lxplus069.cern.ch>
Barry Margolin <··············@level3.com> writes:

>  Did you mean to use :ALLOCATION :SHARED?

No, I expect the slots to have different values for different
instances (as in some cases even to have different values in the same
instance ... in different contexts).

But maybe I've misunderstood you. What makes you think I want shared
slots ?

> >Is there some completely different approach I should consider ?
> 
> I can think of a couple of ways:
> 
> 1. Define a subclass of FOO that has an OVERRIDE-BAR slot, and use this for
> the special instances:
> 
> (defclass overriding-foo (foo)
>   ((override-bar :reader read-override-bar)))
> 
> (defmethod read-bar-in-second-context ((object overriding-foo))
>   (read-override-bar object))

But given that I have many slots, any of which might be overriden, it
might be better to do this with mixins:

(defclass foo (...)
  ((slot-1 ...)
   ...
   (slot-n ...)))

(defclass slot-x-overrider (overrider)
  ((override-slot-x :reader read-slot-x-in-second-contex)))

To override a slot, create a new class by mixing in the relevant
overrider and change the instance's class. The problem is, that if I
want to override a second slot later, I need to remember which other
slots have already been overridden, but this can be done by extracting
subclasses of overrider from the class precedence list.

Still, it seems a bit more complicated than necessary.

> 2. Add slots to FOO:
> 
> (defclass foo ()
>   ((bar :reader read-bar)
>    (override-bar-p :default-initform 'nil)
>    (override-bar :reader read-override-bar)))

I could do away with override-bar-p, as NIL would never be a valid
value for the slots, so override-bar should be returned whenever it is
not nil.

But I expect the vast majority of the slots not to be overridden, so
this approach seems a bit profligate.

Along similar lines, I thought of having a single extra slot for each
instance with a map of overriden slots -> overriding values, which
would be consulted by ALL the readers.
From: Barry Margolin
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <1l3ea.8$F4.636@paloalto-snr1.gtei.net>
In article <···············@lxplus069.cern.ch>,
Jacek Generowicz  <················@cern.ch> wrote:
>Barry Margolin <··············@level3.com> writes:
>
>>  Did you mean to use :ALLOCATION :SHARED?
>
>No, I expect the slots to have different values for different
>instances (as in some cases even to have different values in the same
>instance ... in different contexts).
>
>But maybe I've misunderstood you. What makes you think I want shared
>slots ?

You said that in the non-overriding context, the slot value is the same.  I
thought by "same" you meant the same value for all instances.

-- 
Barry Margolin, ··············@level3.com
Genuity Managed Services, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kenny Tilton
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <3E68ECEB.9010907@nyc.rr.com>
Jacek Generowicz wrote:
> I have some CLOS class instances.
> 
> I have two contexts.
> 
> By default, the slot-values should be the same in both contexts, but I
> want to make it possible to specify that a given slot of a given
> instance should be different in the second context.
> 
...snip...


> Is it possible to add slots to a single instance? If it were, then it
> could be done by making the reader check for the existence of an
> overriding slot, before deciding what value to return:
> 
> (defmethod read-bar-in-secod-context ((object foo))
>   (if (slot-exists-p 'override-bar object)
>       (slot-value 'override-bar object)
>     (read-bar object)))
> 
> 
> Is there some completely different approach I should consider ?

As long as you are defining a specialized reader, why not just store 
everything in the slot and let readers (or one reader with an optional 
context argument) sort out the slot contents?

ie, if there is no special context for a slot for an instance, the slot 
simply holds the value. if you want context, you store a little assoc of 
(context . value)s in there, with I guess t as the context for the value 
outside any context.

you'll probably want to do a writer as well so you can cleanly add 
values with context.

oops. i guess you have to wrap the context-values in a special 
context-space struct so you can reliably determine that the slot holds 
context-sensitive values. otherwise any list would look like a 
context-space to the reader.

A lot easier/more portable than screwing with the MOP, for my money.

-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Cells let us walk, talk, think, make love and realize
  the bath water is cold." -- Lorraine Lee Cudmore
From: Jacek Generowicz
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <tyfptp18y73.fsf@lxplus069.cern.ch>
Kenny Tilton <·······@nyc.rr.com> writes:

> ie, if there is no special context for a slot for an instance, the
> slot simply holds the value. if you want context, you store a little
> assoc of (context . value)s in there, with I guess t as the context
> for the value outside any context.

Actually, I was thinking of adding an "overrides" slot which would be
an accoc or hash-table or whatever of (slot-name . overriding-value),
where readers would look for their slots amongst the keys, and return
the overriding-value if found, otherwise just return the slot's value.

> oops. i guess you have to wrap the context-values in a special
> context-space struct so you can reliably determine that the slot holds
> context-sensitive values. otherwise any list would look like a
> context-space to the reader.

Which wouldn't be a problem with the "overrides" slot.

> A lot easier/more portable than screwing with the MOP, for my money.

Couldn't agree more. (Also, I still might want this to run on Clisp,
so I don't really start want to mess with the MOP.)
From: Kenny Tilton
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <3E6A666B.2050108@nyc.rr.com>
Jacek Generowicz wrote:
> Kenny Tilton <·······@nyc.rr.com> writes:
> 
> 
>>ie, if there is no special context for a slot for an instance, the
>>slot simply holds the value. if you want context, you store a little
>>assoc of (context . value)s in there, with I guess t as the context
>>for the value outside any context.
> 
> 
> Actually, I was thinking of adding an "overrides" slot which would be
> an accoc or hash-table or whatever of (slot-name . overriding-value),

Oh, I thought there could be any number of contexts. Or were you 
thinking one override slot per possible context? Or would 
overriding-value above itslef be an assoc of context-value pairs?

One perhaps irrelevancy is how things look in an inspector. By stuffing 
a context-value structure in a slot and supplying a friendly 
print-object method for the struct you can see it all just by 
eye-balling the original slot.

My productivity with Cells has gone up nicely since making minor 
adjustments to accommodate the inspector.

>>A lot easier/more portable than screwing with the MOP, for my money.
> 
> Couldn't agree more. (Also, I still might want this to run on Clisp,
> so I don't really start want to mess with the MOP.)

That was one factor in me switching Cells back off a metaclass-based 
implementation (except I was thinking MCL).



-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Cells let us walk, talk, think, make love and realize
  the bath water is cold." -- Lorraine Lee Cudmore
From: Jacek Generowicz
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <tyf7kb7tqko.fsf@pcepsft001.cern.ch>
Kenny Tilton <·······@nyc.rr.com> writes:

> Oh, I thought there could be any number of contexts.

I only have use for two contexts, so far ...

> Or were you thinking one override slot per possible context?

That's a possible solution.

> Or would overriding-value above itslef be an assoc of context-value
> pairs?

I had thought of something along these lines, when considering the
possibility of more contexts. Still, it's not an issue yet.

> One perhaps irrelevancy is how things look in an inspector. By
> stuffing a context-value structure in a slot and supplying a friendly
> print-object method for the struct you can see it all just by
> eye-balling the original slot.
> 
> My productivity with Cells has gone up nicely since making minor
> adjustments to accommodate the inspector.

A good point. Thanks.
From: Kalle Olavi Niemitalo
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <871y1hk3tf.fsf@Astalo.kon.iki.fi>
Jacek Generowicz <················@cern.ch> writes:

> (Also, I still might want this to run on Clisp, so I don't
> really start want to mess with the MOP.)

Well then you can't use CHANGE-CLASS with mixins either.
From: Tim Bradshaw
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <ey3smtwof76.fsf@cley.com>
* Jacek Generowicz wrote:
> Actually, I was thinking of adding an "overrides" slot which would be
> an accoc or hash-table or whatever of (slot-name . overriding-value),
> where readers would look for their slots amongst the keys, and return
> the overriding-value if found, otherwise just return the slot's
> value.

This is a good trick.  If you need to have the thing work in a
multithreaded implementation - where the same object might be in
several contexts at once, you will lose doing this though[1].  Instead
you can turn things inside out by having a secret special variable
which holds an alist which maps from object to an alist of overridden
slots for that object.  Getting this right is a bit fiddly, but it's
doable.  This still depends on special variables `working right' with
the thread system, but this is likely true.

--tim

Footnotes: 
[1]  modulo appalling and essentially unimplementable hacks like LETF.
From: Raymond Wiker
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <86vfyr2yr8.fsf@raw.grenland.fast.no>
Jacek Generowicz <················@cern.ch> writes:

> Kenny Tilton <·······@nyc.rr.com> writes:
> 
> > ie, if there is no special context for a slot for an instance, the
> > slot simply holds the value. if you want context, you store a little
> > assoc of (context . value)s in there, with I guess t as the context
> > for the value outside any context.
> 
> Actually, I was thinking of adding an "overrides" slot which would be
> an accoc or hash-table or whatever of (slot-name . overriding-value),
> where readers would look for their slots amongst the keys, and return
> the overriding-value if found, otherwise just return the slot's value.

        If all you want is to add slots dynamically, you could use
something like the following:

(defclass py-class ()
  ((py-aux :initform (make-hash-table :test 'eq))))

(defmethod slot-missing ((class (eql (find-class 'py-class))) object
			 slot-name operation
			 &optional new-value)
  (with-slots (py-aux) object
    (ecase operation
      (setf
       (setf (gethash slot-name py-aux) new-value))
      (slot-boundp
       (second (gethash slot-name py-aux)))
      (slot-makunbound
       (remhash slot-name py-aux))
      (slot-value
       (gethash slot-name py-aux)))))

#||
(defparameter *the-py* (make-instance 'py-class))

(find-method #'slot-missing '() (mapcar #'find-class '(py-class t t t)))

(slot-value *the-py* 'barf)

(setf (slot-value *the-py* 'barf) 42)
||#

        This would obviosuly need some work (error handling :-) to
make it viable...

-- 
Raymond Wiker                        Mail:  ·············@fast.no
Senior Software Engineer             Web:   http://www.fast.no/
Fast Search & Transfer ASA           Phone: +47 23 01 11 60
P.O. Box 1677 Vika                   Fax:   +47 35 54 87 99
NO-0120 Oslo, NORWAY                 Mob:   +47 48 01 11 60

Try FAST Search: http://alltheweb.com/
From: Kalle Olavi Niemitalo
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <87d6l3uhca.fsf@Astalo.kon.iki.fi>
Jacek Generowicz <················@cern.ch> writes:

> (defmethod read-bar-in-secod-context ((object foo))
>   (if (slot-exists-p 'override-bar object)
>       (slot-value 'override-bar object)
>     (read-bar object)))

Could you use SLOT-BOUNDP instead?
From: Jacek Generowicz
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <tyfllzp8y42.fsf@lxplus069.cern.ch>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Jacek Generowicz <················@cern.ch> writes:
> 
> > (defmethod read-bar-in-secod-context ((object foo))
> >   (if (slot-exists-p 'override-bar object)
> >       (slot-value 'override-bar object)
> >     (read-bar object)))
> 
> Could you use SLOT-BOUNDP instead?

Could do, but it would require an override slot for every slot of
every instance, most of which would be unused, as I expect most slots
not to be overriden.
From: Chris Perkins
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <6cb6c81f.0303101237.3dee4d89@posting.google.com>
Jacek Generowicz <················@cern.ch> wrote in message news:<···············@pcepsft001.cern.ch>...
> I have some CLOS class instances.
> 
> I have two contexts.
> 
> By default, the slot-values should be the same in both contexts, but I
> want to make it possible to specify that a given slot of a given
> instance should be different in the second context.
> 

How many of the objects of each type are we talking about?  If it's
"some" or "many", then a sub-class that overrides the accessor seems
like the best choice.  This seems to fit the description of the
problem better - we naturally expect all objects of a class to behave
similarly in each context.  If _some_ of the objects behave
differently then their brethren in the same context, it sounds like
they are not the same class of object.

If it's just one aberrent object, then consider a second method that
dispatches not on the class, but the instance of the object.  I often
trap NIL objects this way to avoid cluttering my code with repetitive
(when (someObject) ....).

Chris
From: Kenny Tilton
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <3E6D5514.1060607@nyc.rr.com>
Chris Perkins wrote:
>  we naturally expect all objects of a class to behave
> similarly in each context.  

<digression>
Yes, and that was the big mistake of OO, why the Grail of reuse was 
never realized.

One thing I noticed about the Cello precursors was that after a while I 
was not defining new classes, because I could coax whatever behavior I 
needed out of a relatively generic high-level class by associating a 
different rule (as opposed to literal value) with different cellular 
slots, instance by instance. Instance-oriented programming? I know 
prototype-based object systems have this quality.

CLOS classes still provide a /lot/ of structure and differentiation, but 
instead of having hard-coded behavior (bye-bye reuse) with Cells one can 
morph them to accommodate new requirements.
</digression>


-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Cells let us walk, talk, think, make love and realize
  the bath water is cold." -- Lorraine Lee Cudmore
From: Jacek Generowicz
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <tyfd6kyrmng.fsf@pcepsft001.cern.ch>
········@medialab.com (Chris Perkins) writes:

> How many of the objects of each type are we talking about?

I would guess that it will range from tens to hundreds.

> If it's "some" or "many", then a sub-class that overrides the
> accessor seems like the best choice.  This seems to fit the
> description of the problem better - we naturally expect all objects
> of a class to behave similarly in each context.  differently then
> their brethren in the same context, it sounds like they are not the
> same class of object.

OK, let's make this abstract discussion concrete. 

I am automatically generating Python (the language by GvR, not the
CMUCL compiler) bindings of C and C++ libraries. The header files are
parsed and CLOS instances are created to represent the functions,
classes, namespaces, methods, types etc. of what is there. Let's take
plain C functions as a concrete example. I must hold the name of the C
function in my instance, so that the code generator knows what
function to call; this name cannot be changed. I expect that in some
libraries I will be prefectly satisfied with the original C-name of
the function and am happy for the funciton to be known by the same
name in Python. However, in some cases there will be reasons to change
the name by which the function is known in Python. The C-name is my
first context. The Python name is my second context. In some cases, I
expect to change _all_ the names (to systematically remove a
long-winded prefix, for example).

Are the instances where I choose to make the Python-name different
from the C-name, not of the same class?  On the conceptual level, I
would say "no", on the practical level ... I don't know. Ultimately,
It's all going to get hidden behind a layer of macros. The idea being
that the tool should be given a a bunch of header files and library
locations and produce a "default wrapping", with zero effort on the
user's behalf. The macro layer should allow me to write a very simple
configuration file/program/script, which modifies the behaviour of the
generator, to allow me to fine-tune the appearance of the final
product.

> If it's just one aberrent object,

Sometimes, sometimes not.

> then consider a second method that dispatches not on the class, but
> the instance of the object.

Yup, thought about that too ...
From: Chris Perkins
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <6cb6c81f.0303121252.7056659c@posting.google.com>
I'm sure you've solved this problem by now, probably just in the act
of writing it up.

Overall, behavior doesn't seem "aberrent", it seems well defined. 
_sometimes_ you want a different name.

It sounds like the question you are asking us and of yourself isn't a
technical question ("how do I do this?") but a question of approach. 
"Should I write this like a formal robust multi-use API" or "Should I
sprint to the result I want?". Sprinting probably means less code (an
oft overlooked advantage unto itself) and more freedom.

You could sprint by dispatching on value, dynamically adding a slot
and then overriding the current accessor method, or maybe making the
retrieval function a slot value and replacing it some cases with a
function wrapped in a closure.  Etc.   If you sprint now you can
always change it later.

If you go the other route, then I'd guess is that you don't want to
lose access to the original name.  So, given that,  I'd say that a
class seems like it should store the original-name (with accessors)
and have a method for retrieving the current-name given/in a
contextual environment (which can be as loosely defined as you want).
That second method can either just be an alternate accessor that you
use or a second slot.  If the idea of the "contextual environment"
takes on some sort of data representation, then it can be a second
argument and maybe you could dispatch off that as well.


Admittedly, I'm one of these guys who is wrong more often than I'm
right, but if this is the sort of question you are asking yourself
(full&robust vs quick&done) I'd say the correct answer is probably the
latter, because you seem like you are still at the open and exploring
phase of this.  You anticipate the problem of needing to change
representations, but aren't sure yet how much of it you'll really be
doing.

Chris
From: Jacek Generowicz
Subject: Re: Adding slots to an instance.
Date: 
Message-ID: <tyfd6kvr7x3.fsf@pcepsft001.cern.ch>
········@medialab.com (Chris Perkins) writes:

> It sounds like the question you are asking us and of yourself isn't a
> technical question ("how do I do this?") but a question of approach. 

I'm looking for alternative approaches, so that I have a selection
available when it becomes clearer exactly what the requirements turn
out to be ... besides generally wanting to expand my understanding of
how things can be done in the language.

> if this is the sort of question you are asking yourself (full&robust
> vs quick&done) I'd say the correct answer is probably the latter

My approach has been to get something that works (with limited
functionality) done as quickly as possible, play with it a bit, see
what the shortcomings seem to be, look for the next most useful
functionality, and modify what I've got in a direction that seems
sensible given the experience of playing with what I have. The goal is
to arrive at something full and robust, by having explored various
possibilities.

I certainly hope to (need to) use it do create some wrappings well
before the system is fully matured.

> you seem like you are still at the open and exploring phase of this.

Indeed.

> You anticipate the problem of needing to change representations, but
> aren't sure yet how much of it you'll really be doing.

I expect that in some cases I will want to change all names
(systematically), while in others I will want to change none, and in
others still, hand pick some names for changing.