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.