
A Brief Guide to CLOS (1998) - Tomte
http://www.aiai.ed.ac.uk/~jeff/clos-guide.html
======
tsm
I learned Common Lisp and "enough" CLOS in high school, was blown away by
CLOS's power†, and have spent the entirety of my professional career wishing
that I could have a job working on a complex system in CL that used all of
CLOS's interesting features. This has not happened (I've been 80% in Ruby-land
and 20% doing Clojure), but what's weird is that I haven't even had situations
where I really yearned for AOP, multi-methods, the MOP, etc. I can't decide if
the systems I've worked in have just been too boring (they certainly solved
the business problem though) or if Ruby's object-orientation (and library
ecosystem) is good enough that I just don't need the full power of CLOS.
(Designing Clojure programs is a totally separate kettle of fish.) I'd be
interested to hear from more seasoned Lispers about their experience with CLOS
in production.

† I took an FP class during my first semester of college. The professor wrote
the book himself, and had an extended sidebar comparing the design
considerations of OO systems (easy to add another class; tedious to reopen _n_
subclasses to add another method to each one) and FP systems (easy to add
another function dispatching on type; tedious to reopen _n_ functions and make
them dispatch on a new type). I pointed out that CLOS handled both situations
equally well, and in the final edition of the book he tossed in a footnote
saying as much and thanking me.

~~~
smnplk
>> I pointed out that CLOS handled both situations equally well, and in the
final edition of the book he tossed in a footnote saying as much and thanking
me. <<

Clojure's multimethods and protocols also solve both.

~~~
tsm
At the time I hadn't used Clojure

------
CamTin
I highly recommend playing with CLOS (or one of the Scheme flavored knockoffs
like GOOPS in Guile or Swindle in Racket) for anyone who hasn't but works in
OO languages (probably most working programmers).

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.

~~~
aidenn0
Another revelation with CLOS is methods are not namespaced to classes. Before
I used CLOS it was not obvious that methods were serving two distinct purposes
(specialization _and_ namespaces) within other OO languages.

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).

~~~
lispm
But it's more difficult to redirect all/most messages to a class/instance to
another class/instance. Especially if there is no message passing going on ...

------
gumby
I wondered if the author would mention method combinators, which he did. They
are super powerful, very useful...and a marvelous opportunity for spaghetti
code.

Actually the CLOS combinators show learning from earlier mistakes and allow
only :before, :after and :around, the most useful and the most comprehensible
combinators.

Lisp Machine Lisp (including the "zetalisp" variant dreamt up by Symbolics'
marketing department) had an enormous panoply of method combinators including
full control structures: IIRC I had cause to resort at one time or another to
:or, :and and :progn combinators (the latter being somewhat like :around, I
suppose, but not the same). They made the code extensible, compact, and
completely unpredicatable (if you didn't understand the _runtime_ method
hierarchy precisely which combined methods would be run in an :or combination?

Nevertheless I sometimes miss them to this day, even in my C++17 codebase!

~~~
lispm
Method Combinators are still there, slightly improved from Zetalisp's Flavors
and New Flavors.

The CLOS Standard Method Combination has primary, :before, :after and :around
methods.

There are in CLOS also various 'simple' method combinations like AND (runs all
applicable methods until one is false), OR, + (sums up the results of all
applicable methods), PROGN (runs all applicable methods and returns the result
of the last one). These seem to be rare in CLOS code.

> They made the code extensible, compact, and completely unpredicatable ...

That's kind of the point that one does not care which methods actually run and
when - the 'system' takes care of that. Similar to, say, when you call REDUCE
on a list - one does not know which elements are in the list at runtime. Or
when you call a generic function on a bunch of instances - you know only at
runtime which method runs - because of dynamic dispatch - the method
combination feature adds a layer of complication to it. Like on a Lisp Machine
where you can add a :before method to a window function and it's immediately
active, because the method combination is recomputed. Or when one adds a mixin
to a window class and the width of a window then has a different result,
because the + generic function computes a new value, because a new method is
available via the + method combination. You can scare a lot of people with
this, though.

One can program full new method combinations in CLOS, like in Flavors. This
might even be useful. ;-) An example is the integration of 'Design by
Contract' methodology into CLOS. One can add a DBC method combination and then
work with preconditions, postconditions and invariants somehow integrated into
Common Lisp's condition system.

[https://www.muc.de/~hoelzl/tools/dbc/dbc-
intro.html](https://www.muc.de/~hoelzl/tools/dbc/dbc-intro.html)

and newer:

[https://github.com/sellout/quid-pro-quo](https://github.com/sellout/quid-pro-
quo)

~~~
crististm
> That's kind of the point that one does not care which methods actually run
> and when

What is the right method for debugging? I've been trying to debug one
particular issue in CL code (McCLIM + Climacs) and I've hit the wall in every
direction that I tried to see which method was responsible with the issue I've
seen.

A plethora of around, before and after in both composition and inheritance
that I've lost my way. I've tried various depths of reading and runtime
debugging with no significant success. It left me with the image that I should
have the whole program in my head to be able to understand it.

It's usually the case that the solution is where you're not looking therefore
my question is where (and in what way) should I look at the code to understand
what it is supposed to do?

~~~
lispm
The debugging tools need to know about that. Usually you would look into the
stack trace and see which methods has been called and editing the method
object brings us to the source code. Best when things are NOT optimized for
low DEBUG and high SPEED.

When I was working more with those things I wrote some browsers for browsing
methods in CLIM and for MCL. The Lisp Machine should already have some tools,
IIRC. LispWorks also has a generic function browser. One enters a generic
function and argument types -> LispWorks shows which methods are used for
those.

    
    
      FOO (FLOAT STRING) ->
    
      MULTIPLE-VALUE-PROG1
        CALL-METHOD
        (METHOD FOO (FLOAT T))
          (METHOD FOO (REAL T))
        CALL-METHOD
        (METHOD FOO :AFTER (T T))

~~~
crististm
Indeed, having good runtime debug tools is absolutely necessary for debugging
dynamic objects [1].

I've come to this conclusion during this particular experience and I've
confirmed it time and again. I've recently seen an older video of Gregor
Kiczales on Aspect Oriented Programming and if I did not read in too much he
emphasizes the same issue with proper debugging tools.

Thanks for the tip; it might be what I needed for me to download LispWorks and
try again.

[1] Some kernel developers just don't get this even after you show them a call
using a function pointer. Read the code they say. Right...

~~~
TeMPOraL
Been working on getting a CLIM codebase (that is older than ANSI CL standard)
running under modern open source implementations (SBCL, CCL). This too was my
primary problem. CLIM spec was written with CLOS in mind, and implementations
tend to absolutely abuse CLOS and MOP. I spent many long hours untangling all
the mixins introducing :around methods. Good runtime debugging is absolutely
crucial for this kind of code - something which open source CL implementations
do not really have. Even prettified by Emacs, the inspector can get you only
so far. Hell, this is the only time in my life where SBCL itself crashed
completely when I did simple "jump to symbol definition" in Emacs...

------
edejong
One of the few eureka moments I’ve had was while reading The Art of the
Metaobject Protocol [1]. Some famous computer scientists regard this as the
best CS book coming out in the 90’s.

[1]
[https://en.m.wikipedia.org/wiki/The_Art_of_the_Metaobject_Pr...](https://en.m.wikipedia.org/wiki/The_Art_of_the_Metaobject_Protocol)

