Generic Functions and Classes
The fundamental idea of object orientation is that a powerful way to organize a program is to define data types and then associate operations with those data types. In particular, you want to be able to invoke an operation and have the exact behavior determined by the type of the object or objects on which the operation was invoked. The classic example used, seemingly by all introductions to object orientation, is an operation draw
that can be applied to objects representing various geometric shapes. Different implementations of the draw
operation can be provided for drawing circles, triangles, and squares, and a call to draw
will actually result in drawing a circle, triangle, or square, depending on the type of the object to which the draw
operation is applied. The different implementations of draw
are defined separately, and new versions can be defined that draw other shapes without having to change the code of either the caller or any of the other draw
implementations. This feature of object orientation goes by the fancy Greek name polymorphism, meaning “many forms,” because a single conceptual operation, such as drawing an object, can take many different concrete forms.
Common Lisp, like most object-oriented languages today, is class-based; all objects are instances of a particular class.3 The class of an object determines its representation—built-in classes such as **NUMBER**
and **STRING**
have opaque representations accessible only via the standard functions for manipulating those types, while instances of user-defined classes, as you’ll see in the next chapter, consist of named parts called slots.
Classes are arranged in a hierarchy, a taxonomy for all objects. A class can be defined as a subclass of other classes, called its superclasses. A class inherits part of its definition from its superclasses and instances of a class are also considered instances of the superclasses. In Common Lisp, the hierarchy of classes has a single root, the class **T**
, which is a direct or indirect superclass of every other class. Thus, every datum in Common Lisp is an instance of **T**
.4 Common Lisp also supports multiple inheritance--a single class can have multiple direct superclasses.
Outside the Lisp family, almost all object-oriented languages follow the basic pattern established by Simula of having behavior associated with classes through methods or member functions that belong to a particular class. In these languages, a method is invoked on a particular object, and the class of that object determines what code runs. This model of method invocation is called—after the Smalltalk terminology—message passing. Conceptually, method invocation in a message-passing system starts by sending a message containing the name of the method to run and any arguments to the object on which the method is being invoked. The object then uses its class to look up the method associated with the name in the message and runs it. Because each class can have its own method for a given name, the same message, sent to different objects, can invoke different methods.
Early Lisp object systems worked in a similar way, providing a special function SEND
that could be used to send a message to a particular object. However, this wasn’t entirely satisfactory, as it made method invocations different from normal function calls. Syntactically method invocations were written like this:
(send object 'foo)
rather than like this:
(foo object)
More significantly, because methods weren’t functions, they couldn’t be passed as arguments to higher-order functions such as **MAPCAR**
; if one wanted to call a method on all the elements of a list with **MAPCAR**
, one had to write this:
(mapcar #'(lambda (object) (send object 'foo)) objects)
rather than this:
(mapcar #'foo objects)
Eventually the folks working on Lisp object systems unified methods with functions by creating a new kind of function called a generic function. In addition to solving the problems just described, generic functions opened up new possibilities for the object system, including many features that simply don’t make sense in a message-passing object system.
Generic functions are the heart of Common Lisp’s object system and the topic of the rest of this chapter. While I can’t talk about generic functions without some mention of classes, for now I’ll focus on how to define and use generic functions. In the next chapter I’ll show you how to define your own classes.