To Be Continued . . .
I’ve covered the basics—and a bit beyond—of generic functions, the verbs of Common Lisp’s object system. In the next chapter I’ll show you how to define your own classes.
1The language now generally considered the first object-oriented language, Simula, was invented in the early 1960s, only a few years after McCarthy’s first Lisp. However, object orientation didn’t really take off until the 1980s when the first widely available version of Smalltalk was released, followed by the release of C++ a few years later. Smalltalk took quite a bit of inspiration from Lisp and combined it with ideas from Simula to produce a dynamic object-oriented language, while C++ combined Simula with C, another fairly static language, to yield a static object-oriented language. This early split has led to much confusion in the definition of object orientation. Folks who come from the C++ tradition tend to consider certain aspects of C++, such as strict data encapsulation, to be key characteristics of object orientation. Folks from the Smalltalk tradition, however, consider many features of C++ to be just that, features of C++, and not core to object orientation. Indeed, Alan Kay, the inventor of Smalltalk, is reported to have said, “I invented the term object oriented, and I can tell you that C++ wasn’t what I had in mind.”
2There are those who reject the notion that Common Lisp is in fact object oriented at all. In particular, folks who consider strict data encapsulation a key characteristic of object orientation—usually advocates of relatively static languages such as C++, Eiffel, or Java—don’t consider Common Lisp to be properly object oriented. Of course, by that definition, Smalltalk, arguably one of the original and purest object-oriented languages, isn’t object oriented either. On the other hand, folks who consider message passing to be the key to object orientation will also not be happy with the claim that Common Lisp is object oriented since Common Lisp’s generic function orientation provides degrees of freedom not offered by pure message passing.
3Prototype-based languages are the other style of object-oriented language. In these languages, JavaScript being perhaps the most famous example, objects are created by cloning a prototypical object. The clone can then be modified and used as a prototype for other objects.
4**T**
the constant value and **T**
the class have no particular relationship except they happen to have the same name. **T**
the value is a direct instance of the class **SYMBOL**
and only indirectly an instance of **T**
the class.
5Here, as elsewhere, object means any Lisp datum—Common Lisp doesn’t distinguish, as some languages do, between objects and “primitive” data types; all data in Common Lisp are objects, and every object is an instance of a class.
6Technically you could skip the **DEFGENERIC**
altogether—if you define a method with **DEFMETHOD**
and no such generic function has been defined, one is automatically created. But it’s good form to define generic functions explicitly, if only because it gives you a good place to document the intended behavior.
7A method can “accept” **&key**
and **&rest**
arguments defined in its generic function by having a **&rest**
parameter, by having the same **&key**
parameters, or by specifying **&allow-other-keys**
along with **&key**
. A method can also specify **&key**
parameters not found in the generic function’s parameter list—when the generic function is called, any **&key**
parameter specified by the generic function or any applicable method will be accepted.
8**CALL-NEXT-METHOD**
is roughly analogous to invoking a method on super
in Java or using an explicitly class-qualified method or function name in Python or C++.
9While building the effective method sounds time-consuming, quite a bit of the effort in developing fast Common Lisp implementations has gone into making it efficient. One strategy is to cache the effective method so future calls with the same argument types will be able to proceed directly.
10Actually, the order in which specializers are compared is customizable via the **DEFGENERIC**
option :argument-precedence-order
, though that option is rarely used.
11In languages without multimethods, you must write dispatching code yourself to implement behavior that depends on the class of more than one object. The purpose of the popular Visitor design pattern is to structure a series of singly dispatched method calls so as to provide multiple dispatch. However, it requires one set of classes to know about the other. The Visitor pattern also quickly bogs down in a combinatorial explosion of dispatching methods if it’s used to dispatch on more than two objects.