Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Throwaway classes (or, prototype-oriented CLOS)

68 views
Skip to first unread message

Alessio

unread,
Oct 17, 2007, 6:35:34 AM10/17/07
to
Hi group. I'd like to have opinions on a particular technique I came
up using in CLOS.
Let's say I have a large collection of mixin classes, each tied to a
certain functionality. E.g. in the classical "shape" example, imagine
having

A shape class (the base class)
various subclasses (circle, rectangle etc. etc.)
and some mixins like
color-mixin (adds color information)
printable-mixin (adds data needed to print the object on paper)
active-mixin (adds callbacks for handling events on the object)

...and so on. Now by means of multiple inheritance I can combine the
mixins to get exactly the functionality I want (slots + methods). But,
this can lead to proliferation of classes which are really used just
as a combination of mixins, i.e. they don't add functionality but just
pack it under a name:

(defclass active-colored-circle (circle color-mixin active-mixin) ())
(defclass printable-colored-rectangle (rectangle color-mixin printable-
mixin) ())

et cetera.

Since I realised I was doing this repeatedly, and I was using such
classes only once or twice, I thought about using "throwaway classes"
instead - that is, combining mixins without actually creating a class
with a given name. This is what I did:

(defmacro make-object (superclasses &rest initargs)
`(make-instance (defclass ,(gensym "THROWAWAY-CLASS") ,superclasses
())
,@initargs))

then I can do (make-object (circle color-mixin active-mixin) :radius
10 :color :green :callback #'some-fn)

Is this the "right" way to do what I want? Or maybe you can achieve
the same effect using the MOP in a simple(r) way? Or, instead, is this
a solution to the wrong problem?

Any input is appreciated :)

cheers
Alessio Stalla

Sacha

unread,
Oct 17, 2007, 6:53:37 AM10/17/07
to
Alessio wrote:

> (defmacro make-object (superclasses &rest initargs)
> `(make-instance (defclass ,(gensym "THROWAWAY-CLASS") ,superclasses
> ())
> ,@initargs))
>

I think your throw away classes will never be thrown away, they won't be
garbage collected.

Sacha

Pascal Costanza

unread,
Oct 17, 2007, 6:54:43 AM10/17/07
to

If you want to stick to that approach, please note that whenever you
create an instance with make-object, you will always get a new class,
even if the same combination of mixins had already been used before.
This creates a considerable overhead.

Further note that such throwaway classes are never garbage collected,
because the CLOS MOP specifies that a class should have a reference to a
list of all its subclasses, and that is not specified as a weak pointer
(which I think is an oversight, due to the fact that the concept of weak
pointers doesn't exist in ANSI Common Lisp).

You could probably compensate for these problems using the CLOS MOP: You
could define a better make-object that caches combinations of mixins,
and you could probably even define a metaclass that hooks into the
garbage collector to ensure that classes without instances are
automatically removed.

However, I think that would be overkill.

Why don't you just factor out some of the functionality into common
superclasses that are always inherited from? CLOS provides the notion of
unbound slots, and that's a pretty straightforward way of indicating
that some feature is not present. This creates a certain overhead,
because all instances will always waste storage on features that aren't
used, but overall, the resulting class hierarchy would be much simpler...

I guess that Kenny will have an even better idea... ;)


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/

Rainer Joswig

unread,
Oct 17, 2007, 7:18:26 AM10/17/07
to
In article <1192617334....@t8g2000prg.googlegroups.com>,
Alessio <alessi...@gmail.com> wrote:


I think the use of mixins is best used for the (internal) implementation
part of a certain piece of code. Best not to expose it to the
user of some 'library'. I would expose only general purpose
classes which probably inherit from more mixins than needed.

Say you would write a BASIC-WINDOW class and then some
mixins (BACKING-STORE-MIXIN, BORDER-MIXIN, TRANSPARENCY-MIXIN, ...).
For the user there would be a STANDARD-WINDOW class which
inherits from BASIC-WINDOW and uses all the basic useful mixins and, say,
a class EXTENDED-STANDARD-WINDOW which provides all the features
you can imagine ;-) .

In some old system I saw an architecture where you
could add or remove classes to the superclasses
of a certain object at runtime. That would make
sense in some 'configuration' tools.

Michael Weber

unread,
Oct 17, 2007, 7:19:35 AM10/17/07
to
On Oct 17, 12:35 pm, Alessio <alessiosta...@gmail.com> wrote:
A shape class (the base class)
> various subclasses (circle, rectangle etc. etc.)
> and some mixins like
> color-mixin (adds color information)
> printable-mixin (adds data needed to print the object on paper)
> active-mixin (adds callbacks for handling events on the object)
>
[...]

> Is this the "right" way to do what I want? Or maybe you can achieve
> the same effect using the MOP in a simple(r) way? Or, instead, is this
> a solution to the wrong problem?

You'll find almost exactly this example in AMOP. Sample code that
works at least in SBCL (thanks to Christophe Rhodes' efforts):
<http://www.foldr.org/~michaelw/lisp/amop-programmatic-class.lisp>

Cheers,
Michael

Tim Bradshaw

unread,
Oct 17, 2007, 7:22:17 AM10/17/07
to
On Oct 17, 11:35 am, Alessio <alessiosta...@gmail.com> wrote:

> (defmacro make-object (superclasses &rest initargs)
> `(make-instance (defclass ,(gensym "THROWAWAY-CLASS") ,superclasses
> ())
> ,@initargs))
>
> then I can do (make-object (circle color-mixin active-mixin) :radius
> 10 :color :green :callback #'some-fn)
>
> Is this the "right" way to do what I want? Or maybe you can achieve
> the same effect using the MOP in a simple(r) way? Or, instead, is this
> a solution to the wrong problem?

As others have pointed out, these classes will never be collected
because they have references from their superclasses, at least.

Worse than that, you will probably end up with multiple classes which
are operationally identical (they mix the same classes) but are not,
in fact, the same class.

That being said, I don't think what you're trying to do is stupid, and
I have done similar things. The trick is to make your class-mixing
macro/function only create the class if it does not already exist. So
it needs a secret cache of classes it has already mixed for you and
should return a preexisting class if it can.

To do it right you need the MOP I think.

Somewhere I have code that does this but I am not sure where and it is
probably no good anyway - it was all more than 10 years ago I think.

--tim

Alessio

unread,
Oct 17, 2007, 8:51:09 AM10/17/07
to
Many thanks for all the feedback. The fact the class might not be
garbage collected had briefly crossed my mind :) but I dismissed it
too early...
The AMOP programmatic class is very interesting, it's certainly not
complicated and I'll investigate it further. In fact my initial idea
was something along that line (i.e. produce an unique class name from
the list of direct superclasses and always use it), but since I know
the MOP only very superficially (eventually I'll find the time to
study it...), I chose the simplest-looking solution.
And, thanks to Tim for dispelling my doubts - I wasn't sure I wasn't
trying to do a stupid thing!

Thanks again!
A.

Michael Weber

unread,
Oct 17, 2007, 1:56:51 PM10/17/07
to
On Oct 17, 1:18 pm, Rainer Joswig <jos...@lisp.de> wrote:
> In some old system I saw an architecture where you
> could add or remove classes to the superclasses
> of a certain object at runtime. That would make
> sense in some 'configuration' tools.

Robert Strandh et al. have a paper about just that:
"Using Stealth Mixins to Achieve Modularity" <http://dx.doi.org/
10.1109/ASWEC.2007.52>

(Robert might have a version without ACM tax somewhere)

Pascal Costanza's ContextL is related to that idea, too:
<http://common-lisp.net/project/closer/>


Cheers,
Michael

Alex Mizrahi

unread,
Oct 17, 2007, 2:01:34 PM10/17/07
to
A> (defmacro make-object (superclasses &rest initargs)
A> `(make-instance (defclass ,(gensym "THROWAWAY-CLASS") ,superclasses
A> ())
A> ,@initargs))

i think it's better to define class only once for each combination -- not
each time make-object is called, and not each time it gets macroexpanded or
whatever. while it still might be a bit suboptimal comparing to MOP
solution, i believe there would be no practical difference.
something like this:

(defmacro make-object (superclasses &rest initargs)

(let ((classname (intern (format nil "~{~a+~}" superclasses))))
`(make-instance (or (find-class ',classname nil)
(defclass ,classname ,superclasses
()))
,@initargs)))


Alan Crowe

unread,
Oct 18, 2007, 8:34:32 AM10/18/07
to
Alessio <alessi...@gmail.com> writes:
> ...and so on. Now by means of multiple inheritance I can combine the
> mixins to get exactly the functionality I want (slots + methods). But,
> this can lead to proliferation of classes which are really used just
> as a combination of mixins, i.e. they don't add functionality but just
> pack it under a name:
>
> (defclass active-colored-circle (circle color-mixin active-mixin) ())
> (defclass printable-colored-rectangle (rectangle color-mixin printable-
> mixin) ())
>
> et cetera.
>
> Since I realised I was doing this repeatedly, and I was using such
> classes only once or twice, I thought about using "throwaway classes"
> instead - that is, combining mixins without actually creating a class
> with a given name.

Wearing my maintenance programmers hat, this distinction,
between "throwaway classes" and "real classes" sounds like
quite a window into how the original programmer was
thinking. Any particular factorisation of the problem into
orthogonal classes probably appears to be the only possible
one, once you have thought of it, but others may approach
your code with clashing expectations and this is the
opportunity for the original programmer to spell out his
vision.

A big block comment, talking at a high level about the
application domain, what it is about this application that
gives rise to the distinction, would be solid gold.

Don't despise the DEFCLASSes of the throwaway classes

(defclass active-colored-circle
(circle color-mixin active-mixin)
()

(:documentation "Throwaway"))

would relate back to the big block comment, re-assuring the
maintainer that he is understanding the code.

Alan Crowe
Edinburgh
Scotland

0 new messages