How much use of CLOS?

858 views
Skip to first unread message

Peter Seibel

unread,
Oct 15, 2002, 1:55:13 AM10/15/02
to
This is, I guess, a poll. I'm interested in how experienced Lisp users
would describe their use of CLOS:

a) Always. I never use a defun or defstruct, may God smite me down if
I do.

b) Sometimes. When appropriate. But sometimes it's just not worth it.

c) Never. OO is nothing but media hype. It's too bad Common Lisp had
to get poluted with it.

Obviously a) and c) are pretty extreme. I'm guessing that most folks
will classify their use of CLOS under b) but based on some things I've
read here and elsewhere I'm speculating that there are *some* c) folks
out there and maybe more than I'd expect. And I've got the Java brain
damage bad enough to be inclined to fall into a) so I'm wondering if
there are others. If you do classify your use under b) I'd be further
interested to know how you decide when is appropriate and what makes
it sometimes not worth it.

Finally, if anyone has pointers to source code that is an example of
good Common Lisp style in any of the three categories of CLOS use, I'd
be interested.

-Peter

--
Peter Seibel
pe...@javamonkey.com

Christopher C. Stacy

unread,
Oct 15, 2002, 4:19:50 AM10/15/02
to
>>>>> On Tue, 15 Oct 2002 05:55:13 GMT, Peter Seibel ("Peter") writes:
Peter> This is, I guess, a poll. I'm interested in how experienced
Peter> Lisp users would describe their use of CLOS:

Although I spent all day today writing code that uses CLOS heavily,
I wouldn't mark myself for any of your poll choices.

Choices (a) and c) are silly, and (b) is loaded with some
kind of weird metric that I don't understand.

I think the only answer that makes sense
-- and it's not on your troll -- err, I mean, poll --
would be: "Experienced Lisp programmers use CLOS when appropriate."
(Wouldn't you assume so, without asking?)

Peter> a) Always. I never use a defun or defstruct,
Peter> may God smite me down if I do.
Peter> b) Sometimes. When appropriate. But sometimes it's just not worth it.
Peter> c) Never. OO is nothing but media hype. It's too bad Common Lisp had
Peter> to get poluted with it.

Tim Bradshaw

unread,
Oct 15, 2002, 4:52:41 AM10/15/02
to
* Peter Seibel wrote:
> This is, I guess, a poll. I'm interested in how experienced Lisp users
> would describe their use of CLOS:

> a) Always. I never use a defun or defstruct, may God smite me down if
> I do.

> b) Sometimes. When appropriate. But sometimes it's just not worth it.

> c) Never. OO is nothing but media hype. It's too bad Common Lisp had
> to get poluted with it.

Well, like all sensible people I'm in (b)[1]. Quite apart from anything
else, using structures and CLOS is not mutually exclusive: Lisp
actually *is* an object oriented language, unlike Java, so in Lisp
everything is an object, including structures and integers, functions,
strings &c. There are no bogus `not an object' things in the language
put there in some half-witted attempt to make the language design
`simpler' or `more efficient' while screwing everyone who ever wants
to use the thing. Further, structures actually have single
inheritance - in other words they provide an inheritance system as
powerful as anything Java gives you.

(defstruct a
x y)

(defstruct (b (:include a))
z)

(defgeneric grind (x)
(:method ((x a))
(+ (a-x x) (a-y x)))
(:method ((x b))
(+ (b-z x) (call-next-method)))
(:method ((x number))
x))

Secondly, I use functions when I don't intend the behaviour I'm
defining to be extensible, or when there are no obvious objects in the
game. For instance, I have a function (which Erik would hate, with
good reason) PRETTY-DATE-STRING, which prints a pretty date string.
It takes one optional argument, which is a universal time, the default
being (get-universal-time). Making it a GF would be kind of strange,
because it has no arguments...

Again, Lisp has been well-designed - there is no syntactic difference
between code which uses GFs and code that uses ordinary functions.
Therefore, if I design using plain functions but then decide I want to
change to GFs, for whatever reason, nothing other than the definitions
of my functions/GFs need change. Compare with C++, say which has two
syntaxes, forcing a big up-front decision about whether to use
functions of methods, or changes to all client code when that decision
changes. Java seems to have done this somewhat better.

--tim

Footnotes:
[1] Yes, if you are not in (b) you are not sensible and must be
terminated. Please do not leave your house, the black
helicopters will arrive shortly.

Erik Naggum

unread,
Oct 15, 2002, 10:51:17 AM10/15/02
to
* Peter Seibel

| This is, I guess, a poll.

An extremely poorly done one that will yield worthless results.

| I'm interested in how experienced Lisp users would describe their use of
| CLOS:

Please understand that your questions do not exhibit any relationship
whatsoever to your stated interest.

--
Erik Naggum, Oslo, Norway

Act from reason, and failure makes you rethink and study harder.
Act from faith, and failure makes you blame someone and push harder.

Thomas F. Burdick

unread,
Oct 15, 2002, 2:18:49 PM10/15/02
to
Peter Seibel <pe...@localhost.localdomain> writes:

> This is, I guess, a poll. I'm interested in how experienced Lisp users
> would describe their use of CLOS:
>
> a) Always. I never use a defun or defstruct, may God smite me down if
> I do.

This doesn't make sense. In particular, the
relation function:generic-function::structure-object:standard-object
is false. I use defclass unless I'm finished with developing a module
and I'm working on performance. If you suspect something might
ultimately be a structure, you can stick to single-inheritance, and
have no problems.

generic-functions are generic *functions*, so if it doesn't make sense
for something to be extensible, you use functions. Additionally,
types aren't the only way to dispatch on something, and CLOS
(correctly) doesn't let you dispatch using the full type system. For
example, part of an application I'm working on is OO, and another part
does symbol processing. When the argument you want to dispatch on is
always of type (CONS SYMBOL LIST), generic functions aren't the right
tool. In this case, we use the symbol's plist for dispatching.

> b) Sometimes. When appropriate. But sometimes it's just not worth it.

This looks awefully troll-like, but I'm giving you the benefit of the
doubt, and assuming it was momentary foolishness. The correct
response here is "when appropriate". It's not that sometimes it's not
worth it, it's that sometimes it's wrong. Back to this function,
imagine the following:

(deftype symbolic-expression ()
'(cons symbol list))

(deftype context ()
;; Right now we're using alists, but this could change
'list)

(defun frob-expression (symbolic-expression context)
(check-type symbolic-expression symbolic-expression)
(check-type context)
(let ((fn (get (first symbolic-expression) 'frob-function)))
(check-type fn function)
(funcall fn symbolic-expression context)))

or, we could force it into an OO paradigm:

(deftype symbolic-expression ...)
(deftype context ...)

(defgeneric frob-expression (symbolic-expression context))
(defmethod frob-expression ((symbolic-expression list) (context list))
(check-type symbolic-expression symbolic-expression)
(check-type context)
(expression-frobber (first symbolic-expression)
(rest symbolic-expression)
context))

(defgeneric expression-frobber (symbol more-expression context))
(defmethod expression-frobber ((symbol (eql foo)) more-expression context)
...)
(defmethod expression-frobber ((symbol (eql bar)) more-expression context)
...)
(defmethod expression-frobber ((symbol (eql baz)) more-expression context)
...)

;;; etc

It's not an issue of "worth it" vs "not worth it", it's a matter of
using the correct tools for the correct problem.

--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'

Marco Antoniotti

unread,
Oct 15, 2002, 2:24:10 PM10/15/02
to

Peter Seibel <pe...@localhost.localdomain> writes:

> This is, I guess, a poll. I'm interested in how experienced Lisp users
> would describe their use of CLOS:
>
> a) Always. I never use a defun or defstruct, may God smite me down if
> I do.

a/b) Most of the times. I use DEFSTRUCT/DEFUN when I know that the
use of the resulting entities will be limited and/or very
performance demanding.

> b) Sometimes. When appropriate. But sometimes it's just not worth it.
>
> c) Never. OO is nothing but media hype. It's too bad Common Lisp had
> to get poluted with it.
>

Cheers

--
Marco Antoniotti ========================================================
NYU Courant Bioinformatics Group tel. +1 - 212 - 998 3488
715 Broadway 10th Floor fax +1 - 212 - 995 4122
New York, NY 10003, USA http://bioinformatics.cat.nyu.edu
"Hello New York! We'll do what we can!"
Bill Murray in `Ghostbusters'.

Michael Sullivan

unread,
Oct 15, 2002, 5:53:35 PM10/15/02
to
Peter Seibel <pe...@localhost.localdomain> wrote:

> This is, I guess, a poll. I'm interested in how experienced Lisp users
> would describe their use of CLOS:
>
> a) Always. I never use a defun or defstruct, may God smite me down if
> I do.
>
> b) Sometimes. When appropriate. But sometimes it's just not worth it.
>
> c) Never. OO is nothing but media hype. It's too bad Common Lisp had
> to get poluted with it.

The problem with your poll is that this is a sliding scale. (a) and (c)
represent the polar extremes where you will find only language
ideologues, and no actual programmers (at least not good ones).

So everyone worth listening to will answer (b) and what does that tell
you exactly?

I'm guessing that what you really want to know is how many people are
closer to one end or the other and why.

As far as getting to the meat of the issue, I pretty much agree with Tim
Bradshaw's reponse. The most wonderful thing about lisp is not having
to make this decision up front.

There can be some case made for the equivalent of (a) in languages where
switching from one paradigm to another requires client code rewrites --
with few exceptions, it results in maximum design flexibility. Lisp,
OTOH, has that kind of design flexibility by nature, so an upfront
decision is not so very important. You get a lot of data modularity and
other OO benefits without using CLOS at all.


Michael

--
Michael Sullivan
Business Card Express of CT Thermographers to the Trade
Cheshire, CT mic...@bcect.com

Kenny Tilton

unread,
Oct 15, 2002, 10:34:58 PM10/15/02
to

Peter Seibel wrote in message ...

>This is, I guess, a poll. I'm interested in how experienced Lisp users
>would describe their use of CLOS:
>
> a) Always. I never use a defun or defstruct, may God smite me down if
> I do.
>
> b) Sometimes. When appropriate. But sometimes it's just not worth it.
>
> c) Never. OO is nothing but media hype. It's too bad Common Lisp had
> to get poluted with it.
>

Always, unless I am tuning.

kenny
clinisys


Peter Seibel

unread,
Oct 15, 2002, 11:09:08 PM10/15/02
to
t...@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> Peter Seibel <pe...@localhost.localdomain> writes:
>
> > This is, I guess, a poll. I'm interested in how experienced Lisp users
> > would describe their use of CLOS:
> >
> > a) Always. I never use a defun or defstruct, may God smite me down if
> > I do.
>
> This doesn't make sense. In particular, the relation
> function:generic-function::structure-object:standard-object is
> false.

I'm sorry--why is that false? Because generic functions *can* be used
on arguments that aren't instances of classes? And defun-defined
functions can?

> I use defclass unless I'm finished with developing a module and I'm
> working on performance. If you suspect something might ultimately
> be a structure, you can stick to single-inheritance, and have no
> problems.

So that's the kind of thing I was wondering about: if I understand you
correctly, your inclination is to start with classes and only switch
to structs as an optimization--a kind of strength-reduction. That's a
more a-style approach (though obviously not as extreme as I put it)
than to start with structs and then "upgrade" to classes when the
advantages they bring (being redefinable on the fly being one,
multiple inheritance another, as I understand it) become useful.

> generic-functions are generic *functions*, so if it doesn't make
> sense for something to be extensible, you use functions.
> Additionally, types aren't the only way to dispatch on something,
> and CLOS (correctly) doesn't let you dispatch using the full type
> system. For example, part of an application I'm working on is OO,
> and another part does symbol processing. When the argument you want
> to dispatch on is always of type (CONS SYMBOL LIST), generic
> functions aren't the right tool. In this case, we use the symbol's
> plist for dispatching.
>
> > b) Sometimes. When appropriate. But sometimes it's just not worth it.
>
> This looks awefully troll-like, but I'm giving you the benefit of
> the doubt, and assuming it was momentary foolishness.

I appreciate that--as I was getting ready for work this morning, it
occured to me that this might seem troll-like. FWIW, it wasn't
intended that way.

What makes the OO code "wrong" and the p-list solution the correct
tool in this case? It's not obvious to me--I could imagine that one
requires more or fewer lines of code? (Though presumably the p-list
based implementation would require some code not shown here to hang
the 'frob-function properties off of foo, bar, and baz, right?) Or the
efficiency of dispatch might differ? Or something else?

Peter Seibel

unread,
Oct 15, 2002, 11:25:57 PM10/15/02
to
Erik Naggum <er...@naggum.no> writes:

> * Peter Seibel
> | This is, I guess, a poll.
>
> An extremely poorly done one that will yield worthless results.
>
> | I'm interested in how experienced Lisp users would describe their use of
> | CLOS:
>
> Please understand that your questions do not exhibit any relationship
> whatsoever to your stated interest.

Okay, fair enough. Let me try again in a more straightforward way:
I've read the Keene book and _The Art of the Meta Object Protocol_ and
think I have a reasonable understanding (though not a lot of practical
experience) of the various mechanisms available in CLOS for doing OO
programming. However I don't have any feel for when I *should* use
CLOS or how to decide. I'd be interested to hear from experienced Lisp
programmers about how *you* decide. Are there features of a problem
that call out for using or not using CLOS? Are there rules of thumb
that you use such as (and these may be totally wrong, I'm just
guessing what they might be given my limited experience): always start
with classes and then "downgrade" to structs for performance reasons
or vice versa, always start with structs and then "upgrade" as you
require specific features of classes?

Or another way: given that I've spent most of my career programming in
an an OO style, my inclination on first opening a new .lisp file is to
to type "(defclass ...". Is that a reasonable idiomatic Lisp approach
or is that baggage from my past that I should try to shed.

Apologies if my earlier post tried to get at this in a too cute, too
eliptical, or just plain too stupid manner.

Jochen Schmidt

unread,
Oct 16, 2002, 12:05:25 AM10/16/02
to
Peter Seibel wrote:

> t...@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
>
>> Peter Seibel <pe...@localhost.localdomain> writes:
>>
>> > This is, I guess, a poll. I'm interested in how experienced Lisp users
>> > would describe their use of CLOS:
>> >
>> > a) Always. I never use a defun or defstruct, may God smite me down if
>> > I do.
>>
>> This doesn't make sense. In particular, the relation
>> function:generic-function::structure-object:standard-object is
>> false.
>
> I'm sorry--why is that false? Because generic functions *can* be used
> on arguments that aren't instances of classes? And defun-defined
> functions can?

Point a seems to imply that there is a similar relation between
defun<->defmethod and defstruct<->defclass.

This is actually not true. The common thing between a structure-class and a
standard-class is that they both are subclasses of class. There is no
difference in using them as specializers in a method-definition.

I think structure-classes are best imagined as a mean to optimize your code
by accepting the fact that one loses some flexibility in expressiveness and
comfort.

>> I use defclass unless I'm finished with developing a module and I'm
>> working on performance. If you suspect something might ultimately
>> be a structure, you can stick to single-inheritance, and have no
>> problems.
>
> So that's the kind of thing I was wondering about: if I understand you
> correctly, your inclination is to start with classes and only switch
> to structs as an optimization--a kind of strength-reduction. That's a
> more a-style approach (though obviously not as extreme as I put it)
> than to start with structs and then "upgrade" to classes when the
> advantages they bring (being redefinable on the fly being one,
> multiple inheritance another, as I understand it) become useful.

I think it is generally a good idea to first try to get your problem solved
and then think about doing optimizations. Standard-classes can do anything
you can do with structure-classes so there is no real help in getting your
problem solved faster.

Some time ago I wrote together with a colleguae a genetic algorithm to do
TSP optimization. Our first version was quickly done and then we sat down
and optimized by changing datastructures and allocation issues which finally
lead to a >30 fold speedup. Finding the *right* datastructure was easy
since after solving the problem we knew what they need and what not. I'm
sure if we would have tried that from the beginning the result would have
been slower, buggier and we would have needed more time to finish it.

Well - one could say that you just not need the full flexibility of CLOS
here since you get the same by a rather simple hack using plists.

Syntactically there is no real difference between both approaches. One could
wrap both, the method definition or the registration of the function with
the symbol-plist into a macro like

(def-expression-frobber foo (more-expression context)
...)

which expands to either

(defmethod expression-frobber ((symbol (eql foo)) more-expression
context)
...)

or

(eval-when (:compile-toplevel :execute)
(setf (get 'foo 'frob-function)
(lambda (more-expression context)
...)))

The CLOS solution is probably a little bit more bloated because you need a
generic function you would not need otherwise. But this is IMHO nothing
critical.

Such a decision (not to use CLOS) might pay off when you may deliver your
application and then are able to rip out CLOS completely. Besides of that I
see no fundamental difference in both approaches.

ciao,
Jochen

--
http://www.dataheaven.de

Erik Naggum

unread,
Oct 16, 2002, 12:20:23 AM10/16/02
to
* Peter Seibel

| Because generic functions *can* be used on arguments that aren't
| instances of classes?

And what would those arguments be?

You see, you labor under a serious delusion: The languages you think
about is not /really/ object-oriented, but Common Lisp is. It is true
for Java and C++ and lots of other "OO" languages that give you classes
but do not give you objects all the way up that there can be objects that
are not instances of classes and the very phraseology "instance of class"
gives you away immediately. In Common Lisp, we have type hierarchies and
system classes, such that for any object, (typep <object> t) is trivially
true and the function `class-of´ is defined on all objects. (Please note
that it is not a generic function.)

Less premature judgment, more observation, please.

Coby Beck

unread,
Oct 16, 2002, 12:27:24 AM10/16/02
to

"Peter Seibel" <pe...@localhost.localdomain> wrote in message
news:m3hefnu...@localhost.localdomain...

> that call out for using or not using CLOS? Are there rules of thumb
> that you use such as (and these may be totally wrong, I'm just
> guessing what they might be given my limited experience): always start
> with classes and then "downgrade" to structs for performance reasons
> or vice versa, always start with structs and then "upgrade" as you
> require specific features of classes?

I think Thomas Burdick's advice is spot-on, just use classes all the time
until you have identified it as a performance problem and you know it can be
a struct instead. Structures are way too brittle when you are still
developing your ideas.

> Or another way: given that I've spent most of my career programming in
> an an OO style, my inclination on first opening a new .lisp file is to
> to type "(defclass ...". Is that a reasonable idiomatic Lisp approach
> or is that baggage from my past that I should try to shed.

Baggage from your past!

> Apologies if my earlier post tried to get at this in a too cute, too
> eliptical, or just plain too stupid manner.

No worries...it was looking a little provocative, but thanks for clarifying
your intent.

--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")


Peter Seibel

unread,
Oct 16, 2002, 1:26:13 AM10/16/02
to
Erik Naggum <er...@naggum.no> writes:

> * Peter Seibel
> | Because generic functions *can* be used on arguments that aren't
> | instances of classes?
>
> And what would those arguments be?
>
> You see, you labor under a serious delusion: The languages you think
> about is not /really/ object-oriented, but Common Lisp is. It is true
> for Java and C++ and lots of other "OO" languages that give you classes
> but do not give you objects all the way up that there can be objects that
> are not instances of classes and the very phraseology "instance of class"
> gives you away immediately. In Common Lisp, we have type hierarchies and
> system classes, such that for any object, (typep <object> t) is trivially
> true and the function `class-of´ is defined on all objects. (Please note
> that it is not a generic function.)
>
> Less premature judgment, more observation, please.

Sorry. I think my problem in this case was a lack of verbal precision.
In the sentence you quoted, instead of "instances of classes" I should
have said (I think) "instances of classes defined with defclass" or
maybe "instances of classes that are themselves instances of
standard-class" or even "instances that were made with
'make-instance'". But that may be a distinction without a difference,
even if my terminology is now correct. It seemed meaningful to me at
the time but that could be based on my experience with other languages
that take different (and/or lesser) slices of the OO pie.

However, after another brief sojurn to the HyperSpec I reremember the
fact about Common Lisp that was in the back of my mind when I drew the
distinction--while it's true that every object has a class, isn't it
the case that not all classes are created equal. It seems to me that
from a CLOS point of view (as distinct, perhaps, from a more-general
OO view) built-in classes are somewhat less useful than
standard-classes. To wit, I can't subclass them, I can't instantiate
them with make-instance the way I do all my user-defined classes, and
I can't change-class to or from them. But you knew that--anyway,
thanks for prompting me to go look it up.

-Peter

P.S. Have no fear: I labor under no delusion that Java (the language
I, sadly, spend most of my time thinking about) is really
object-oriented all the way down. At least in Java 1.5 (coming
whenever--I hope to not be programming Java by then) they're adding
automatic boxing/unboxing of the primitive types. But that's for
another newsgroup.

--
Peter Seibel
pe...@javamonkey.com

Jochen Schmidt

unread,
Oct 16, 2002, 2:10:51 AM10/16/02
to
Peter Seibel wrote:

> However, after another brief sojurn to the HyperSpec I reremember the
> fact about Common Lisp that was in the back of my mind when I drew the
> distinction--while it's true that every object has a class, isn't it
> the case that not all classes are created equal. It seems to me that
> from a CLOS point of view (as distinct, perhaps, from a more-general
> OO view) built-in classes are somewhat less useful than
> standard-classes. To wit, I can't subclass them, I can't instantiate
> them with make-instance the way I do all my user-defined classes, and
> I can't change-class to or from them. But you knew that--anyway,
> thanks for prompting me to go look it up.

There is the notion of a "System Class" in Common Lisp. A System Class may
be of type builtin-class in any conforming CL implementation and therefore
may be forbidden to inherit from.

As far as I understand it this means that it is allowed for a conforming
Implementation to support subclassing of those classes specified as "System
Class" for example:

- package
- stream
- symbol
- number
- character
- array
- string
- sequence
- hash-table
...

This fact actually seems to be used extensively if you take a look at the
different CL implementations. Particularily the system classes stream,
character and string got extended in various ways. One example is the "Gray
Streams" facility which allows writing user-defined stream-classes. This
was done by subclassing stream with a class called fundamental-stream which
is then the superclass of the userdefined streams.

One could imagine something similar for system classes like sequence and
hash-table. The problem is that one has to provide a protocol in which all
functions using sequences or hash-table could get implemented. This would
be probably alot of work. In the case of hash-tables there are other
opportunities to make them extensible and some vendors already use them.

Bruce Hoult

unread,
Oct 16, 2002, 2:26:47 AM10/16/02
to
In article <aoivvi$i1v$01$1...@news.t-online.com>,
Jochen Schmidt <j...@dataheaven.de> wrote:

Dylan does exactly this sort of thing. There are high level collection,
stream, number classes that can be subclassed by the user, and lower
level ones that can't be subclassed, from which the built-in classes are
derived.

As you point out, there needs to be some protocol to integrate
user-defined things into system-defined things.

In the case of numbers, the various arithmetic operators (+, -, *, /
etc) are Generic Functions defined on the subclassable class <number>.

In the case of streams, there are operations such as read-element and
write-element that are Generic Functions defined on abstract streams.

In the case of sequences, there are the (compulsory)
forward-iteration-protocol() and (optional)
backward-iteration-protocol() Generic Functions which are used to
communicate between sequences and iteration/mapping functions/macros.
The user can freely define new collection types and/or new iteration
constructs that work seamlessly with the built in things.

In the case of hash tables, there is the table-protocol Generic
Function, which returns the appropriate hash function and equality
predicate for the hash table. Once again, the user can easily create
new types of hash tables which (in Gwydion Dylan at least) are exactly
as efficient as the built-in ones.

-- Bruce

Anton N. Mescheryakov

unread,
Oct 16, 2002, 2:57:46 AM10/16/02
to
Tim Bradshaw <t...@cley.com> wrote in message news:<ey3adlg...@cley.com>...

> * Peter Seibel wrote:
> > This is, I guess, a poll. I'm interested in how experienced Lisp users
> > would describe their use of CLOS:
>
> > a) Always. I never use a defun or defstruct, may God smite me down if
> > I do.
>
> > b) Sometimes. When appropriate. But sometimes it's just not worth it.
>
> > c) Never. OO is nothing but media hype. It's too bad Common Lisp had
> > to get poluted with it.
>
> Well, like all sensible people I'm in (b)[1]. Quite apart from anything
> else, using structures and CLOS is not mutually exclusive: Lisp
> actually *is* an object oriented language, unlike Java, so in Lisp
> everything is an object, including structures and integers, functions,
> strings &c. There are no bogus `not an object' things in the language
> put there in some half-witted attempt to make the language design
> `simpler' or `more efficient' while screwing everyone who ever wants
> to use the thing. Further, structures actually have single
> inheritance - in other words they provide an inheritance system as
> powerful as anything Java gives you.
> [1] Yes, if you are not in (b) you are not sensible and must be
> terminated. Please do not leave your house, the black
> helicopters will arrive shortly.

I'am somewhere inside [b,c). CL *is* OO thing from its roots. And CL
is't polluted by silly questions like, say, Smalltalk (eq Lotofcry):
how Metaclass "Class" manages to _inherit_ something from Metaclass
"Behavior", if class guts are defined in it? Actualy, rich set of
macro features makes many "design patterns" of inferior languages
irrelivant in Lisp. But, CLOS is nice chromed and overhyped piece of
synthetic shugar, so using CLOS is't big error or sign of brain damage
(like using Java;) - all CLOS functionality may be implemented in
plain vanilla Lisp, anyway. CL is good base for various embedded
languages, and CLOS is just an embedded language.
P.S.
Concerning black helicopters, this guys may recive kill ratio they
can't afford if they dare to hurt _me_!

Erik Naggum

unread,
Oct 16, 2002, 1:36:56 AM10/16/02
to
* Peter Seibel

| However I don't have any feel for when I *should* use CLOS or how to
| decide.

Notice from the Food for Thought Department: You cannot /not/ use CLOS if
you program in Common Lisp. You can choose various different ways to
deal with the type of your objects. Some approaches are consonant with
what other languages call "object-oriented" and some are not. However,
in languages that try to combine static typic and object-orientation,
which is in fact theoretically unsound, you have to inform the system of
the top-most class of every object. If you do not declare the top-most
class of an object in Common Lisp, it is always the system class named
`t´, which is the supertype of all types.

In many "OO" languages, especially those with dot notation, the methods
are owned by the class, and you cannot access a method without compiler
promises that the object it will call that method with is of a class that
owns that method. This is not object-orientation, it is just plain lunacy.

There are two real approaches to object-orientation. The first is known
as message-passing. You send an object a message and ask it to deal with
it. (This would not work with many people in this newsgroup.) The
meaning of the message is local to the object, which inherits it from the
class of which it is an instance, which may inherit it from superclasses
of that class. In other words, you have no idea what happens when you
send a message to an object, how many arguments it needs to be happy or
anything. (Much like many people in this newsgroup.) This can get very
messy, and it is therefore deemed appropriate to "clean up" this mess by
adding compiler checks that that message on that object really takes that
argument list. This is the core mistake in the message-passing model.
Smalltalk did not make this core mistake, which means that people from
the non-OO-"OO"-language camps get all uneasy about Smalltalk.

The second approach is generic functions. A generic function has one
definition of its semantics, its argument list, and is only /specialized/
on particular types of arguments. It can be specialized on any argument
or any number of arguments in the argument list, on any type each. If
you think you do not specialize on an argument type, you have specialized
on type `t´. This fact comes into play when you want to find the most
specialized method to call given a particular argument list and which
method to call if it invokes `call-next-method´, etc. You do not get to
make the class implement methods on generic functions. There is no way
to add "method" definitions to a `defclass´. There are, however, means
to specify methods directly in the `defgeneric´. Now, please note that
`defgeneric´ defines a function that gets called with your arguments and
then dispatches on those arguments to the methods. The methods are /not/
called directly, and neither are they actually defined directly. If you
do not define a generic function, one will be appointed to you. Anything
you say can and will be used against you, too, as you cannot do a lot of
interesting things on the default generic function defined by `defmethod´,
such as method combinations or funky argument lists. You can only
redefine a generic function with `defgeneric´, a fact that you should note
right now before it comes back to bite you if you think you can load a
file of updated `defmethod´ forms and expect it to run. Documentation
goes with the generic function, too. See `defgeneric´ in the standard.

What does this mean with respect to when you think you use OO? In the
message-passing paradigm, you need to define a class to get any methods
at all and you have to use those methods on instances of that class. In
the generic function paradigm, you can define generic functions without
ever defining any classes. This tends to blow "OO" people's mind.

| I'd be interested to hear from experienced Lisp programmers about how
| *you* decide.

The decision is often between a `typecase´ and a generic function and
should in my view be made on pragmatic grounds. There is no concept of
the Right Class Hierarchy in Common Lisp. There is nothing wrong with a
regular function that does its own complex type dispatch. The language
does not become any less "OO" because of that. The point with object-
orientation of the /real/ persuasion is that objects have identity and
with that identity comes state, but even that state information does not
need to be "in" the object. In primitive languages that pretend to be
"OO", a "class" is not only a type identifier useful for type dispatch
any way it seems convenient, it conflates the issues of generic functions
/and/ data container. That is just plain lunacy.

In CLOS, if your class has distributed compoents, you can maintain a
mapping outside the instance. Slot access is via generic functions, too,
so you can make a decision whether to compute or cache a value. If you
redefine a class to cache a value, you can decide whether to pre-compute
the values or wait until it is actually used. There are lots of things
here that are just plain /convenient/ and that is where CLOS comes in.
If you need the convenience, you use it. OO in Common Lisp is not a
religious good/evil issue, it is a pragmatic issue. To people who think
in good/evil terms, this is evil, as the good of OO can only be obtained
through force and restrictions. CLOS offers freedom. It can be very
hard to deal with freedom if what you were looking for in an OO system
was a way to cage yourself in.

| Are there rules of thumb that you use such as (and these may be totally
| wrong, I'm just guessing what they might be given my limited experience):
| always start with classes and then "downgrade" to structs for performance
| reasons or vice versa, always start with structs and then "upgrade" as
| you require specific features of classes?

It is not classes that experienced Common Lisp programmers start with.
It is the generic functions.

| Or another way: given that I've spent most of my career programming in an
| an OO style, my inclination on first opening a new .lisp file is to to
| type "(defclass ...". Is that a reasonable idiomatic Lisp approach or is
| that baggage from my past that I should try to shed.

Shed it. Also the idea that you were programming in an OO style. There
is no such thing. You were only programming a particular object system.
Now you get to program a different object system.

Takehiko Abe

unread,
Oct 16, 2002, 4:45:48 AM10/16/02
to
In article <m3hefnu...@localhost.localdomain>, Peter Seibel <pe...@localhost.localdomain> wrote:

> [...] However I don't have any feel for when I *should* use


> CLOS or how to decide.

FYI, CLOS is not mentioned in Hyperspec except in 1.1.2 History
section as far as I can see.

Tim Bradshaw

unread,
Oct 16, 2002, 4:46:37 AM10/16/02
to
* Peter Seibel wrote:
> t...@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> I'm sorry--why is that false? Because generic functions *can* be used
> on arguments that aren't instances of classes? And defun-defined
> functions can?

Generic functions can't be used on things which aren't instances of
classes. Because *everything* in CL is an instance of a class.

--tim

Thomas F. Burdick

unread,
Oct 16, 2002, 2:17:53 PM10/16/02
to
Peter Seibel <pe...@localhost.localdomain> writes:

> t...@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
>
> > Peter Seibel <pe...@localhost.localdomain> writes:
> >
> > > This is, I guess, a poll. I'm interested in how experienced Lisp users
> > > would describe their use of CLOS:
> > >
> > > a) Always. I never use a defun or defstruct, may God smite me down if
> > > I do.
> >
> > This doesn't make sense. In particular, the relation
> > function:generic-function::structure-object:standard-object is
> > false.
>
> I'm sorry--why is that false?

Well, the big reason is that you need to get your structure-class
definitions right the first time. I redefine ordinary functions all
the time, dozens and dozens of times a day, as I do generic-functions,
and my standard-class definitions.

> Because generic functions *can* be used on arguments that aren't
> instances of classes? And defun-defined functions can?

I really don't understand what you're saying here.

> > I use defclass unless I'm finished with developing a module and I'm
> > working on performance. If you suspect something might ultimately
> > be a structure, you can stick to single-inheritance, and have no
> > problems.
>
> So that's the kind of thing I was wondering about: if I understand you
> correctly, your inclination is to start with classes and only switch
> to structs as an optimization--a kind of strength-reduction. That's a
> more a-style approach (though obviously not as extreme as I put it)
> than to start with structs and then "upgrade" to classes when the
> advantages they bring (being redefinable on the fly being one,
> multiple inheritance another, as I understand it) become useful.

Uhm, you can't really change "up" to standard-classes. Well, I
suppose you could restart your Lisp image, but I try to avoid that,
and only very occasionally rebuild the system from source to make sure
it's still bootstrappable.

> What makes the OO code "wrong" and the p-list solution the correct
> tool in this case? It's not obvious to me--I could imagine that one
> requires more or fewer lines of code? (Though presumably the p-list
> based implementation would require some code not shown here to hang
> the 'frob-function properties off of foo, bar, and baz, right?) Or the
> efficiency of dispatch might differ? Or something else?

The CLOS code is going through all the syntactic gyrations of OO code,
but it's not really OO. It's not actually dispatching on types, and
it's less clear what it's doing and why, because it's going through
the whole generic-function dispatching system -- just to dispatch on
the pointer-value of the symbol. Maybe you have to have seen a larger
example of this in both styles to really appreciate how ugly the CLOS
approach gets.

Thomas F. Burdick

unread,
Oct 16, 2002, 2:29:55 PM10/16/02
to
Erik Naggum <er...@naggum.no> writes:

> It is not classes that experienced Common Lisp programmers start with.
> It is the generic functions.

Usually. The project that I'm working on right now involves a lot of
modules operating on the same graph. There is one file that defines
the classes of the objects that make up the graph, and all the rest of
the system is conceptually lined up around these class definitions.
The project as a whole started with class definitions -- ie, deciding
what the graph would look like. Each module begins with generic
functions.

Frank A. Adrian

unread,
Oct 16, 2002, 10:40:28 PM10/16/02
to
Peter Seibel wrote:

> Or another way: given that I've spent most of my career programming in
> an an OO style, my inclination on first opening a new .lisp file is to
> to type "(defclass ...". Is that a reasonable idiomatic Lisp approach
> or is that baggage from my past that I should try to shed.

It is baggage. Use classes when other alternatives would lead to more
complicated code. For example, if something is to be used as a mix-in or
as an object whose code should be shared by inheritance (except when
possible to use the :includes keyord on a defstruct), then use a class. If
you are thinking that you'll have cause to change the structure of the
object (e.g., add fields, etc.), the MOP gives some very nice
infrastructure for this type of thing. If the code has a lot of
duplication when modeling the data as a sequence having proper accessors
defined or as a structure, go ahead and pull out defclass.

As a pragmatic factor, the Lisp code you write with defstruct or a sequence
will be portable to more Lisps. Many older Lisps do not have a full CLOS
implementation, but do have structures (or you can write the code to
implement them easily enough :-) and lists.

That being said, I've broken this rule and used a class more than a few
times when I could have used a struct or list (it's a bad habit from my
training as a Smalltalk programmer :-). The good news is that I eventually
added functions that needed the use of defclass in these cases. So it
goes...

In any case, most commercial Lisps have efficient enough CLOS
implementations that it shouldn't make a huge performance difference except
in inner loops. So it's mainly a matter of taste. I tend to like simpler
constructs and if you don't need inheritance, structs are simpler than
classes.

faa

Peter Seibel

unread,
Oct 16, 2002, 11:37:27 PM10/16/02
to
Erik Naggum <er...@naggum.no> writes:

> * Peter Seibel
> | However I don't have any feel for when I *should* use CLOS or how to
> | decide.
>
> Notice from the Food for Thought Department: You cannot /not/ use
> CLOS if you program in Common Lisp.

Again, I was insufficiently precise. I should have said, "I don't have
any feel for when I *should* use certain features of Common Lisp,
notably defgeneric, defmethod, and defclass, which in my understanding
are the primary features of CLOS." But your point is well taken--CLOS
wasn't bolted on; the fact that 'class-of' works for any object in
Lisp is an example of how deeply the features of CLOS were integrated
with the rest of the language. And my understanding of what the
"primary" features of CLOS are is also bound to be flawed.

> To people who think in good/evil terms, this is evil, as the good
> of OO can only be obtained through force and restrictions. CLOS
> offers freedom. It can be very hard to deal with freedom if what
> you were looking for in an OO system was a way to cage yourself
> in.

It is indeed strange to me that object orientation and bondange-and-
discipline languages are so often conflated when what I consider the
two of the best OO languages (if one allows that there is such a
thing) are Smalltalk and Common Lisp, both of which are
peace-and-freedom languages. I don't know a ton about Simula but I
don't believe it was particularly b-n-d. And wasn't Pascal the
original b-n-d language? It wasn't OO. Anyway, that's probably for
another newsgroup.

> It is not classes that experienced Common Lisp programmers start
> with. It is the generic functions.

Is it common to write a program with lots of generic functions, all of
whose methods are specialized only on arguments of pre-existing types?
I.e. to never use defclass. Or is it just that you *start* with
generic functions and then defclass the classes that you end up
needing?

> | Or another way: given that I've spent most of my career
> | programming in an an OO style, my inclination on first opening a
> | new .lisp file is to to type "(defclass ...". Is that a
> | reasonable idiomatic Lisp approach or is that baggage from my past
> | that I should try to shed.
>
> Shed it. Also the idea that you were programming in an OO style.
> There is no such thing. You were only programming a particular
> object system. Now you get to program a different object system.

And there I was trying to avoid the phrase "OO language" on the,
there's-no-such-thing grounds. ;-) Anyway, thanks for your thoughts.

Erik Naggum

unread,
Oct 16, 2002, 11:59:01 PM10/16/02
to
* Peter Seibel

| Or is it just that you *start* with generic functions and then defclass
| the classes that you end up needing?

In my view and experience, the best OO design focuses on what you want to
do, and the classes fall naturally out of the design. If you start with
the classes, the apparently strong desire to design the intrinsically
perfect class hierarchy can ruin the entire project. Encapsulation and
methods are two orthogonal dimensions that are extremely hard to get
right at the same time before you have some experience with how the
design works. That CLOS does not force you to encapsulate the methods
with the types is a major aspect to my ability to "think design" directly
in CLOS. After the design has been worked out in this way, it can be
documented and discussed and revised. Some would call this prototyping,
but that appears to me to be an unhealthy state of mind, the intent to
throw it away is too clear.

Eli Barzilay

unread,
Oct 17, 2002, 1:45:55 AM10/17/02
to
Peter Seibel <pe...@localhost.localdomain> writes:

> But your point is well taken--CLOS wasn't bolted on; the fact that
> 'class-of' works for any object in Lisp is an example of how deeply
> the features of CLOS were integrated with the rest of the
> language.

This is actually quite simple to do in an add-on CLOS.

--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!

Kenny Tilton

unread,
Oct 17, 2002, 2:29:28 AM10/17/02
to

Peter Seibel wrote in message ...
>Is it common to write a program with lots of generic functions, all of
>whose methods are specialized only on arguments of pre-existing types?
>I.e. to never use defclass. Or is it just that you *start* with
>generic functions and then defclass the classes that you end up
>needing?

At a higher level, Lisp offers so much that folks with vastly different
internal languages can still express themselves successfully. Suggest you
just go bang out some code and fuggedabout bizarre metrics like how often
one syntax is used vs another.

k,c


Christopher C. Stacy

unread,
Oct 17, 2002, 2:38:28 AM10/17/02
to
>>>>> On Wed, 16 Oct 2002 19:40:28 -0700, Frank A Adrian ("Frank") writes:
Frank> As a pragmatic factor, the Lisp code you write with defstruct
Frank> or a sequence will be portable to more Lisps. Many older
Frank> Lisps do not have a full CLOS implementation, but do have
Frank> structures (or you can write the code to implement them easily
Frank> enough :-) and lists.

Just to emphasize what Frank is saying above: if you are trying to
write your code so that it runs not only in ANSI Common Lisp,
but also in some other similar Lisp language that pre-dates
the standard, then you may be wise to avoid using CLOS.

To diverge from his line...

I don't know why anyone would want to do such a thing, though,
since there are plenty of compliant ANSI Common Lisps available
(commercially, with or without charge, "free", etc.)

Frank> In any case, most commercial Lisps have efficient enough CLOS
Frank> implementations that it shouldn't make a huge performance
Frank> difference except in inner loops.

CLOS is part and parcel of Common Lisp, not some adjunct facility.
You don't have to use the features of CLOS in order to write good
Lisp programs, just as you are not required to use LOOP, or rational
numbers. You could also write perfectly good programs using DEFSTRUCT,
although there is no true guarantee that it will be any more efficient
than if you had used DEFCLASS instead.

If CLOS is not efficient enough in the ANSI Common Lisp system that
you are using, then that's probably a performance bug, and you should
certainly contact your vendor and tell them that you would like them
to fix it. (Or choose a different vendor that already has done so.)

"CLOS" should be "efficient" in "inner loops", too.
(Whatever exactly that's supposed to mean!)
In particular, function calling, method invocation,
and slot access, should all be reasonably efficient

In almost all cases, it would be a terrible mistake to try and
optimize your code for efficiency by avoiding CLOS.
That's unlikely to be why your code is slow.
Develop your program first, and if there is a problem,
use the metering tools to find out what the real bottleneck is.
Then go back and optimize what's needed. This way of working
will not be painful, unlike in other programming languages.

When I first started using Lisp, DEFSTRUCT had not been invented.
We had to carry our data in conses and hunks. In the snow.
And when Flavors (a predecessor of CLOS) came out, I continued to
use DEFSTRUCT a lot, if I didn't need to define methods on the data.
But these days, I just always use DEFCLASS.

In the very unlikely event that a DEFSTRUCT would be more efficient
(in any significantly meaningful sense) my program, I can go back and
change it. But I can't think of any time in many years when I've ever
had to go back and change DEFCLASS to DEFSTRUCT.

What is much more likely, is that if I write DEFSTRUCT, pretty soon
I will want to write some methods on it, and so I'll have to go back
and change it all around to DEFCLASS and DEFMETHOD.

Also, I used to write more DEFVAR (global variables), and nowadays I
tend to bag those up into an object that represents the application.
I think I started thinking this way when I was writing network protocols,
and wanted more than one instance of the protocol stack running at once.
Also, CLIM (the Lisp user interface management library) encourages you
to think like that: your running application is an instance.

I also use the non-CLOS features (in particular, DEFUN) a lot.
Mostly for the more general-purpose subroutines, and also for top-level
functions when there isn't an instance of anything interesting yet.

Chris

Bruce Hoult

unread,
Oct 17, 2002, 3:02:16 AM10/17/02
to
In article <32438159...@naggum.no>, Erik Naggum <er...@naggum.no>
wrote:

> * Peter Seibel
> | Or is it just that you *start* with generic functions and then defclass
> | the classes that you end up needing?
>
> In my view and experience, the best OO design focuses on what you want to
> do, and the classes fall naturally out of the design. If you start with
> the classes, the apparently strong desire to design the intrinsically
> perfect class hierarchy can ruin the entire project.

I find that in Dylan I actually make very little use of inheritance to
form class hierarchies -- I probably use type-union as much as or more
than superclasses.

Not only do CLOS and related systems allow you to add methods to
pre-existing classes, they allow you to form pre-existing classes into
new groupings. This is something *totally* foreign to C++ users.

-- Bruce

Hannah Schroeter

unread,
Oct 23, 2002, 7:48:43 AM10/23/02
to
Hello!

Peter Seibel <pe...@localhost.localdomain> wrote:
>[...]

>Or another way: given that I've spent most of my career programming in


>an an OO style, my inclination on first opening a new .lisp file is to
>to type "(defclass ...". Is that a reasonable idiomatic Lisp approach
>or is that baggage from my past that I should try to shed.

Of course, most of your .lisp files will start with

(in-package "MYPACKAGE")

Perhaps you have a separate file defining that package, which is a
dependency for everything else in the system definition, and that
will probably start differently (something like defpackage).

For your point: You should get rid of the thinking ingrained from
Java or C++, where classes serve many different purposes (holding
data, encapsulation, containing functionality). In Lisp, a class
holds data and defines subtype relationships usable for dispatch and
explicit type tests. Encapsulation can be done using uninterned symbols,
the package system, or closures, at least. Functionality is in functions
and GFs.

>[...]

Kind regards,

Hannah.

Joe Marshall

unread,
Oct 23, 2002, 9:01:22 AM10/23/02
to
han...@schlund.de (Hannah Schroeter) writes:

> Of course, most of your .lisp files will start with
>
> (in-package "MYPACKAGE")

or (cl:in-package "MYPACKAGE") if you are paranoid and/or pedantic.

Nils Goesche

unread,
Oct 23, 2002, 5:29:23 PM10/23/02
to
Joe Marshall <j...@ccs.neu.edu> writes:

Or maybe (CL:IN-PACKAGE "MYPACKAGE") if you are really
super-paranoid? ;-) Seriously, I think if people parse my code
files with reader or package settings that break even
(in-package "MYPACKAGE"), they deserve whatever they get, or am I
missing something?

Regards,
--
Nils Goesche
Ask not for whom the <CONTROL-G> tolls.

PGP key ID #xD26EF2A0

Tim Bradshaw

unread,
Oct 24, 2002, 8:52:08 AM10/24/02
to
* Nils Goesche wrote:

> Or maybe (CL:IN-PACKAGE "MYPACKAGE") if you are really
> super-paranoid? ;-) Seriously, I think if people parse my code
> files with reader or package settings that break even
> (in-package "MYPACKAGE"), they deserve whatever they get, or am I
> missing something?

I'd use CL:IN-PACKAGE, *if* more than about 10% of the lisp-aware
editors out there would actually realise that this meant the same
thing as IN-PACKAGE. I'd like to encourage people implementing lisp
editor modes to fix them so they do recognise these as the same. I do
occasionally say (load x) in a universe where it's not clear that
IN-PACKAGE alone is enough.

--tim

Joe Marshall

unread,
Oct 24, 2002, 10:05:15 AM10/24/02
to
Nils Goesche <n...@cartan.de> writes:

> Joe Marshall <j...@ccs.neu.edu> writes:
>
> > han...@schlund.de (Hannah Schroeter) writes:
> >
> > > Of course, most of your .lisp files will start with
> > >
> > > (in-package "MYPACKAGE")
> >
> > or (cl:in-package "MYPACKAGE") if you are paranoid and/or pedantic.
>
> Or maybe (CL:IN-PACKAGE "MYPACKAGE") if you are really
> super-paranoid? ;-) Seriously, I think if people parse my code
> files with reader or package settings that break even
> (in-package "MYPACKAGE"), they deserve whatever they get, or am I
> missing something?

I only add the package prefix when I'm being pedantic on usenet. I
leave it off in code.

Kalle Olavi Niemitalo

unread,
Oct 26, 2002, 7:36:55 AM10/26/02
to
Nils Goesche <n...@cartan.de> writes:

> Or maybe (CL:IN-PACKAGE "MYPACKAGE") if you are really
> super-paranoid? ;-)

If you worry about READTABLE-CASE, don't forget :INVERT.
(|CL|:|IN-PACKAGE| "MYPACKAGE") perhaps?

Reply all
Reply to author
Forward
0 new messages