From: ··········@eudoramail.com
Subject: Extensive use of change-class.
Date: 
Message-ID: <1138098717.269526.288590@g44g2000cwa.googlegroups.com>
I have a GUI that is linked to a model.  The model is a collection of
CLOS classes.  As the user enters information through the GUI, I use
change-class to move the current object down through the specialization
hierarchy.  I have about 90 change-class methods. The object model is
correct -- that is, I am not making mistakes such as using inheritance
to simulate containment.  It's just that the model's  behavior is
complex.

I must always have an object instantiated because there is dependent
behavior: a calculation must always be displayed -- the result of the
calculation changes as the class of the object changes.

I like the way I've done this, but I realize that maybe it's not the
best way.  The reason why I have used change-class so much is so that I
can maintain the flexibility of the GUI; the user is not presented with
an initial screen where they must enter all relevant information --
instead they can slowly walk toward their goal.  If you don't mind, I
would like to ask you a few questions:

1.  Given this scenario, do you think that such extensive use of
change-class is incorrect?

2.  If you are familiar with AI, is this the sort of problem that is
solved with description logic?

Thank you.  I very much value your thoughts.

-R
(Please don't email me because I have difficulty sorting spam out of
this account.  Thank you.)

From: Thomas A. Russ
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <ymiu0btfmuz.fsf@sevak.isi.edu>
··········@eudoramail.com writes:

> 
> I have a GUI that is linked to a model.  The model is a collection of
> CLOS classes.  As the user enters information through the GUI, I use
> change-class to move the current object down through the specialization
> hierarchy.  I have about 90 change-class methods. The object model is
> correct -- that is, I am not making mistakes such as using inheritance
> to simulate containment.  It's just that the model's  behavior is
> complex.

> 1.  Given this scenario, do you think that such extensive use of
> change-class is incorrect?

Well, I suppose that it should work.  Change class is generally a fairly
expensive operation, but it is one of the distinguishing features of
CLOS.  And any operation that is essentially user-driven won't be doing
change-class frequently enough for it to be a performance bottle-neck.

> 2.  If you are familiar with AI, is this the sort of problem that is
> solved with description logic?

Yes.  This is solved quite nicely with description logics.

I suggest you take a look at Loom, a system that I was involved in
creating.  It is an efficient and expressive description logic
implemented entirely in Common Lisp.  It is also now available under an
open source license.

   http://www.isi.edu/isd/LOOM/

> 
> Thank you.  I very much value your thoughts.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Kenny Tilton
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <IwoBf.5855$cj3.3897@news-wrt-01.rdc-nyc.rr.com>
··········@eudoramail.com wrote:
> I have a GUI that is linked to a model.  The model is a collection of
> CLOS classes.  As the user enters information through the GUI, I use
> change-class to move the current object...

The what? The current object? What current object?

Let me guess. You view the interaction as being the editing of some 
object X that starts out as class t or some root class about which 
nothing can be said. Then the user supplies some information, you use 
that to determine that X is now of some slightly more specific type. 
That then tells you what further questions to ask, and in turn what new 
GUI elements to present.

If that is right, my question then would be: are they really editing an 
object in their minds, ie, this is something they will save, look up 
later, use in some other context, give a name to, etc -- or is X known 
only to you the developer who is using it to manage the GUI?

Another way of asking that is....

> ... down through the specialization
> hierarchy.

...did this type hierarchy exist before you came up with this scheme to 
present the user with the minimum GUI, or is it something invented to 
serve that purpose? ie, Is this a natural hierarchy from the problem 
domain, or something you bend and twist to achieve your GUI programming 
objective?

>  I have about 90 change-class methods. The object model is
> correct -- that is, I am not making mistakes such as using inheritance
> to simulate containment.  It's just that the model's  behavior is
> complex.
> 
> I must always have an object instantiated because there is dependent
> behavior: a calculation must always be displayed -- the result of the
> calculation changes as the class of the object changes.
> 
> I like the way I've done this, but I realize that maybe it's not the
> best way.  The reason why I have used change-class so much is so that I
> can maintain the flexibility of the GUI; the user is not presented with
> an initial screen where they must enter all relevant information --
> instead they can slowly walk toward their goal.

Well, there are a lot of ways of having a GUI presentation change as the 
user enters information, so this alone does not justify using 
change-class. Not saying change-class is a bad idea, just that there are 
other ways.

What makes me think it might be a bad idea is that you have ninety 
methods. How did they all arise? A combinatorial explosion off different 
ways of answering a relatively few questions?

>  If you don't mind, I
> would like to ask you a few questions:
> 
> 1.  Given this scenario, do you think that such extensive use of
> change-class is incorrect?

It is hard to say with so little information. An example would help. "If 
they say A, I change-class to J. Then the GUI shows W."

If the user is using this GUI to identify to the application an object, 
including importantly its place within a natural type hierarchy, then 
change-class has some obvious appeal as a solution.

If not, I have my doubts. But I am just one programmer.

kenny
From: ··········@eudoramail.com
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <1138120541.739020.221570@g14g2000cwa.googlegroups.com>
Here's what happened: I was writing a long reply to your message
explaining why I needed a specialization hierarchy (in short, every
class in the hierarchy has unique behavior) and how my GUI was
structured (as a series of option panes).  To support my argument on
why it was necessary to use change-class and
update-instance-for-different-class, I considered several alternative
designs.

It turns out that if I use multimethods I can avoid the need for a deep
specialization hierarchy.  Boy, don't I feel stupid!

I think that there's a book missing on my bookshelf: CLOS design
heuristics.
From: Ron Garret
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <rNOSPAMon-A325F5.09230524012006@news.gha.chartermi.net>
In article <························@g14g2000cwa.googlegroups.com>,
 ··········@eudoramail.com wrote:

> Here's what happened: I was writing a long reply to your message
> explaining why I needed a specialization hierarchy (in short, every
> class in the hierarchy has unique behavior) and how my GUI was
> structured (as a series of option panes).  To support my argument on
> why it was necessary to use change-class and
> update-instance-for-different-class, I considered several alternative
> designs.
> 
> It turns out that if I use multimethods I can avoid the need for a deep
> specialization hierarchy.  Boy, don't I feel stupid!

You shouldn't.  This is a very common phenomenon.  Forcing thoughts into 
coherent sentences often helps one to achieve clarity.

Once you realize this it can become a very powerful tool in your life.

rg
From: Alan Crowe
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <8664o9k13d.fsf@cawtech.freeserve.co.uk>
··········@eudoramail.com writes:
> Here's what happened: I was writing a long reply to your message
> explaining why I needed a specialization hierarchy (in short, every
> class in the hierarchy has unique behavior) and how my GUI was
> structured (as a series of option panes).  To support my argument on
> why it was necessary to use change-class and
> update-instance-for-different-class, I considered several alternative
> designs.
> 
> It turns out that if I use multimethods I can avoid the need for a deep
> specialization hierarchy.  Boy, don't I feel stupid!
> 
> I think that there's a book missing on my bookshelf: CLOS design
> heuristics.

I had hoped that Keene would be that book, but I found it
rather slow and it didn't seem to go very far. I guess her
aim was to provide a more relaxed exposition of CLOS than
CLtL2, which is terse.

If you could find the time to write up your experience and
add a page to cliki on CLOS design heuristics I would be a
most attentive reader.
--
Alan Crowe
Edinburgh
Scotland
From: Kenny Tilton
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <mBxBf.8434$SD.1258@news-wrt-01.rdc-nyc.rr.com>
··········@eudoramail.com wrote:
> Here's what happened: I was writing a long reply to your message
> explaining why I needed a specialization hierarchy (in short, every
> class in the hierarchy has unique behavior) and how my GUI was
> structured (as a series of option panes).  To support my argument on
> why it was necessary to use change-class and
> update-instance-for-different-class, I considered several alternative
> designs.
> 
> It turns out that if I use multimethods I can avoid the need for a deep
> specialization hierarchy.  Boy, don't I feel stupid!

Blame Lisp. So many different ways to achieve the same goal.

That is why I was asking goofy questions about what your user was 
thinking and whether the hierarchy existed independently of the GUI 
requirement. Those are the kind of tie-breaker questions I use when I 
have a hard time choosing between algorithms.

btw, my Cells project was invented to make dynamic GUIs manageable.

    http://common-lisp.net/project/cells/

Turns out they are more generally useful, but they do rock when it comes 
to making a GUI that changes in response to user input. There is a 
Cells-gtk project if you like Gtk, and you can check with the Ltk 
project to see if Cells is part of that yet. If not, I think I will be 
resurrecting soon my Celtic project (+ Cells Ltk).


kenny
From: Brad Might
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <1138214816.000390.225640@g14g2000cwa.googlegroups.com>
I've been thinking of blogging about this but here's a good spot to
bring up my use of change class and see whether others think i'm
putting it to good use or doing something completely goofy.

I'm using the same pattern for all my game resources...still exploring
that space but here's how it looks for the textures.

I have a hash table which contains texture-definition objects, indexed
by an id. The texture defnition object contains enough info to find and
load the texture with appropriate parameters.

I only want to have 1 instance of a given texture loaded at a time for
resource (memory, OpenGL) preserving reasons. Of course 1 mechanism
would be to have some form of mappging from texture-definition to
texture, if said texture is already instantiated.

What i have done instead is subclassed texture from texture-definition.
 When a textured-object (a texture using object :-) ) is loaded, it
looks up the texture-definition, and puts it in its texture slot. At
this time it will place a call to load-texture.

load-texture is a generic function....when the argument is a
texture-definition, it will make the appropriate calls to do what is
needed to create the OpenGL texture, change-class texture-definition to
texture, and add the OpenGL texture-id to that object.

load-texture specialized on texture is a no op...so if the
texture-definition fetched already happens to be a texture object, no
processing need be done.

A similar thing is done for draw...when draw is called upon a
texture-definition, it is a no op, since it is not yet in a form where
it can be rendered.  draw on a texture will set the appropriate OpenGL
state to use that texture object.

There's one more wrinkle in this....what happens when a second object
wants the texture as well?  Sharing is ok...that's what I want to
do...the only issue arises when the second object accesses the
texture-defnition after the first object, but before it has been
loaded?    I am adding a texture-loading object....(load
texture-definition) can change the texture-definition instance to a
texture-loading instance...where load and draw are no-ops upon
it..therefore the second object to grab it will have no problems when
it calls load or draw, and will then benefit when thet texture-loading
object becomes a texture.

Now resource release....when done with the texture, changing it back to
a texture defnition....same old problem..how do i know when all objects
are done with it...a dependency list/reference counting scheme, or use
finalization and 'hope'/design a way to call a full gc in a timely
manner to enable finalization to actually occur?
From: ··········@eudoramail.com
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <1138263306.538896.296690@g14g2000cwa.googlegroups.com>
I think that you are making the mistake of modeling the behavior of an
object over its lifetime with classes.

Let's say that you want to model a stack.  You are doing this:

(defclass stack ()
  ((elements :initform nil)
   (index :initform 0)
   (maximum :initform 2)))

(defclass empty-stack (stack)
  ())

(defclass normal-stack (stack)
  ())

(defclass full-stack (stack)
  ())

(defun make-stack ()
  (make-instance 'empty-stack))

(defgeneric push-element (stack element)
            (:documentation "Add ELEMENT to STACK if possible. Return
the current contents of the stack and an indicator of the success of
the operation (T if the element was successfully added, NIL
otherwise)."))

(defmethod push-element ((self empty-stack) element)
  (with-slots (elements index maximum) self
    (if (= index maximum)
        (progn
          (change-class self 'full-stack)
          (values elements nil))
      (progn
        (change-class self 'normal-stack)
        (setf elements (cons element elements))
        (incf index)
        (values elements t)))))

(defmethod push-element ((self normal-stack) element)
  (with-slots (elements index maximum) self
    (if (= index maximum)
        (progn
          (change-class self 'full-stack)
          (values elements nil))
      (progn
        (setf elements (cons element elements))
        (incf index)
        (values elements t)))))

(defmethod push-element ((self full-stack) element)
  (with-slots (elements) self
    (values elements nil)))

And here is how you would use it:

CL-USER 70 > (setf cargo-hold (make-stack))  ; Start with an empty
stack
#<EMPTY-STACK 20681144>

CL-USER 71 > (push-element cargo-hold 'blue-container) ; Change class
to a normal stack
(BLUE-CONTAINER)
T

CL-USER 72 > (class-of cargo-hold)
#<STANDARD-CLASS NORMAL-STACK 22FD0554>

CL-USER 73 > (push-element cargo-hold 'green-container)
(GREEN-CONTAINER BLUE-CONTAINER)
T

CL-USER 74 > (push-element cargo-hold 'red-container) ; Change class to
a full stack
(GREEN-CONTAINER BLUE-CONTAINER)
NIL

CL-USER 75 > (class-of cargo-hold)
#<STANDARD-CLASS FULL-STACK 22FF6394>




You are changing an object's class based on its normal, everyday
behavior. It doesn't have to be so complicated; instead of using
polymorphism use an if statement.

(defclass stack ()
  ((elements :initform nil)
   (index :initform 0)
   (maximum :initform 2)))

(defun make-stack ()
  (make-instance 'stack))

(defmethod push-element ((self stack) element)
  (with-slots (elements index maximum) self
    (if (= index maximum)  ;; full stack
        (values elements nil)
      (progn
        (setf elements (cons element elements))
        (incf index)
        (values elements t)))))

Another thing I noticed is that you are using no-ops liberally.  I
think that, generally, this is a mistake.  Let's say that you have a
class MUSICIAN:

(defclass musician ()
  (instrument))

(defgeneric play (musician)
  (:documentation "Play instrument."))

(defmethod play ((self musician))
  (format t ·@$%^#$~%"))

(defclass person (musician)
  ())

(defmethod play ((self person))
  ())

This is saying that a person is a musician who doesn't play an
instrument.  I think than no-ops indicate that the inheritance
hierarchy is upside down.  It should be something like this:

(defclass person ()
  ())

(defclass musician (person)
  (instrument))

I think the reason you are having no-ops could be psychological -- if
you already have a class, you feel that you should inherit from the
existing class rather than turn the hierarchy upside down.

Brad, I haven't talked about your specific games application because
I'm not familiar with the domain.


-R
(i have problems receiving email on this account)
From: Barry Margolin
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <barmar-ABE95F.21215726012006@comcast.dca.giganews.com>
In article <························@g14g2000cwa.googlegroups.com>,
 ··········@eudoramail.com wrote:

> I think that you are making the mistake of modeling the behavior of an
> object over its lifetime with classes.

I remember Symbolics made a mistake like this, with their initial Table 
facility (extensions of the CL hash-table type) implementation.  They 
recognized that different table designs are appropriate for different 
tables, depending on the size, equality predicate, sparseness, and other 
criteria (e.g. an association list or binary-search array may be fine 
for small tables, while a hash table is appropriate for large ones).  
They tried doing this with different classes, but because some of these 
criteria can change over the life of a table they ended up using 
CHANGE-CLASS.

For various reasons this turned out to be a bad decision.  I think it 
was hard to get all the UPDATE-INSTANCE-FOR-CHANGED-CLASS methods right, 
because they had to support almost any combination of old and new 
classes.  So they later redid it completely, with a single TABLE class 
that had an IMPLEMENTATION slot (please excuse any slight innaccuracies, 
I'm trying to remember something from over 15 years ago).  I think there 
were a number of implementation classes, but rather than change 
instances from one to another, the containing TABLE object would create 
a new instance and copy the elements from one to the other.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Brad Might
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <1138384455.059247.178680@o13g2000cwo.googlegroups.com>
··········@eudoramail.com wrote:
> I think that you are making the mistake of modeling the behavior of an
> object over its lifetime with classes.
>

I can see your points here, but i'm not sure that changing the code to
be seemingly more complex (adding if statements depending upon object
state) is the right idea either.

The reason for common inheritance of texture-definition by
texture-loading and texture is that once i am done with a texture i can
revert to texture-definition since the slots are preserved throughout
all the changes.

An alternative and closer modelling of the situation would not put the
texture information into the texture definition at all (ie...following
the example of a stack being a single class with different states to be
conditionalized upon), I would instead think of a texture as an
independent object which is named/identified/described by the
texture-definition.

A more exact mapping would be between a texture definition and a
texture, and when such a mapping is not existent, the texture would
need to be created.

The multiple class solution feels much cleaner to me.  Whenever i start
encountering conditionals (if) in an object oriented language I often
find that polymorphism looks cleaner and feels better (i'm seeing if as
a code smell...probably because i have done a lot of Smalltalk
programming).
I don't find null-ops or null objects to be a problem with
polymorphism, and to me it is a cleaner solution...the alternative
reminds me too much of all the checking for null in Java code.
From: Peter Seibel
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <m28xt1x9pc.fsf@gigamonkeys.com>
"Brad Might" <······@gmail.com> writes:

> ··········@eudoramail.com wrote:
>> I think that you are making the mistake of modeling the behavior of
>> an object over its lifetime with classes.
>>
>
> I can see your points here, but i'm not sure that changing the code to
> be seemingly more complex (adding if statements depending upon object
> state) is the right idea either.
>
> The reason for common inheritance of texture-definition by
> texture-loading and texture is that once i am done with a texture i
> can revert to texture-definition since the slots are preserved
> throughout all the changes.
>
> An alternative and closer modelling of the situation would not put the
> texture information into the texture definition at all (ie...following
> the example of a stack being a single class with different states to be
> conditionalized upon), I would instead think of a texture as an
> independent object which is named/identified/described by the
> texture-definition.
>
> A more exact mapping would be between a texture definition and a
> texture, and when such a mapping is not existent, the texture would
> need to be created.
>
> The multiple class solution feels much cleaner to me.  Whenever i start
> encountering conditionals (if) in an object oriented language I often
> find that polymorphism looks cleaner and feels better (i'm seeing if as
> a code smell...probably because i have done a lot of Smalltalk
> programming).
> I don't find null-ops or null objects to be a problem with
> polymorphism, and to me it is a cleaner solution...the alternative
> reminds me too much of all the checking for null in Java code.

There are other options that take advantage of polymorphic dispatch
without resorting to changing the class of the object at runtime:

  (defclass texture-definition
    ((state :initarg :state :initform 'loading :accessor state)
     (some-other-slot ...)
     (and-another ...)))

  (defun frob-texture (texture)
    (frob-texture/state (state texture) texture))

  (defgeneric frob-texture/state (state texture)
    (:documentation "Frob the texture in some way dependent on it's current state."))

  (defmethod frob-texture/state ((state (eql 'loading)) texture)
    (do-one-thing))

  (defmethod frob-texture/state ((state (eql 'completed)) texture)
    (do-another))

Now you can create a single object that preserves its identity and
slot values throughout it's lifecycle but can have certain
functionality polymorphically dispatched based on the value of it's
current state.

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: Alan Crowe
Subject: Re: Extensive use of change-class.
Date: 
Message-ID: <868xt5k1ow.fsf@cawtech.freeserve.co.uk>
··········@eudoramail.com writes:

> I have a GUI that is linked to a model.  The model is a collection of
> CLOS classes.  As the user enters information through the GUI, I use
> change-class to move the current object down through the specialization
> hierarchy.  I have about 90 change-class methods. The object model is
> correct -- that is, I am not making mistakes such as using inheritance
> to simulate containment.  It's just that the model's  behavior is
> complex.
...
> 1.  Given this scenario, do you think that such extensive use of
> change-class is incorrect?

I've only used change-class once

(defun refine-token-type (token)
  "We only use this to spot rests"
  (typecase token
        (note (if (find #\z (token-text token))
                  (change-class token 'rest)
                token))
        (otherwise token)))

I think I'm starting down a path that parallels yours. I
have input from the user, and as processing procedes the
tokens get analysed, changed to more specific classes, and
get details filled in by rather elaborate defaulting rules.

It took me a long time to realise that change class would
work. Coming from a C/assembler/hardware background I tend
to think of a class with three slots as occupying three
words of memory. My incorrect assumption was that if I
change it to a class with four slots it will no longer fit
in the place where it used to live, so it has to be
moved. This will break eql. The new class will not show up
in data structures that pointed to the old class.

In fact 7.2 Changing the Class of an Instance
specifically requires that implementations make this work

    Changing the class of an instance does not change its
    identity as defined by the eq function.

X3J13 has required implementors to do extra work in order to
make change-class really useful. I take that as a hint that
change-class is not just completest decoration but is
intended for heavy use.

I hope it is the right technique to use in you application :-)
--
Alan Crowe
Edinburgh
Scotland