Subject: Re: How much use of CLOS? From: Erik Naggum <erik@naggum.no> Date: 16 Oct 2002 05:36:56 +0000 Newsgroups: comp.lang.lisp Message-ID: <3243735416407529@naggum.no> * 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.