Re: To CLOS or not to CLOS?
Pascal Costanza <[email protected]>
| Newsgroups | gmane.comp.lang.lightweight |
|---|---|
| Message-ID | <[email protected]> |
On 20 Feb 2007, at 05:16, [email protected] wrote: >> On Sun, 2007-02-18 at 18:22 -0600, Paul F. Dietz wrote: >> Over the past couple years, I have really learned to appreciate >> CLOS... > > Paul Graham mentioned at one point that after years of LISP > programming he never *once* used CLOS to get the job done. Perhaps > because LISP's pre-OO parts are so powerful you can do a lot already > without resorting to objects. If I understand Paul Graham correctly, he doesn't like OOP in general, so his criticism is not specifically about CLOS, but he uses CLOS more as an example to make a few points. > But obviously CLOS has it's fans as > well. If you are a Common LISP user, what do you like or not like > about CLOS? For example, is the generic function approach the best > way to do OO, or does it have some drawbacks compared to the common > object-dispatched system? CLOS is pretty much the most complete object system you can get. There are only a few things that come to my mind that CLOS doesn't do well: - It doesn't provide strong encapsulation: Methods are defined outside classes (to make multiple dispatch work and, more specifically, to integrate method dispatch better with functional programming). So if a method needs access to some state that an object provides, you need to provide that state to that method. (I don't think that encapsulation belongs in an object system. Encapsulation should rather be part of a module/package system, and then there is no real issue here.) - CLOS doesn't provide straightforward means to scope classes, generic functions and methods to lexical environments. This would provide functionality somewhat similar to classboxes. This is no principle issue, though, it's just not implemented. - With CLOS, it's not straightforward to implement object wrappers, i.e., methods/functions that can intercept all messages sent to specific objects (for example like the "message not understood" machinery in Smalltalk). (However, I don't think that's a strong limitation. All object wrappers that I have seen filter out the messages that they are actually interested in and leave out others.) - CLOS doesn't provide strong static type checking. But that's a topic of its own... ;-) The reasons why I like CLOS are essentially this: - CLOS provides a very smooth integration with functional programming. There is no syntactical difference between a function call and a generic function call. From a client's perspective, there is even no semantical difference between function and generic functions - you just invoke them. You can also easily pass around generic functions as first-class values, just like any other function. This smooth integration is very interesting for bottom-up software evolution: You can start out your code with plain functions, grow it to a certain level, and then decide to switch to generic functions / OOP without breaking any client. - CLOS's combination of class specializers, eql specializers (methods defined on single objects), and method combinations (like :before/:after/:around methods) provides a very powerful way to express partial behavior of a generic function. You can get pretty close to a very declarative programming style this way. - CLOS's class system provides, among other things, multiple inheritance and the ability to redefine class hierarchies at development time / runtime. Such redefinitions have an effect on existing instances and generic functions, so this allows for very straightforward ways to evolve your system while it's running. For example, you can easily add mixins to existing classes / objects without interrupting a running program. - CLOS is highly configurable. Almost each aspect of CLOS can be changed by providing user-defined method combinations, and more specifically, user-defined metaclasses for classes, generic functions, slots and methods. Since CLOS already provides a rather complete coverage of possible functionality, it's relatively straightforward to carve out your own domain-specific object systems with adaptations that you need for your specific use cases. - On top of that, if you design your metaclasses in the right way, your own adaptations of the object system can take advantage of the fact that CLOS implementations themselves are highly efficient. That's probably one of strongest advantages over building your own domain-specific object system from scratch: Implementing an OOP language in, say, Scheme, plain Lisp, or some other non-OOP language, is indeed quite easy - but doing this such that the resulting system yields good performance characteristics is very hard. (For example, think of all the optimizations advanced OOP implementations do for you, like in the JVM, Self or Strongtalk - you don't want to put such effort into your own object systems.) With CLOS metaclasses, you basically get the same advantage here that OOP gives you at the base level: You can more easily reuse what's already there, including its characteristics wrt functionality, performance, etc. These are just the reasons why I like CLOS. For another overview from a slightly different perspective, see http://www.lisp.org/table/ objects.htm For a rationale of CLOS, see the excellent paper "CLOS in Context: The Shape of the Design Space" at http://www.dreamsongs.com/CLOS.html Pascal -- Pascal Costanza, mailto:[email protected], http://p-cos.net Vrije Universiteit Brussel, Programming Technology Lab Pleinlaan 2, B-1050 Brussel, Belgium