You probably spend all day dealing with OO ideas like method dispatch and inheritance, but to see them laid out explicitly in CLOS, as distinct from the underlying Lisp language, really helps you to internalize them and how arbitrary and malleable they are, as well as how astoundingly general they are when interpreted as broadly as CLOS does (the object systems in workaday languages cut off a lot of the avenues open to systems defined in terms of CLOS).
You can get a similar "aha" moment by looking at how Perl does OO if you already know Perl, or reading the implementations of one of its dozens of different user-implemented object systems. Perl has some language-level support for OO but it is very minimal so these object systems have to do most of the work that a Common Lisp object system would.
I think the main benefit is in seeing OO implemented in a language with no built-in support for it, rather than a language built around the concept of classes and inheritance or some version of it.
Interestingly enough, most OO systems for lisp older that CLOS were much more smalltalk like in this sense, so there were several people that found the non-namespaced approach to methods to be superior.
I'm honestly not yet convinced they are superior, but they are clearly more generic, in that it is trivial to implement single-dispatch message-passing style methods in CLOS, but much harder to do the reverse (several patterns in the GoF book exist just to emulate multiple dispatch).
That said, I used to be fascinated with CLOS, and yet these days I write in Clojure and have no more than a few multimethods in my entire code base. OO has many drawbacks and I find I can write better (simpler, more understandable and debuggable) code without it.
What are some, according to you? I mean, I have read a bit about why immutability and functional programming is becoming popular, e.g. it makes concurrent programming and use of multiple cores easier (only a general idea, may be wrong). But what are your specific reasons for saying OO has many drawbacks?
This realization came as I switched to Clojure: it found that I'd rather use the generic data structures with the excellent functions that come with the standard library. This gave me much better reusability than inheritance. Enforcing contracts can be done in a multitude of ways, and I no longer liked the idea that the only way to enforce data integrity is by keeping the data private and accessible only using "accessors".
Debugging OO code can be a major nightmare. Reading and understanding it is also difficult. How do you know which method implementation will get called at a certain point in your program? You really have no idea until you run it and dynamic dispatch happens. This is also a problem with Clojure multimethods, although it is somewhat mitigated by Clojure's explicit dispatch fn.
As for data modeling, I found that inheritance isn't such a useful tool in practice. It looks great in theoretical problems, but in practice the perfect "is a kind-of" relationship is rare.
The final nail in the coffin was Clojure's STM. Immutable data structures bring so many benefits that after using them for a while it is unthinkable to me right now to go back to mutating data in-place.
>As for data modeling, I found that inheritance isn't such a useful tool in practice.
True. It can lock you into a particular style and hierarchy. Plus, inheritance just to inherit the methods of the superclass seems like overkill or sometimes might be a wrong approach. Personally I think composition can often be more flexible than inheritance.