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.
>>>>> 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.
* 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.
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.
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)
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'.
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 mich...@bcect.com
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.
> 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)
> 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.
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?
Erik Naggum <e...@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.
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.
>> 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)
>> 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.
> 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?
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
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.
* 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.
-- 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.
> 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.
Erik Naggum <e...@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 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.
> 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.
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.
Tim Bradshaw <t...@cley.com> wrote in message <news:ey3adlg5bja.fsf@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_!
* 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.
-- 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.
In article <m3hefnuksq....@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.
* 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.
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.
-- /|_ .-----------------------. ,' .\ / | No to Imperialist war | ,--' _,' | Wage class war! | / / `-----------------------' ( -. | | ) | (`-. '--.) `. )----'
Erik Naggum <e...@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.
-- /|_ .-----------------------. ,' .\ / | No to Imperialist war | ,--' _,' | Wage class war! | / / `-----------------------' ( -. | | ) | (`-. '--.) `. )----'
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.
Erik Naggum <e...@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.
* 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.
-- 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.