The Art of the Metaobject Protocol
Gregor Kiczales, Jim des Riviéres, and Daniel G. Bobrow
The MIT Press
Cambridge, Massachusetts
London, EnglandContents
Introduction
1 THE DESIGN AND IMPLEMENTATION
OF METAOBJECT PROTOCOLS
1 How CLOS is Implemented
LL A Subset of CLOS
12 Basic Backstage St
1.3. Representing Classes
14
15
16
LT
1.8 Invoking Generic Funetions
1.9 A Word About Performance
1
2 Introspection and Analysis
24 18 Class Metaobjects
2.2 Browsing Classes
23 Browsing Generi
24
2.5 Summary
inctions
ygrammatic Creation of New Classes
3 Extending the Language
3.1 Spe Class Metaobjects
3.2. Terminology
3.3. Using Specialized Class M
34 Class Precedence Lists
35. SI
6 Other
B.7 Slot Access
1ee Protocols
1B
uu
15
Ww
26
26
34
36
40
45
a7
48
52
58
66
69i %
3.8 Instance Allocation
3.9 Summary
4 Protocol Design
4.1 A Simple Generic Function Invocation Protocol
4.2 Functional and Procedural Prot
4.3 Layered Protocols
44 Improving Performance
4.5 Protocol Design Summary
Il AMETAOBJECT PROTOCOL FOR CLOS
5 Concepts
5.2 Metaobjects
53 Inheritan
Structure of Metaobject Classes
5.4 Processing of the User Interface Macros
5.5. Subprotocols
Generic Functions and Methods
Introduction to CLOS
Solutions to Selected Exercises
Living with Circularity
boo wD p>
A Working Closette Implementation
E Cross Reference to Full MOP
References
Index
99
105
107
107
0
ug
125
131
269
aT
317
325,
327
ContentsAcknowledgments
The work described here is synthetic in nature, bringing together techniques and insights
from several branches of computer science. It has also been essentially collaborative; we
have had the pleasure to meet and work with people from am
‘The largest of these communities has been the users of
aber of comrnunities,
prototype CLOS imple-
mentation, PCL, By using and experi ith early metaobject protocols, these
peopl ile ground for the ideas behind this work to grow. Their enthu-
siasm provided energy for this work and their successes provided the insight. Everyone
who ever used the metaobject protocol facilities of PCL eontributed to this work, but
im Kempf, Andreas Paepeke
aspects of Metaobject Protocol use; their con-
helped many others learn to use the CLOS
Yasuhiko Kiuchi and Luis Rodriguez made signi
‘ant contributions to the design and maintenance of PCL, a task that. was also helped
f the Common Lisp vendors who suggested in
‘ance PCL performance. The project also benef
courage and e¢ :pt large projects with early versions of the CLOS Meta-
object Protocol: Ken Anderson, James Bennett, John Collins, Angela Dappert-Farq)
Harris, Reed Hi ick Irvine, Jim Kempf, Joshua Lubell,
Phillip McBride, Steven Nicoud, Greg Nuyens, Paepcke, Pe-
‘der, Dan Rabin, Doug, Rand, George Robertson, Larry Rowe, Richard
Shapiro and Arun Wel
Electronic mail has been essent
leserve partic
and Mike Thome were experts on dif
logue in this project. In particular, the
ity. Yasuhiko
Kiuchi maintained this list for three years, overseeing its growth from less than
hundred to almost ight-hundred readers. More recently, Arun Welch has taken this list
ng both it and the gateway to the comp. lang.clos newsgroup.
Masayuki Ida has been the primary force for establishing and nurturing a PCL and
CLOS Metaobject Protocol user community in Japan. His suecess at mediating the
cross-cultural language and stylistic differences has enabled the community to include
the Japanese users.
‘The CLOS design community has also been in ‘The people
involved in that effort, or in the larger Common Lisp standardization effort, have par-
ticipated in the development fundamental principles of metaobject protocol
design presented in Part I and in the full CLOS Metaobject Protocol presented in Part II.
‘They are: Kim Barrett, Eric Benson, Scott Cyphers, Harley Davis, Linda DeMi
Gary Drescher, Patrick Dussud, John Foderaro, Richard P. Gabriel, David Gray, Ken
Kahn, Sonya Keene, pf, Larry Masinter, David A. Moon, Andreas Paepcke,Acknowledgments
Chris Richardson, Alan Snyder, Guy Steele, Walter van Roggen, Dan Weinreb, Jon
L White and Jan Zubkoff
Work on reflect
CLOS Metaobject Protoc
n another source of inspiration guiding the design of the
and influencing its presen Smith’s 3-Lisp had shown.
lc reflective interpreter can be used to clarify the relation
ties and its implementation, by factoring out po-
ly distracting is des Riviéres, who joined the project at a
latively late stage, used his ox; +e with reflection to develop the Closette
ation and pedagogical structure on which Part I of the book is based. We would
is, Mike Dixon, Brian Foote, Nicolas Graube, John L
Matsuoka, Ramana Rao and Takuo Watanabe,
Jok has taken shape over a period of years.
Cointe, Doug Cutting, Mike Dixon, Brian Foote, Volker Haat
suhiko Kiuchi, Wilf L
lev, Masayuki Ida, Ya
hn Lamping, Stan Lanning, Yosh
n Smith, Deborah Tatar, Dave Thomas, Takuo Watanabe,
rand Peter Wegner.
here are our friends and colleagues at Xerox PARC, who have helped and
lating and enjoyable place to work. In particular,
Bob Bauer, Alan Bawden, Nora Boettcher, John Seely Brown,
leer, Mike Dixon, Mimi Gardner, David Goldsto
Stan
han Rees, George Robertson, Luis Rodrig
otal Tatar and Mark Weiser.
, Doug,
Volker Haarslev,
Larry Masinter,
Brian Smith, MarkIntroduction
Modern programming language design lives in tension between two apparently conflict-
ing deyeénds. On the one hand, high-level languages sueh as Scheme, Prolog, and ML
incorporate significant advances in elegance and expressive power. On the other hand,
‘many industrial programmers find these languages too “theoretical” or impractical for
everyday use, and too inefficient. As a result, these languages are often used only in
academic and research contexts, while the majority of the world’s mainline programming
is conducted in such languages as C and C++, distinguished instead for their efficiency
and adaptability.
This book is about a new approach to programming language design, in which these
two demands of elegance and efficiency are viewed as compatible, not conflicting. Our
goal is the development of languages that are as clean as the purest theoretical designs,
but that make no compromises on performance or control over implementatio
The way in which we have achieved elegance and efficiency jointly is to base language
design on metaobject protocols. Metaobject protocols are interfaces to the language that
xzive users the ability to incrementally modify the | 's behavior and implementa-
language.
Languages that incorporate metaob ect protocols blur the distinction between language
designer and language user. Traditionally, designers are expected to produce languages
with well-defined, fixed behaviors (or “semanties”). Users are expected to treat these
languages as immutable black-box abstractions, and to derive any needed flexibility or
“open languages up,” allowing users to adjust the design and implementation to
ir particular needs. In other words, users are encouraged to participate in the
design process. If handled properly, opening up the language design need not
or implementation efficiency.
guage implementation adjustable and
does not represent a single point in the
‘overall space of language designs, but rather an entire region within that space.
The protocols followed by this object-oriented program serve two important functions.
First, they are used by the designers to specify a distinguished point in that region,2 Introduction
correspofiding to the langus Second, they
fllow ess to create variant languages, using standard techniques of subelasing and
specialization. In this way, users can select whatever point in the region of language
designs best serves their needs
Our development of the metaobject protocol approach has emerged hand-in-hand with
over the past several years, in the design of the Common Lisp Object
our involvern
System (CLO'
designed as part
us face to face
design by gradually deriving a simplified metaobject protocol for CLOS.
Because we expect the notion of a metaobjeet protocol itself to evolve, we not only
present the approach as we jowards open issues and
a detailed and complete description of a particular metaobject
protocol we have designed for CLOS. This second CLOS metaobject protocol can be
incorporated into production CLOS implementations and used for writing produetion-
auality code.
‘The work reported in this book synthesizes a number of concerns and approaches from
different parts of the computer science field. As a result we hope it will be of interest to
a wide spectrum of the community:
© Programming language designers should benefit from our analysis of some of the prob-
lems users have with high-level languages. Some designers may be interested in adding
a metaobject protocol to existing languages or designing a new language with a meta-
object protocol
a language with a metaobject protocol does not need to be a radical
c language or implementatsone
neers should be interested in our analysis of why bigh-
languages are often inadequate, and in our suggestions for how to improve them.
if they are not working with a metaobject protocol, they may find that, the
analysis helps them conceptualize and address problems they are having with existing
languages.
«© People working with object-oriented languages will have two reasons to be interested in
this work. First, since our approach relies extensively on object-oriented techniques,
the book can simply be viewed as presenting a well-documented case-study within
the object-oriented paradigm. Particular attention is given to issues of designing and
os involved in designing seh languages
recognize that our approach also relies on the tech-
niques of procedural reflection. We hope they will see what we have done as helping
to bring reflection into wider practical use, by engineering reflective techniques to be
robust, efficient, and easy to use
© The CLOS com
share many of thei
information on ho
metaob ject protocol
being a cross-section of these other groups,
terests, Further! book provides them with specific
}0 use, implement, and continue the development of
‘The Problems We Faced
During the development of the CLOS standard, we realized that we were up against a
number of fundamental problems. The prospective CLOS user community was already
using a variety of object-oriented extensions to Lisp. They were committed to large bod-
ies of existing code, which they
with these earlier extended languages had shown that various improvements
desirable, but some of these involved incompatible changes. In fact the very notion of a
single, standardized object-oriented extension to Common Lisp was an inherently incom-
patible change, since the set of earlier extensions were not compatible among themselves.
We therefore faced a traditional dilemma: genuine needs of backward compatibility were
fandamentally at odds with important goals of an improved design.
As is often the case, the situation was particularly aggravating because of an essential
is designed and each of the older ones—the fact
-y were all based, at a deeper level, on
‘was an object-oriented programming4 Introduction
\
lasses, instances, inheritance, methods, and generic functions? which,
‘automatically determine the appropriate method to run. From the broader
perspective of the family of object-oriented programming languages, they differed only in
how they interpreted various aspects of object-oriented behavior: the syntax for calling
generic functions; the rules for handling multiple inheritance; the rules of method lookup:
etc.
Along with this first challenge, of compatibility, we faced a second one, of extensi
As well as using a small number of major existing languages, many of the prospec
CLOS users had also developed custom languages of their own. In many ways these,
too, were essentially compatible with the major languages, and with the new language
‘we wanted to design. But each had its own distinguishing characteristics, supporting a
number of additional features, or implementing variant interpretations of basic object-
oriented behavior. For examp!
for representing the structure
and method lookup mechanisms.
Furthermore, it turned ont on examination that these added functionalities and varia-
¢ object-oriented model were neither arbitrary nor superfiuous. Because
language
when call
some of these languages provided special mechanisins
tances. Others employed various special inheritance
that some prospective CLOS users would always be in a
ition. No matter what design was agreed upon, there would be times when
a given user, for entirely appropriate reasons, would need this or that variant of it. For
‘example, while CLOS was being designed, it emerged that some users were interested in
the support of persistent objects, of the sort that would be provided by an object-oriented.
database, Our challenge was to find a way to enable such users, who wanted something,
close to CLOS, to adapt it to fit their needs
‘The third problem we faced was that of ensuring that programs written in CLOS would
run efficiently.* Unfortunately, the very expressiveness of high-level languages makes thisIntroduction 5
difficult. No single implementation strategy is likely to perform wel
behaviors that user programs \ean be expected to exhibit,
rent uses of CLOS classes. In the first, which might
ions, with two slots for x
t slot access performance
h might arise in a blackboard system,
of slots, but in practice any given instance
be a very large number of these instances,
the space taken up instances would be of critical concern, making it important
not to waste space on all the unused slots.
Even though the behavior of both of these classes is well captured by the CLOS lan-
guage, they would clearly benefit from different implementation strategies. As a result,
used a single strategy would at best perform well on only one.
is further exacerbated by the fact that the information needed to choose
mentation strategy might be difficult or impossible for a compiler to ex-
tract from the program text, since it depends on dy: ehavior, such as how many
instances will be created, or how often their slots will be accessed. This is why effci
is a challenge: somehow, the context of a single language design, differen
should be given the specific implementation and performance profiles they need,
‘While these goals of compatibility, extensibility ane at first seemed different,
we eventually came to see them as instances of the same underlying problem, and were
therefore able to address them within a common structural framework,
‘The underlying problem is a lack of fit: in each case, the basic language design fails to
meet some particular need. Compat is an obvious example: as soon as the lang
is changed to incorporate a new feature, it fails to match up with existing bodies of
But the other cases have the same structure. The graphies program is well served by one
implementation of instance representation, but the sparsely populated slots case requires
another.
‘The unavoidable c
the full range of,
ion is that no single language will ever be universally appro
Rather than
‘we would instead support a region of pot lesigns within that overall
the essence of the metaobject protocol approach. In the case of CLOS,
stead of providing a single fixed language, with a single implementation6 Introduction
strategy the metaob ject rote extends a basco eit” CLOS by providing sur
rounding reton of licrases er are on lo move to whateer pune i het rg
best matches thelr parla eiements
Strategy hat to tangle be
dea! with wile range fr encerm lows
relying on the metaobject proto
ie base case—CLOS itself—to be simpler
and more elegant. The very existence of the metaobject protocol, in other words, takes
some pressure off the design of the base language, to its benefit. Second, the strategy
of supporting a CLOS region, rather than a single CLOS point, enables us to solve all
inal problems.
'y problem is solved by ensuring that the behavior of each of the earlier
languages lay within the scope of the newly supplied region. As we have already said,
ages were already tible with one other, and none was located
to delineate a coherent region of object-oriented languages
all. Users can select whichever language they prefer by adjusting default CLOS to
appropriate new point. Fu they derive some addit Since the
language or can be incrementally adjusted to any point in the region, not just to
‘one or two pre-designated posi
rts of a program can be assigned to
isers can combine code written in different versions of
hin the same program.
implementation strategy to
f a region can be ident
understand and large en
to incrementally adjust the default language behavior and implementation within that
region, then our three goals can be mi
tion preserved. The question rer
and the investment in the basic language imple
of how this can be done.
Metaobject Protocol Based Language Design
‘While designing a language—or language region—in this way departs significantly from
traditional practice, it can be done while preserving the important qualities of existing
ive techniques
ation without revealing unnecessary
compromising portability; and object-oriented techniques allow
implementation detaildel of the language's
fjusted,
Reflective tech
posed in a way ti
appropriately high level of abstraction, so that implementors retain enough freedom to
exploit idiosyncrasies of their target platforms, and so that users aren't saddled with
gratuitous (and non-portable) details. Second, that access must be effective, in the sense
stments must actually change the language behavior. ‘These two prop
exactly what is provided by a reflect
What reflection o1
cease of use,
lementation and behavior to be locally and
low the implementation to be ex-
le behavior, but a space or regiot
f behaviors—this is commonly
defining a default behavior, a single point in the region, in te
otocol this is the role of the default classes and methods; and (iii) making
Possible to effect. incremental adjustments from the default behavior to other points in
the region—this is the role of inh
cts, Because these objects represent fragments of a program, they are given the spe-
of metaobjects. Sec behavior of the language
iobject protocol. Third,
for each kind of metaobject, a default class is created, which lays down the behavior of
‘the default language in the form of methods in the protocol. In this way, metaobject
protocols, by supplementing the base language, provide control over the language's be-
havior, and therefore provide the user with y to select any point within the
region of languages around the one specified by the default classes.8 Introduetion
default, and by giving it specialized methods on those generic functions. By doing this,
the usr is making an incrdmenta adjustment inthe language. Mast aspect of both it
behavior and implementation remain unchanged, with just the instance representation
strategy being adjusted.
In this way, by combining these two techniques into an integrated protocol, we are able
to meet a number of important design criteria:
Robustness: moving the language around in the region to sut one program shouldn't
have an adverse effect on other programs or on the system as a whole;
@ Abetraction: in order to adjust the languege, the user should not have to know the
complete details of the language implementation;
Base of we: adjusting the language must be natural and straightforward, and the
ist themselves be easy to use; and
[ficiency providing the flexibility ofa surrounding region should not undermine the
performance of the default language, nor curtail the implementor’s ability to exploit
idiosyncrasies of target architectures to improve ce of the entire reg
fact we retain our gool of having programs written in a language augmented
metaobject protocol be more, not less efficient than programs written in a traditional
language)
Our conclusion is that a synthetic combination of object-oriented and reflective tech-
niques, applied under existing software engineering considerations, make possible a new
approach to programming language design, one that meets a wider set of design criteria
‘than have been met before. Doing so is the art of metaobject protocol design, the subject
of this book,
Structure of the Book
‘The remainder of the book is divided in two parts. The first part presents metaobject
protocol design. The second part gives a detailed specification of a metaobject protocol
for CLOS,
In Part I, metaobject protocol design is presented as a narrated derivation of a meta-
abject protocol for CLOS. We begin with a (simplified) CLOS sans metaobject protocol,
and gradually derive one for it. ‘The derivation is driven by examples of the kinds of
problems metaobject protocols can solve. This approach allows us to give attention not
t protocols work and are implemented, but also to the process of
and of incorporating those needs into the design of a protocol
effect, metaobject protocol design requires determining the size, shape, and dimensions
of the region to be provided. In the early stages of the derivation,Introduction 9
wie nett al prot ofeach pecs da Tn er ag nan
shifts to problems of ease of use and efficiency.
‘Throughout, we will work with actual code for a simplified implementation of CLOS,
and as we develop it, its metaobject protocol. This will give the reader an opportunity
to gain some practical experience with the evolving design. In the san
included a number of exercises, addressing important concerns and oper
courage all readers at least to read them, if not actually to work them throu
to those that can be answered with code are includ i
discursive replies, from short essays to moderate
‘The presentation throughout this first part presumes a famili
is CLOS subset and implementatio
‘age even those familiar with CLOS to read this chapter.
xt 2 begins the derivation of the metaobject protocol by |
‘users writing browsers and other program analysis tools. We
introspective protocols, which make it possible to analyze the stru
a program
Chapter 3 continues the derivation of the metaobject protocol with examples of com-
performance needs that require adjusting the default lan-
fange behavior. Thi ‘to “step in” or intercede in the behavior of the system will
be provided by a set of intercessory protocols
In Chapter 4, we continue to develop intereassory protocols. But, in this chapter, our
focus is the problem of designing protocols that are efficent and easy to use. We discuss
various protocol design considerations and techniques
Part II presents a detailed spec of a metaobject protocol for CLOS. The spec-
ification is divided into two chapters, in a manner simi
iteolf [X8I13]. Chapter 5 presents basic terminology and concepts; Chapter 6 describes
each function, generic function, and method in the protocol
Readers interested in designing metanbjec cols for other languages will find that
this part not only ill i c technical details, but also conveys additional information
about the overall nature of our design approach. For CLOS users and implementors,
however, the primary interest of Part II will be as a specification of a complete metaobject
protocol for CLOS. This protocol is not offered as a proposed standard, but as a basis
for experimentation, which may subse. lead to the development of a revis10 Introduction
standardized CLOS metaobject protocol. There is evidence that this is already underway;
many Common Lisp vendors are already implementing metaobject protocols based on
the one presented here.THE DESIGN AND IMPLEMENTATION OF METAOBJECT
PROTOCOLS1 How CLOS is Implemented
rotocol design in stages, by following the development
ue will be to
will be resolved by showing how it is handled by the newly developed portion of the
rotocol
P etaabject protocol design requires an understanding both of the language behavior
this case CLOS), and of the common architecture of that language's implementations.
‘The first task therefore, addressed in this chapter, is to present the architecture of CLOS.
smentations in the form of Closette, a simple CLOS interpreter.! Closette is the
sveryman” of CLOS implementations—despite the simplifications, it is representative
of the architecture of all CLOS implementations?
Although understanding implementation architecture is important, it is vital to distin-
the implementat language. A useful metaphor for making
n think of the documented language as
s, which we audience, only get to see this on-stage
behavior. The internal parts of the implementation are backstage: they support what
happens on-stage nce doesn't get to see them. Finally, implementors are
the producers: they get to see what happens both on and offstage, and they are the ones,
responsible for putting on the show.
In presenting Closette, we will be showing the essential structure of what can be
found backstage in any CLOS implementation. We will see how this backstage structure
supports the on-stage language behavior. This will be useful in later chapters as we design
the metaobject protox nformation waiting in
jes there are for implementors
is separation come
being on-stage. Ust
1out this part of the book, presentations are based on working code.
low readers to try the examples, work through the exercises, and try alternative
In fact, we recommend this (see Appendix D for the complete code).
be assumed that the reader is familiar with Lisp and has some fami
CLOS programming. Those with a background in other object-oriented progr:
languages, such as Smalltalk or C++, can ac4 Chapter 1
\dix A. Those who are not fa
introduction to both it and
d programming can
1.1 A Subset of CLOS
In the interests of pedagogy a
fied subset of CLOS. All the essent
hherit structure
tive) brevity, we have chosen to work with a simpli:
d to them; and methods which define the class-specific
and operations of generic functions. The major restrictions of the simplified
dialect include:
No class redefi
changes are pi
allow classes to be redefined.
No method redefinition. Full CL
definition completely replacing the
redefined. (For convenience, the work
d one. The subset does not allow methods to be
x D does support method
ions. The subset requires that a generic
y introduced with a defgeneric form before any methods are
Standard method combination only. Full CLOS provides a powerful mechanism for
"user control of method combination. The subset defines only sim
nation (primary, before, a ods).
ss methods to be specialized not on
objects. The subset restricts method sp.
ass allocation. Full CLOS supports slots allocated in each instance
of a class and slots which are shared across all of them. The subset defines only per-
instance slots.
‘Types and classes not fully integrated. Full CLOS closely integrates Common Lisp
‘and CLOS classes. It is possible to define methods specialized to primitive classesHow CLOS is Implemented
not for structure classes.
ence macros and special forms are not
flet and generic-labels.
1.2. The Basic Backstage Structures
Inits simplest terms, a CLOS program consists of defclass, defgeneric, and defmethod
forms mixed in with other more traditional Common Lisp forms. Executing these forms
defines the program's classes, generic functions and methods.
Backstage, execution of these forms creates internal represé
generic functions, and methods, recording the information provi
‘The implementation uses the information stored internal repre
to create instances of that class and to access their slots. Information
internal representation of a generic function and its methods is used to invoke the generic
function.
jons of the classes,
‘To make things concrete, consider the following example CLOS program:
(defclass rectangle (
((height :initform 0.0 :initarg :height)
(width :initform 0.0 :initarg :width)))
(defclass color-mixin ()
((cyan :initform 0 :initarg :cyan)
(magenta :initform 0 :initarg :magenta)
(yellow :initform 0 :initarg :yellow)))
(detclass color-rectangle (color-mixin rectangle)
((clearp :initforn (y-or-n-p "But is it transparent?")
Hinitarg :clearp :accessor clearp)))
(aotgeneric paint (x))
(defmethod paint ((x rectangle)) sMethod #1
(vortical-stroke (slot-value x ‘height)
(slot-value x 'width)))16 Chapter 1
(defmethod paint :before ((x color-mixin)) ;Method #2
(set-brush-color (slot-value x ‘cyan)
(elot-value x ‘magenta)
(elot-value x ‘yellow)))
(ofnethod paint ((x color-rectangle)) iMethod #3
(unless (clearp x) (call-next-method)))
(setq door
(make-instance ‘color-rectangle
iwidth 38 theight 84 :cyan 60 :yellow 55 :clearp nil))
t
standard-object
fi Method #3 Method #2 Method #1
door
— Direct-superclass/subelass
= = Genericfunction/Methods
++ Specializer/Direct-method
ass-of
Figure 1.1 A First Glimpse Backstage.
‘These definitions cause several new internal objects to be ereated and connected as
sure 1.1. They can be divided into two groups: classes and instances, and
functions and methods. In the former group, each class object is connected to its direct,
the generic function paint is connected to its methods, and conversely the methods areHow CLOS is Implemented 1
connected to their generic function. Connecting the two groups are bidirectional links
between methods and the classes they are specialized to.
Unlike instances of color-rectangle, such as the one door is bound to, which repre-
sent rectangles, the backstage objects, such as the one corresponding to the class rectan-
gle, represent elements of the program. These backstage objects are called metaobjects
because they represent the program rather than the program's domain. In fact, every-
‘thing that goes on inside the implementation is considered to be at the “meta” level
‘with respect to the program; i.e., about the program itself, rather than about whatever
the program happens to be about. Of course, if you weren't peering into the insides of
fan implementation you might not even notice that there were such metaobjects; being
backstage they are normally hidden from the user.
CLOS implementations divide the execution of defining forms (defclass, def generic,
what reminiscent of furniture construction:
‘* ‘The macro-expansion layer that provides a thin veneer of syntactic sugar that the user
gets to see; eg., the defclass macro.
‘© The glue layer that maps names to the metaobjects; e.g., the function find-class,
jects. This is where the behavior of classes, instances, generic-functions, and methods
is implemented. (Our metaobject protocols will end up being concentrated in this
layer.)
Given this overall picture, the remainder of the chapter will fill in the details by working
through Closette, dealing in turn with the following issues:
)
‘+ How classes are represented. (Si
+ How objects are printed. (Section 1.4
** How instances are represented, and accessed. (Section 1.5)
‘+ How generic functions are represented. (Section 1.6)
*+ How methods are represented. (Section
‘+ What happens when a generic function is called. (Section 1.8)
1.3 Representing Classes
The CLOS user defines classes with the defclass macro, It is natural, therefore, to
start by examining how defclass is implemented. Although a lot of machinery will be
ine18 Chapter 1
‘Name: ‘color-rectangle
Direct superclasses: (coler-aixin rectangle)
Dinect slots: {eleazp}
Class precedence list: (color rectangle color-nixin rectangle standard-object ) |
{clearp, cyan, height, magenta, vidth, yellow)
{paint method #3)
Figure 1.2 The metaobject for the class color-ractangle,
‘The term class metaobject is used for the backstage structure that represents the classes
the user defines with def class. For example, the class metaobject corresponding to the
class named color-rectangle contains the information shown in Figure 1.2. In general,
information will include:
from the defclass f name (color-rectangle), direct su-
perelasses (color-mixin lot specifications ({cLearp. ..})
‘* Information that is derived or inherited; ¢ class's superclasses in
order of precedence
‘* Backlinks to the
among the met
stances of standard-class. Here is
{] detctass standard-class 0
(name tinitarg :nane
accessor class-nane)
(airect-superclasses :initarg :direct-euperclasses
accessor class-direct-uperclasses)
(irect-slots :accessor class-direct-slots)
(class-precedence-List :accessor class-precedence-1ist)
(effective-siots :accessor class-slota)
(Girect-subclasses :initfora
accessor class-direct-subclasses)
(direct-nethods :initforn ()
raccessor class-direct-methods)))How CLOS is Implemented 19
Note that throughout this part of the book, all code that is part of the Closette
implementation will be marked with a backstage door this way.
Bach of the slots has been given accessor functions; these will be used consistently to
access the slots of met (rather than employing s1ot-value)
1.8.1 The defclass Macro
In Closette’s three layered implementation structure, the job of the defclass macro
(macro-expansion layer) is to parse the class definition and convert it into a call to
ensure-class (glue layer).
‘The implementation of defclass is organized so that the bulk of the macro-expansion
work is carried out by canonicalize-... procedures:
i] (cotancro dotciass (nane direct-superclasses direct-slots treet options)
fengure-clase ' name
sdirect-superclasses , (canonicalize-diract-superclasses
direct-superclasses)
sdirect-slots , (canonicalize-direct-slots
direct-slote)
,@(cononicalize-defclass-options options)))
1.8.2 Direct Superclasses
The processing of the direct ses, which is performed by canonicalize-direct-
superclasses (286), is responsible for ensuring that the class names are looked up in
the global database. For example, the definition
(defclass color-rectangle (color-mixin rectangle) (.,.))
‘macro-expands into
(ensure-class ‘color-rectangle
@irect-superclasses (List (find-class ‘color-mixin)
(find-class ‘rectangle))
direct-slots (list ...))
The direct
find-class (
class names appearing in the defclass form expand into calls to
ve the corresponding class metaobjects.Ey Chapter 1
1.8.3 Direct Slots
‘The processing of a defclass form's direct slot specifications, which is carried out by
canonicalize-direct-slots (286), is more complex. It involves:
‘© Converting the slot’s name into a :name property (each slot is encoded in the form of
a property list).
© Converting the :initform option into an :initfunction property whose value is
‘a zeto-argument function‘ that gets called at instance creation time to execute the
lexical environment of the defclass. The original :initform
for display purposes.
initargs property,
der and :writer options, and collecting mul-
into a single :readers property, and multiple : writer options
property.
tiple :reader optio
into a single :vrite:
‘The result of this process is a list of property lists, each property list describing one
slot. Our example class definition
(dofelass color-rectangle (...)
((clearp :initform (y-or-n-p "But is it transparent ?")
Hinitarg :clearp :accessor clearp)))
macro-expands to
(ensure-class
‘color-rectangle
direct-superclasses (list ...)
:direct-slots
(ist
(ist
:mame 'clearp
Hinitform '(y-or-n-p "But is it transparent?")
simitfunction #' (lambda ()
(y-or-n-p "But is it transparent?"))
sinitargs '(:clearp)
readers ‘(clearp)
sri setf clearp)))))
“Readers familiar
function to be consiHow CLOS is Implemented a
The property list describing the slot is later handed to aake-direct-slot-
definition (289) which creates a direct slot definition metaobject, supplying appropriate
default values for any properties not mentioned explicitly. ‘The details of the
representation of direct slot definition metaobjects is not of concern to us;
know the various accessor functions that can be used with them:®
slot-definition-nane
slot-definition-initargs
slot-definition-initform
slot-definition-initfunction
slot-definition-readers
slot-definition-writers
1.3.4 Class Options
Options inch
options (287).
dofclass form are processed by canonicalize-defclass-
e will ignore class options for now, but will return to them in Chapter 3,
1.3.5 ensure-class
‘The glue layer function ensure-class takes the name and description of a class a5
Keyword arguments and defines a class with that name. make-instance is called to
create and initialize the new class new instance of standard-class),
and that class metaobject is then registered in the global database. (Because our CLOS
Subset does not allow class redefinition, we arrange for eneure-class to signal an error
if there is already a class with the given name.)
F] satan eorceciae out tert ai-teye
Cf eteisae tase 2h)
GcvorYen'evaitine the cae nga“. ne
Cer (cine Copy wauesnetaes
Teandndshee sane ease aL-268))
cece tin-ctass tune east
class)))
‘The all-keys parameter receives all of the keyword arguments (:direct-
SPerclasses, :direct-slots, and other class options) and passes them along to2
nake-instance wi are used to initialize the class metaobject. ‘This general
‘treatment means that neither defclass nor ensure-class needs to be aware of the
options communicated from the defclass form through to the point of creation and
of the class metaobject. The only concern of ensure-class is class naming,
he gluelayer functions find-class and (setf find-class), that maintain the
slobal mapping from class names to class metaobjects, are defined as follows
[oct ccinetnhe (otecasi-tie sant 4a)
(defun find-class (synbol koptional (errorp t))
et ((class (gethash symbol class-table nil)))
(Gf (and (null class) errorp)
(error "No class named “S." sysbol)
class)))
(defun (sett find-class)* (nev-value symbol)
(setf (gethash symbol class-table) nev-value))
oper default value for the direct superclasses.
s to the new class’s direct superclasses,
property lists into direct slot definition metaobjects,
accessor methods (we come back to these in Section 1.7.2).
# Performing inheritance-related activities.
As is the convention in CLOS, these initialization operations are carried out by an after-
method on initialize-instance specifically targeted for new instances of standard~
class:28
How CLOS is Irn
f] (aefsethod initialize-instance :after
((class standard-class) tkey direct-superclasses
direct-slots)
(let ((supers:
(or direct-superclasses
(1ist (find-class ‘standard-object)))))
(setf (class-direct-superclasses class) supers)
(dolist (superclass supers)
(push class (clase-direct-subclasses superclass))))
Get (slots
(mapcar #! (lambda (slot-properties)
(apply #'nake-direct-slot-definition
slot-properties))
direct-slots)))
(setf (class-direct-slots class) slots)
(dolist (direct-slot slots)
(dolist (reader (slot-definition-readers direct-slot))
(add~reader-aethod
class reader (slot-definition-nane direct-slot)))
(dolist (writer (slot-definition-vriters direct-slot))
(add-writer-method
class writer (slot-definition-nane direct-slot}
(finalize-inheritance clas
1.3.7 Inheritance
st all of the steps involved in the creation of a class metaobject: from
ensure-class down to make-instance and initialize~instance
‘The remaining steps are the inheritance-related activities:
‘+ Computing and the class preced
* Computing and storing 1]
from superclasses,
directly in the class and inherited
fimalize-inheritance, which is called from the preceding after-method on
Anitialize-instance, performs both of these activities:Chapter 1
EF] (cotun tinatszo-imerivance (class)
(sot! (class-precedence-list class)
(compute-class-precedence-list class))
(sett (class-slots clas
(compute-slots class))
A class inherits structure and behavior from
a specified order, from most specific to least spe«
its direct and indirect superclasses in
ic. The class precedence
Class precedence lists are stored in the class metaobject in the form of a I
motaobjects. For example, the class precedence list for color-rectangle consists of the
class metaobjects for color-rectangle, color-aixin, rectangle, standard-object,
and t, in that order. Notice that the class itself always appears first, and the classes
standard-object and t always appear at the end.
The algorithm used to compute class precedence lists is described in the section of
the CLOS Specific
topologically sorting
local precedence ordering as the constra
details of this process are not important to this
produce a predictable ordering of the set of a ci
[Fy (aetun compute-class-precedence-list (class)
Get ((classes-to-order (collect-superclassest class)))
(topological-sort classes-to-order
(renove-duplicates
(mapappend® #'1ocal-precedence-ordering
classes-to-order))
#'std-tie-breaker-rule)))
collect-superclassese collects the set consisting of a given class, all its direct su-
perclasses, all their direct superclasses, and so on.How CLOS is Implemented 25
Fy (defun collect-superclasses* (class)
(renove-duplicates
(cons class
(mapappend #' collect-superclasses+
(class-direct-supercla
class)))))
topological-sort (291) takes a set of elements to order, a set of.
breaker-rule (292),
‘The full set of slots which a class inherits is computed by all of the
slot definition metaobjects from each class that appears on that class's precedence
For the time being, we imple ler slot inheritance rules than those used by
CLOS: when more than one class in the class precedence list has a slot with a given
no merging of slot properties is supported.” The direct slot definition metaobjects are
werted into effective slot definition metaobjects, which are similar in structure to
direct slot definition metaobjects but omit values that only
(readers and writers).
‘The function compute-slots is res
] (tetun compute-siots (class)
Goapcar #'(Lanbda (slot)
(oake-sffective-slot-definition
nane (slot-definition-nane slot)
initfors (slot-definition-initform slot)
initfunction (slot-definition-initfunction slot)
initargs (slot-definition-initargs slot)))
(renove-duplicates
Goapappend #'class-direct-slots
(class-precedence-list class))
#inition-nane
Bake-effective-slot-definition (289) creates effective slot definition metaobjects,
the details of which need not concern us. We need only know that, with the exception of
Slot-definition-readers and slot-definition-vriters, all of the accessor functions
Inheritance that ae closer to what is provided26 Chapter 1
which work on direct slot definition metaobjects also work on effective slot de
metaobjects.
‘This completes the prese
mented,
tion of class metaobjects and how defclass is
14 Printing Objects
In CLOS, printing of objects is controlled by the generic function print-object. All of
the standard Common Lisp ions (print, pprint, format, etc.) call print-
object to actually print each object. Users can control the printing of objects by defining
their own methods on print-object. We define print-object. as follows:!”
[sterner pintobjost Gastance stream)
‘The default printed representation of objects shot ude the name of the object's
class. For example, the object bound to the variable door should print as somet
thods defined on standard-object will, by default, be applicable to instances
Of these classes.)
A (@efmethod print-object ((instance standard-object) strean)
(print-unreadable-object' (instance stream :identity +)
(format streaz "":(°S")" (claas-nane (class-of instance))))
instance)
1.5 Representing the Structure of Instances
CLOS provides a collection of user-visible functions for creating (nake-inetance
slot~value), slot-boundp, slot~makunbound an‘
1e class of (change~class and update-instance-for-
defined classes. Implementing these functions places
slot-exists-p), and changit
4ifferent-class) instances ofHow CLOS is Implemented ar
Object identity Different calls to make-instance must create distinct objects (Le.,
non-eq)
Slot storage There
slots.
Classification It must be possible to determine the class that a given object is an
tance of.
\gs of each instar
Reclassification change-class must change the class of an instance without changing
its identity.
For example, the door
Figure 13. The actu
stance of color-rectangle is aid ont along the lines of
fance representation is not
important to this presentation. We simply assume the existence of an abstract data type
called standard instance with the terface:!?
lowing func
@ (allocate-std~instance (cl returns a brand new standard in-
stance whose class is (class) and whose slot storage is (slot-storage)
# (etd-inetance-p (object)) returns true only when (object) is a standard instance
, the result of a call to allocate-std-instance, and not some other Common
like a cons or a symbol),
provides access to the class of the standard in-
© (std-instance-slots (instance)) provides access to the slot storage for the standard
stance (instance).
+ (allocate-slot-storage (size s a new chunk of storage big
‘enough to hold the bindings of (size) tialized to (initial
‘+ (slot-contents yrage) (location)) provides access to the binding of the slot,
-storage). Locations are numbered from zero.
at a given (loca!
Slots: clearp=nil, cyan=60, height=84, nagenta—0, vidth=38, yellow=55
Figure 1.8 The structure of an instance of color-rectangle
1.5.1 Determining the Class of an Instance
Every object in Common Lisp is an instance of a CLOS class. Internally, implementing.
generic function invocation and other operations requires that we be able to determine
For the curious, se sta-instance (287).28 Chapter 1
the class of an instance. We define the function class-of to return a class metaobject.
given an instance. The class of
class of other objects is determined in other hig!
wwe needn’t go into; we simply assume that built~
class metaobject. The definition of class-of is as fo
implementation-dependent ways that
“lass-of returns the appropriate
A (detun class-of (object)
Gf (sta-instance-p object)
(std-instance-class object)
(ouile-in-class-of object)))
1.5.2 Allocating Storage for Slots
‘The slot storage of a standard instance holds the current values of all the slots associated
with that instance. allocate-instance creates a new standard instance with slots
initialized to a izable, but otherwise secret value. We will use the presence of this
value in a slot to indicate that it is unbound.
[fl strceesersneret-uhoudcraive (st “slot anus)
(detun allocate-instance (class)
(allocate-std-instance class
(allocate-slot-storage
Gength (class-slots class))
secret-unbound-value)))
‘The complete list of slots associated
metaobject with class-slots. The lengt!
to store the instances slots,
of a class is obtained from the class
tells us how much space is needed
1.5.3 Accessing the Bindings of Slots
‘The standard CLOS slot accessing procedures (slot-value et al.) all use information
about the slot’s location in the slots to access the slot. Determination of a slot’s location
is centralized in the internal function slot~location:How CLOS is Implemented A
defun slot-location (class slot-nane)
(let ((pos (position slot-name
(class-slots class)
tkey #'slot-definition-nane)))
(Gf (null pos)
(error "The slot “S is missing from the class “S."
slot-name class)
pos)))
ot-value (instance) (slot-name}) returns the current binding of the slot named
(slot-name) in the standard instance (instance), or reports an error if the slot is currently
unbound
“| (defun slot-value (instance slot-nane)
(let (location (slot-location (class-of instance) slot-nane))
(slots (std~instance-slots instan
(val (slot-contents slots location)))
(Gf (eq secret-unbound-value val)
(error "The slot “S is unbound in the object ~S."
slot-name instance)
stance) to (new-value)
| tntan Cnet siete) (om-ttan tnatence sot-mne
(slots (std-instance-slots instance)))
(sett (slot-contents slots location) new-value
(s1ot-boundp
of the standard is
(defun slot-boundp (instance slot-naze)
Get ((ocation “location (class-of instance) slot-nane))
(slots (std~instance-slots intance)))
(not (eq secret-unbound-value
(slot-contents slots location)))))Chapter 1
(slot-makunbound (instance)
dard instance (instance)
-name)) unbinds the (slot-name) slot of the stan-
[P] (cotun e1ot-nakunbound (instance slot-nae)
(et (Gocation (slot-location (class-of instance) slot-naze))
(slots (std-instance-slots instance)))
sett (slot-contents slots location) secret~unbound-value))
anstance)
(slot-exists-p (instance) (slot-name)) returns true if and only if the standard in-
stance (instance) has a (slot-name) slot:
4 (defun slot-exists-p (instance slot-name)
(not (aul (find slot-nane (class-slots (class-of instance))
tkey #'slot-definition-nane))))
1.5.4 Init Instances
Having seen how to sand access their slots, we next turn to the
normal way in w! nstances, name
The
he generic function make~instance look:
by the user and then calls make-instance again with the class metaobject.
[Pf caetgeneric make-instance (class they?)
(detmethod ake~instance ((class syabol) Brest initargs)
(apply #*nake-instance (find-class class) initargs))
A secondary method allocates a new instance of the class using allocate-instance,
and then cal tialize it, passing along the keyword argument
that
initialize-instance
ss the class argument:
] ctetsetnod make-instance ((class standard-class) brest initargs)
(et (Gnstance (allocate-instance class)))
Capply #*initialize-instance instance initargs)
instance)
For example, the call
(make~instance ‘color-rectangle
rwidth 38 theight 84 :cyan 60 :yellow 55 :clearp nil)How CLOS is Implemented a
‘would invoke the former method, which would look up the class and call make-snstance
i it would allocate a fresh standard
of keyword arguments.
of instances are funneled to the generic
shared-initialize.
FF] etgeeric intetatceninatance (nstance ey?)
(dofacthod inivialize-insvance. (Cinstance otandard-object)
rest inttergs)
(apply #'shared-iniialize instance t snieargs))
(defgenoric reinitialize-instance (instance &key))
(dofmethod reinitialize-instance (instance standard-object)
rest initargs)
apply #'shared-initialize instance () initargs))
(defgeneric shared~initialize (instance slot-names bkey))
(detuethod shared-initialize ((instance standard-object)
slot-names krest all-keys)
(dolist (slot (class-slots (class-of instance)))
(et ((slot-name (slot-definition-name slot)))
(qultiple-value-bind (init-key init-value foundp)
(get-properties'®
all-keys (slot-definition-initargs slot)
(declare (ignore init-key))
(Gt founap
(setf (slot-value instance slot-nane) init-value)
(when (and (not (slot-boundp instance slot-nane))
(not (null (slot-definition-initfunction
slot)))
(or (eq slot-nanes t)
(weber slot-naze slot-nanes)))
(setf (slot-value instance slot-name)
(funcall (slot-def inition-initfunction
siot))))))))
instance)
The Common Lisp function get proper:
rey and value foun (and 2 ac32
shared~initialize binds values to an instance’s slots in any of several ways. In order
of decreasing precedence, they are:
1. From an explicit initialization argument whose keyword matches one of the slot’s
sinitarg names.
2. From an existing binding of the slot.
3. From a slot's :initform if it has one, and provided that the name of the slot appears
in the list of slot names passed in as the second argument to shared-initialize (t
‘means all slots).
4. Do nothing whatsoever.
1.5.5 Changing the Class of an Instance
When the class of an existing instance is changed with change-class, the bi
slots common to both the old and new classes must be carried over. After the class of
the instance has been changed, the generic function update-instance-for-different-
class is call ialize the new slots and to provide the user an opportunity to
salvage the igs that are about to be dropped on the floor. For example, exe-
cuting (change door ’rectangle) would change the class of door from color-
fle. Since rectangles don’t have clearp, cyan, magent
-ates a new instance of the new
¢ class and slot storage of these
1as the desired effect of preserving the instance's
class and realigning its slots.How CLOS is Implemented
A sfgeneric change-class (instance nev-class &key))
(defmethod change-class ((old-instance standard-object)
(nev-class standard-class)
brest initargs)
(let ((new-instance (allocate-inatance nev-class)))
(@olist (slot-name (mapcar #'slot-definition-nane
(class-slots new-class)))
(when (and (slot-exists-p old-instance slot-nane)
(slot-boundp old-instance slot-nane))
(setf (slot-value nev-instance slot-nane)
(slot-value old-instance slot-nane))))
(rotatet™*
(std-instance-slots new-instance)
(std-instance-slots old-instance))
(rotatet
(std-instance-class nev-instance)
(std-instance-class old-inetance))
(apply #'update-instance-for-different-class
new-instance old-instance initargs)
old-instance))
Just as with make-instance, a second method intercepts calls with a symbol in the
second argument position, which it coerces into a class metaobject:
©] cetantod change-ctass (instance seandar-ahject
(new-class symbol)
dew inteargo
Cepiy #tangeclane tnrtnce (Entelass eves) snseargn)
AAs mentioned above, the generic function update~instance-for-different-class
serves a two-fold purpose. By default, it simply initializes all of the new slots that
‘appeared because of the class change. But by defining other more specialized methods
‘on it, the user gains (temporary) access to the old state of the instan ie old instance
ts new state—remember the identity swap), making it possible to salvage any
bindings that are disappearing
"Te Conon Uap mao zoe nar o wet, 1 nterchangs thew of ts wo armenChapter 1
] ccetgeneric update-inetance-for-difterent-class (old nev &key))
(detuethod update-instance-for-different-class ((old standard-object)
(new standard-object)
brest initargs)
et (Cadded-siots
(renove-if #' (lambda (slot-name)
(slot-exists-p old slot-nane))
(mapcar #!slot-definition-name
(class-slots (class-of new))
(apply #'shared-initialize new added-slots initargs)))
This completes our presentation of standard instances.
1.6 Representing Generic Functions
‘The CLOS user defines generic functions with the defgeneric macro. In this section
‘we see how defgeneric is implemented. All other definitions, such as the function
ensure-generic-function lass standard-generic-function are part of the
supporting implement
A generic funct
captures the inf
fon pte
lambda list as obtained from the generic function defi ‘one for the associated
method metaobjects:!®
Ecstacy stadnr-generte-tnction 0
Cae vinta rne aocanor geeri-fectin-aae
Catach settee seen ae
ceetoassnitore 0
accessor generic-function-methods)))How CLOS is Implemented my
Figure 1.4 The metaobject for the g
1.6.1 The defgeneric Macro
n like defclass; it macro-expands into a call to the glue layer
(defgeneric paint (x))
macro-expands into
onsure-generic-function ‘paint :lambda-List '(x))
The dofgeneric macro is defined as follows:
F] tdetaacro dofgenoric (function-nane Lambda-list)
“(ensure-generic-function ',function-nane
Jambda-List 'lambda-list))
ensure-generic-function creates the generic function metaob;
there isn't one with that name already) and installs it as the glot
for its name,
+t (provided that
tion definition
E]tetan enrore-gneric-functin (function sass)
Gt Gfeoundp tunction-naze)
(fdefinition’® function-name)
Qet (gf (apply #'make-instance
‘standard-generic-function
sane function-nane al-xeys)))
Gott (fdefinseion function-nane) gf)
gt)))
This completes our presentation of how defgeneri.
behavior of generic functions (j.e., method looku
method metaobjects.
nplemented. The interesting
e presented after we introduce
"This isthe new Common Lisp replacement for syabot-function that can also be used wit
et foo) which arent aymbsis36 Chapter 1
Qualifies
Specializers
Body: (set-brush-coler ...)
Environment: the top-level environment
Genericsunetion: paint
Figure 1.5 The metaobject for paint method #2.
1.7. Representing Methods
‘The CLOS user defines methods with the defmethod form. In this section we see how
dofnethod is implemented. Again, all other definitions are part of the supporting im-
plementation and are not user-visible.
Method metaobjects capture the informati
in a defmethod definition and
provide a direct link to the corresponding generic function metaobject. This information
includes:
‘© The method's unspecialized lambda list.
© The list of method qualifiers (e.g., :before, :after).
# The list of class metaobjects that are the method’s parameter specializers.
# The form that constitutes the body of the method.
© The lexically enclosing environment.
‘ The corresponding generic function metaobject.
Figure 1.5 shows the method metaobject for paint method #2.
We introduce a class standard-method whose instances are method metaobjects that
store this information:How CLOS is Implemented. a7
5] (defclass standard-nethod ()
(Qambéa-List :initarg :lanbda-list
cossor mothod-lanbda-list)
initarg :qualifiers
accessor method-qualifiers)
(specializers :initarg :specializers
accessor method-specializers)
(body :initarg :boay
raccessor method-body)
environment
3r method-environaent)
(generic-function :initform nil
(qualiti
raccessor method-generic-function)))
1.7.1 The defaethed Macro
‘The three-layered handling of defmethod is analogous to that of defclass and def-
generic. For example,
(detnethod paint :before ((x color-mixin)) — ;Method #2
(set-brush-color (slot-value x ‘cyan)
(slot-val ‘magenta)
(slot-value x 'yellov)))
macro-expands to
(ensure-nethod (fdefinition ‘paint)
lanbda-List '(x)
iqualitiers '(:before)
ispecializers (List (find-class 'color-nixin))
Hbody (block paint
(set-brush-color
t-value x ‘cyan)
t-value x 'magenta)
t-value x ‘yellow)))
‘environment (top-Level-environment)!”)38 Chapter 1
1acro expansion layer is to ensure that the lexical environmes
of the defmethod is capt retrieved when the body of the method
is executed. Unfortunately, do. Rather than
try to solve this probl implement defmethod in a way that works only
for the most common case of top-level definitions.
il (defmacro defmethod (trest args)
Gaultiple-value-bind (function-nane qualifiers
lambda-List specializers body)
(parse-detnethod args)
“(eneure-nethod (fdefinition * function-name)
Jambda-List | lanbda-List
rqualifiers ' ,qualifiers
rspecializers , (canonicalize-specializers specializers)
body ' body
renvironnent (top-level-environment))))
‘The tedious job of pars s give
canonicalize-specializers (298) converts spec
spocializers (class metaobjects).
ensure-nethod creates a new method metaobject and adds it to the generic function
with add-method
parse-defmethod (298);
sr names (class names) to actual
P]
.50 Chapter 2
only reason to do this would have been for the convenience of the im-
plementor since the user would never have seen a class metaobject displayed.) A simple
change to Closette supports this.
jethod print-object ((class standard-class) strean)
int~unreadable-object (class stream :identity t)
(format stream "Standard-Class ~S" (class-name class)))
class)
‘We also document the identity conditions for class metaobjects: equality of class meta-
objects can be tested with eq. To make meaningful use of class metaobjects, the user
must also be given ways of extracting information from them, In Closette these ser-
vices were provided internally by metaobject accessor functions (e.g., class-nane). We
continue to supplement the language by making these available to the user:
(class-name (class)) Returns the name of the class metaobject
defining defclass form?
(class-direct-superclasses (class)) Returns a list of class metaobjects that are the
direct superclasses of the class metaobject (class), in the order specified in the defining
def class form.
(class-direct-slot
the class metaobject
dof class definition.
(class-precedence-List (class)) Returns the class precedence list for the class meta-
object (class).
(class~slots (class) Returns a set of effective slot definition metaobjects that come:
spond to all of the slots associated with the class metaobject (class)
(class-direct-subclasses a set of class metaobjects that are the
lass).
of method metaobjects, each of which
lass) among its list of sp
ss) as given in the
Returns a set of direct slot definition metaobjects for
lass), corresponding to the slot specifications in the class's
st-methods (cla:
has the class metaobject
der to make
je some general
jer for the implementor to provide these accessorIntrospection and Analysis,
nake any assumptions about the order of elements in unordered
, class~direct~subclasses, which returns a set of class meta-
there are no duplicates, but makes no promise about the order
of the elements in it returns.
2, The user should not make any assumptions about whether the results are obtained
by retrieval or by recomputation, For example, the lists returned by successive calls to
class-direct-superclasses may not be eq © nigh they will be equal.
3, The user should not try to change the metaobject, either directly with something like
(sett (class-direct-subclass: with some sort of destruc-
tive operation like rplaca app! The results of smashing
these structures are undefined.
js provision an implementation wouldn't dare hat
copy of a list containing critical information.
‘The balance between user convenience and implementor freedom we have struck here—
provide the metaobjects and accessors
raobject protocol design. As with all other language design, we consider not o
advantage to users o: our decisions, but also the potential effect on implementors.52 Chapter 2
2.2 Browsing Classes
a position to present
ld portable browsing
ove provide a standard programmatic interface to the
lass metaobjects. User code can call these functions to ascertain how the
classes are related to one another, and what the structure of each class is,
2.2.1 Finding a Class’s Subclasses
the structure of the class
with class-precedence-
ts superclasses. We can also define the function
ting of a given class, all its direct su
subclasses* to return the set co
their direct subclasses, and so on:
lasses, all
fun subclasses* (clase)
(renove-duplicates
(cons class
(mapappend #'subclasses+
(class-direct-subclasses class)
Just as we have been marking code that is part of the Closette implementation, we wi
also mark code that the user writes using the metaobject protocol with a mop. We
continue to leave both interactions with the lisp interpreter and example CLOS programs
unmarked,
In some cases, we may find it more convenient just to get the subclasses without the
class itself
(detun subclasses (class) (renove class (subclasses class)
Returning to the example of the last x, we can use this code to find the
subclasses of the class rectangle:
=> (subclasses (find-class ‘rectangle))
(#)
Moreover, since all user-defined classes are subclasses of the class standard-object,
the easy way for the user to automatically construct a list of the names of all user-defined
classes is as follows:Introspection and Analysis 33
(defvar ay-clas:
(mapcar
lase-name
(subclasses (find-class ‘standard-object))))
=> ay-classes
(COLOR-MIXIN RECTANGLE COLOR-RECTANGLE)
This solution is fully general,
portable.®
ly automatic, reasonably efficient, and completely
2.2.2 Regenerating Class Definitions
In addition to information concer
ide access to properti
ips among classes, class metaobjects
also pre class itself. For example, suppose the user wanted
to regenerate a defclass from a class metaobject. This requires extracting information
stored with class metaobjects and displaying it in appropriate ways,
Af (efen display-defclass (class-nane)
(print (generate-defclass (find-class class-nam
(valves)
(defun generate-defclass (class)
“(defclass , (class-name class)
(wapcar #'class-nane (class-direct-superclasses cl
s(napcar #' generate-slot~specification (class-direct-slots class
However, in order to generate the slot specifications,
about slot definition metaobjects. This means that
‘coessing functions as part of the language
user needs to know something
ist also document the relevant
(slot-definition-nane (slot)) Returns the name of the slot
(slot-definition~initargs (slot)) Returns a set of :initarg keywords associated
with the slot
(slot-definition-initfunction
be called to produce the value that
has no explicit :initform.
(slot-definition-initform (slot)) Returns a rendition of the form that appeared as
the slot’s :initforn, or nil if the slot has no ¢
) Returns a function of no arguments that can
e used to initialize the slot, or ni if the slot
There is catch, which we will discuss later: many system-efined classes will also appear among the
Subclasses of seandard~ob ject54 Chapter 2
stores must be a direct slot definition metaobject.
‘The fair use rules mentioned on page 50 in the context of class metaobjects also govern
the use of slot definition metaobjects.
With this knowledge, itis an easy matter for user code to regenerate an approximation
of the original slot specification:
d (dofun generate-slot-specification (slot)
“( (elot-definition-nane slot)
jen (slot~definition-initfunction slot)
Ginitform ,(slot-definition-initforr slot)))
‘when (slot-definition-initargs slot)
(mapappend #' (lambda (initarg) ‘(:initarg ,initerg))
(slot-definition-initargs slet)))
,Q(uhen (slot~definition-readers slot)
(wapappend #' (lambda (reader) ‘(:reader ,reader))
(slot-definition-readers slot)))
,Q(uhen (slot-definition-writers slot)
(napappend #' (lambda (writer) ‘(:uriter ,writer))
(elot-definition-writers slot)))))
‘We can now use display-defclass to display existing class metaobjects in a familiar
format:
=> (display-defclass ‘rectangle)
(DEFCLASS RECTANGLE (STANDARD-OBJECT)
((HEIGTH :TNITFORM 0.0 :NITARG :HETGTH)
(WIDTH :INITFORM 0.0 :INITARG :WIDTH)))
Out of curiosity we can take a peck at the classes t and standard-object:
=> (display-defclass 't)
(DEFCLASS T () ())
=> (display-defclass ‘standard-object)
(DEFCLASS STANDARD-OBJECT (T) ©)
Everything is as expected.Introspection and Analysis 55
2.2.3 Displaying Inherited Information
It is possible to display more than just the information that was explicitly supplied in
the defclass; information about inheritance can be included as well. For instance,
oth the class precedence list and the complete list of slots are associated with the class
metaobjecti here we display them in a format resembling a def class.
- jefun display-defclass* (class-naze)
(pprint (generate-defclass+ (find-class class-nane)))
(vatues))
(defun generate-defclass* (class)
“(defclass* ,(class-name class)
s(apcar #'class-nane (cdr (class-precedence-list class)))
s(napcar #' (Lambda (slot)
(generate-inherited-slot-specification class slot))
(class-slots class))))
The big difference between generate-slot-specification and generate-
imherited-slot-specification is that the latter labels slots inherited from other
plied it:
Ghee (ouree-les
Crin-ie 8 Cabin (apercass
Clint (Gloeterinicionnene set)
fey #eiot-deticiien-ne)
Cetae precedence ioe else)
Ceenraterciot pacification siob)))
Cf (ag touree hose can)
Ctmerivet-few s(class-nane souree-tass))))56 Chapter 2
For example, the class color-rectangle, which inherits slots from both rectangle
and color-mixin displays as:
=> (display-defclass+ 'color-rectangle)
(DEFCLASS* COLOR-RECTANGLE (COLOR-MIXIN RECTANGLE STANDARD-OBJECT T)
((CLEARP :INITFORM '(Y-OR-N-P "But is it transparent?")
SINITARG :CLEARP)
(CYAN: INTTFORM 0 :TNITARG :CYAN
(HERITED-FROM COLOR-MIXIN)
{ITFORM 0 :INITARG :MAGENTA
(HERITED-FROM COLOR-MIXIN)
ITTFORM 0 :INITARG : YELLOW
SINHERITED-FROM COLOR-MIXIN)
(HEIGHT: INITFORM 0.0 :INITARG :HEIGHT
INHERTTED-FROM RECTANGLE)
(WIDTH :INTTFORM 0.0 :INITARG :WIDTH
QMAGENT
(YELLOW
2.2.4 Ordering of Classes in Multi
Multiple inheritance is a powerful tool for organizing large object-oriented programs, but
there are cases where the very size of the program can cause the multiple
behavior to be unclear. Given the metaobject accessors we have ¢
now write tools which enable them to analyze these sorts of issues. For example, suppose
we were to add another class to our sample class hierarchy:
(defclass color-chart (rectangle color-aixin) ())
Recall that we have previously defined the class col
superclasses, but in the opposite order. This means that i
angle with the same
(mapcar #'class-name
(class-precedence-1ist (find-class ‘color-rectangle)))
(COLOR-RECTANGLE COLOR-MIXIN RECTANGLE STANDARD-OBJECT T)
=> (mapcar #'class-nane
(class-precedence-List (find-class ‘color-chart)))
(COLOR-CHART RECTANGLE COLOR-HIXIN STANDARD-OBJECT T)Introspection and Analysis or
This lack of consistency in the order in which coler-mixin and rectangle appear may
ions. For example, consider a generic
jon with exactly two primary methods, one specialized to the class color-mixin
when the generic funct
ized to rectangle will be
may not be what is desired. In any case,
‘case s0 that the programmer can be aware of it
‘We say that a pair of classes C; and Cp are in order provided that Ci appears before
Cy in the class precedence list of all of their common subclasses.* If two classes are in
order, we know that pairs of methods specialized to them will always be run in that
order. The predicate in-order-p tests whether a pair of classes are in order:
d defun in-order-p (ci 2)
(let (Gn-order-at-subclass-p (sub)
(let ((cpl (class-precedence-1ist sub)))
(not (null (member 2 (cdr (ember ci cpl))))))))
(or (eq ct 2)
(every #'in-order-at-subclass-p
(intersection (subclasses« ct)
(subclasses* c2))))))
‘The classes color-mixia and rectangle are not in order because their common sub-
lasses, color-chart and color-rectangle, are not in agreement:
=> (in-order-p (find-class ‘color-mixin)
(find-class ‘rectangle))
wr,
On the other hand, the classes standard-object and t are in order. (It would be
sign of serious trouble if they weren't.)
=> Gn-order-p (find-class 'standard-object)
(find-class ‘t))
T
on ae sich that a class Cis always in order with respect to58 Chapter 2
cad~elenent (position ...) ...)
sfclass display-elenent (position ...) ...)
‘The class position is suppos ‘a general-purpose
‘The class cad-elenent is supposed to model solid geor tudes position
' direct superclasses s0 as to model the position of that sol -space. Quite
ent is used for data objects that can be displayed on
the screen, and includes position within its direct superclasses to record the screen location
of object. A class of data objects that both model solid objects and are displayable on the
sereen could then be defined as follows:
(defclass displayable-cad-elenent (display-cloment cad-olenent) ())
However, under the CLOS inheritance rules, the res
x and y coordinate slots, which is clearly wrong.
‘lass only has one pair of
(Under similar circumstances
where a class inherits from some other class
along two different paths. In the above setting, there is ex position.
Write a function (has-dianond=p (class)) that determines whether there are any diamonds
class) at the apex.
jond with,
2.2.5 Summary
By documenting a metaobject protocol for class metaobjects we have made it possible
to write browsers and other program analysis tools portably. Our protocol gives the user
a natural and conveni
class structure, By following
jot overly burden imple
lar metaobject, ith generic func~
tion and method metaobjects, and shows how the one remaining piece of information
about class metaobjects, namely the list of methods specialized to the class (the result
of class-direct-nethods), can be used to bridge the gap between the class and generic
function halves of the metaobject world.
‘The next sectic
2.3 Browsing Generic Functions
Properly designed access to jon and method metaobjects allow:
to browse the world of generic functions and methods as well as the one of classes andIntrospection. 59
stances. Just as with class metaobjects, we begin by documenting a way to get access
generic function metaobjects. For generic functions defined with defgeneric, we
ymmon Lisp function fdefinition can be used to access
sject; for example, (fdefinition *paint) can be used to
thet owns them. Equ:
with 69,
We also revise Closette to i printed appearance of these metaobjects. Print-
ing of generic function metaobjects should include the name of the generic funetion; so
jon paint prints somethi ‘Standard-Generic-Function PAINT
inting of method metaobjects ude the name of the method's
as well as the method's qualifiers and specializers; so that paint method
ike #.
a (Gefmethod print-object ((gf standard-generic-function) stream)
(print-unreadable-object (gf stream identity t)
(format stream
tandard-Generic-Function “S*
(generic-function-nane gf)))
et)
(defmethod print-object ((method standard-nethod) stream)
(print-unreadable-object (method stream :identity t)
(format strean
“Standard-Method “S~( “S“} “S"
(generic-function-nane
(method-generic-function method) )
(method-qualifiers method)
(mapcar #'class-nane
(method-specializers method))))
method)
lowing additional metaobject protocols provide a functional interface to generic
‘ion and method metaobjects similar to what. we have already defined for class and
slot definition metaabjects:
(generic-function-nane (
(af), which is given in the
) Returns the name of the gen
ining def generic form,
on metaobject60 Chapter 2
(generic-function-lambda-list
tion metaobject (gf) from wl
arguments and the presence of Zoptional, rest, and #key arguments.
(generic-function-methods Returns a set of method metaobjects that are as-
sociated with the generic function metaobject (gf)
(nethod-generic-function (method)) Returns the generic function metaobject with
which the method metaobject (method) is associated; this method metaobject appears
‘among that generic
(aethod-lambda-list unspecialized lambda list for the method
‘metaobject (method) from which to determine the number of required
arguments and the presence of optional, krest, and &key arguments for this method.
(aethod-qualifiers (method) Returns a list of atomic qualifiers for the method meta-
object (method) which were given in the defining defmethod form.
(nethod-specializers (method)) Returns a list of class metaobjects that are the spe-
cializers of the method metaobject (method), one per required argument position; each
class
(mothod-body {method)) Returns the form that is the body of the method metaobject,
(method).
(method-environment (method) R
definition for the m
) Returns the lambda list for the generic func-
to determine the number of required
6 the lexical environment that enclosed the
).
¢ rules mentioned for class metaobjects also apply to the han-
and method metaobjects,
Once again, the
dling of generic functi
2.3.1 Regenerating Generic Function and Method Definitions
‘The task of regenerating defgeneric and defmethod forms from generic function and
objects is straightforward:
f (setun generate-detgeneric (gt)
‘ (detgeneric , (generic-function-nane gf)
s(generic-function-laabda-List gf)))
(dofun generate-defnethod (method kkey show-body)
“(dotmethod , (generic-function-name (nethod-generic-function method))
@(nethod-qualifiers method)
(generate-specialized-arglist method)
s@(uhon show-body (List (method-body method)))))
generate-specialized-arglist weaves t
method's lambda list:
specializer names back into theIntrospection and Analysis 61
(dofun generate-specialized-arglist (sethod)
(lete ((specializers (nethod-specializers ethod))
Canbda-List (eethod-Lanbda-list nethod))
(sunber-required (length specializers)))
(append (napear #' (lambda (arg class)
Gf (oq class (find-class 't))
are
‘Garg ,(class-name class))))
(oubseq lasbda-list 0 number-required)
specializers)
(subseq lanbda-List number-require
\ction 4isplay-generic-function pretty-prints a generic function along with
nethods:
fun display-generic-function (gf-name kkey show-body)
(display-defgeneric gf-nane)
(dolist (method (generic-function-methods (fdefinition gf-nane)))
(pprint (generate-defmethod method :show-body show-body)))
(values))
(defun display-defgeneric (gf-nane)
(pprint (generate-defgeneric (fdefinition gf-name)))
(values))
For example, we can have a look at the generic function paint:
=> (4isplay-generic-function ‘paint :shou-body t)
(DEFGENERIC PAINT (X))
(DEFMETHOD PAINT ((K RECTANGLE) )
(BLOCK PAINT
(WERTICAL-STROKE (SLOT-VALUE X ' HEIGHT)
(SLOT-VALUE X 'WIDTH))))
(DEFMETHOD PAINT :BEFORE ((X COLOR-MIXIN))
(BLOCK ParnT
(SET-BRUSH-COLOR (SLOT-VALUE X 'CYAN)
(SLOT-VALUE X 'MAGENTA)
(SLOT-VALUE X ‘YELLOW))))2 Chapter 2
(DEFMETHOD PAINT ((X COLOR-RECTANGLE))
(BLOCK PAINT
(UNLESS (CLEARP X) (CALL-NEXT-METHOD))))
Automatically generated slot reader and writer functions like cLearp can also be dis-
played:
=> (display-generic-function ‘clearp :show-body t)
(DEFGENERIC CLEARP (OBJECT))
(DEFMETHOD CLEARP ((OBJECT COLOR-RECTANGLE))
(SLOT-VALUE OBJECT ‘CLEARP) )
=> (display-generic-function '(setf clearp) :show-body t)
(DEFGENERIC (SETF CLEARP) (NEW-VALUE OBJECT))
(DEFMETHOD (SETF CLEARP) (NEW-VALUE (OBJECT COLOR-RECTANGLE) )
(SETF (SLOT-VALUE OBJECT ‘CLEARP) NEW-VALUE))
‘And so can standard generic functions like shared-initialize
=> (display-generic-function 'shared-initialize)
(DEFGENERIC SHARED-INITIALIZE (INSTANCE SLOT-NAMES &KEY))
(DEFMETHOD SHARED-INITIALIZE (INSTANCE STANDARD-OBJECT)
‘SLOT-NAMES
BREST ALL-KEYS))
2.3.2. Finding All Generic Functions
‘The user can find every generic function in the system by exploiting the links between
the class hierarchy and the
among the list of direct methods of the classes it is specialized to (even the class t).
Since each useful generic function has at least one method, we are guaranteed that every
‘useful generic function is reachable from some class in the class hierarchy.
‘The function all-generic-functions returns the set of all generic function metaob-
jects in the system:
f (eten ali-gener
(renove-duplic
(napappend #'class-direct-generic-functions
(subclasses* (find-class ‘t)))))
\ctions ()
where class-direct-generic-funct ions is defined in terms of class-direct-methods:ntrospection and Analysis 63
(gefun class~direct-generic-functions (class)
(cenove-duplicates
(napcar #'method-generic-function
(class-direct-methods class))))
ist of all generic functions in this system:
The (abridged)
=> (mapcar #*gonoric-function-name (al1-generic-functions))
(CLEARP PAINT UPDATE-INSTANCE-FOR-DIFFERENT-CLASS
AEINITIALIZE-INSTANCE INITIALIZE-INSTANCE CHANGE-CLASS
MAKE-INSTANCE (SETF CLEARP) SHARED-INITIALIZE
PRINT-OBJECT ...)
2.3.3 Finding Relevant Generic Functions
specialized to standard-object or t, it is useful to be able to set an upper limit on the
search for methods,
_f ston roerant-genoric-tontione (te citing
Cnr proceanee List eetap))09
can be called
le, we can find all the generic
the class color-rectangle:
=> (relevant-generic-functions (find-class ‘color-rectangle)
(find-class ‘standard-object))
(#
*
")64
Chapter 2
Exercise 2.3 When there are several methods that may be applicable
to be able to visualize exactly what
easy way to convey thi
For instance, using
it a parti
ed with a particular list of next methods, the following form expresses
‘what happens when the generic function paint is called with a color-rectangle:
(Prog
(CALL-METHOD
(METHOD PAINT :BEFORE ((X COLOR-MIXIN))
(BLOCK PAINT
(SET-BRUSH-COLOR (SLOT-VALUE X ‘CYAN)
(SLOT-VALUE X "MAGENTA
(SLOT-VALUE X "YELLOW))))
0)
(CALL-METHOD
(METHOD PAINT (( COLOR-RECTANGLE) )
(BLOCK PAINT
(UNLESS (CLEARP X) (CALL-NEXT-METHOD))))
(QMETHOD PAINT. ((X RECTANGLE)
(BLOCK PAINT
(VERTICAL-STROKE (SLOT-VALUE X 'HETGHT)
(SLOT-VALUE X ‘WIDTH
»
jon (display-effective-method (af) (args)) that constructs and displays
generic function will actually
functions only if you are
certain you could have defined them yourself using t mented accessors
2.3.4 Finding All Slot Accessors
10 be able to filter stich generic
his, we need to be able to determine whether a given
generated reader o writer.
dence between the name of the method's generic function and the class the method is
If one of the class's slots defines a reader with the same generic functionntrospection and Analysis 65
(defun reader-nethod-p (method)
(et ((specializers (method-specializers method)))
(and (= (Length specializers) 1)
(menber (generic-function-nane (method-generic-function method))
(wapappend #'slot-definition-readers
ss-direct-slots (car specializers)))
c
stest #'equal))))
(defun writer-method-p (method)
(let ((specializers (wethod-specializers method)))
(and (= (Length specializers) 2)
(member (generic-function-nane (mothod-generic-function method))
pappend #!slot-definition-uriters
(class-direct-slots (cadr specializers)))
et #'equal))))
Given these two predicates, we can revise the definition of relevant-generic-
functions as follows:
fun relevant-generic-functions (class ceiling &key elide-accessors-p)
‘renove-duplicates:
(mapcar #'nethod-generic-function
(renove-if #' (lambda (a)
(and elide-accessors-p
(or (reader-method-p 2)
(writer-method-p m))))
(wapappend #'class-direct-methods
(set-difference (class-precedence-list class)
(class-precedence-List. ceiling)
The pruned list of generic functions is only a bit shorter in this example setting:
=> (relevant-generic-functions (find-class ‘color-rectangle)
(find-class '‘standard-object)
relide-accessors-p t)
(#)66 Chapter 2
Exercise 2.4 The definition we have given for reader-method-p (and writer-nethod-p)
is somewhat indirect; it infers the result by checking class metaobjects to see what reader
methods were created and what that says about the n
these are common in object-oriented programming and there are two common techniques for
implementing them more directly. The first is to mark each object as to whether it satisfies
Le., a mark, pethaps using the value of a slot, on each method metaobject
er it was a readier. The second is to use a special subclass to indicat
special property. Modify Closette to jent reader-nethod-p (and writer-nethod-p)
2.3.5 Summary
Documenting the information associated
objects makes it possible for the
ing all metaobjects of
lass, generic function, and
1¢ portable programs to do things such as
ies of individual
being
functionality makes
fa program defined using defclass,
1 since provide a
ig defclass, defgeneric and defmethod to
and how documenting information required to create
defgeneric and defmethod forms,
documented abstract representation of the user's program, In
metaobjects
directly in these cases.
2.4 Programmatic Creation of New Classes
Consider a graphics application in which the program manipulates graphical
amber of geometric shapes which can be painted a vari
of several different ways. ‘The classes defining these traits
colors and labeled in one
it look something. like:
(defclass shape () ...)
(defclass circle (shape) ...)
defclass triangle (shape) ...)
(defclass pentagon (shape) ...)oe ind Analysis
or
(aefclass color () ...)
(defelass fuchsia (color) ...)
(defclass orange (color) ...)
(dofclass magenta (color) ...)
(defclass label-type () ...)
(defclass top-labeled —(Label-type) ...)
center-labeled (label-type) ...)
bovton-labeled (label-type) ...)
A orange circle with its label at the top would be an instance of the class with direct
supe
‘lasses cirele, orange, and top-Labeled. Such a class could be defined with
(Gefclass orange-top-labeled-circle (circle orange top-labeled)
)
ih it would be possible to write explicit class definitions for all valid combic
would be tedious to do so—and wasteful if of
1 few of the combinations
In such
creating the
ile execution of the gray
/ detn sake-progranaatic-inatance (euperclass-nanes trest inivargs)
(apply #'zake-instance
(tind-progrannatic-class
Gaapcar #'find-class superclass-nanes))
inivargs))
le programmatic instance creation:
> (aake-progrannatic-instance ‘(circle orange top-labeled)
rtitle "Color Wheel"
radius 10)
*S(CIRCLE ORANGE TOP-LABELED) 923456>68
Chapter 2
et we have already
ion of classes before:
dA jefun find-progranmatic-class (superclasses)
(et ((class (find-it
#' (lambda (class)
(equal superclasses
(class-direct-superclasses class)))
(class-direct-subclasses (car superclasse:
Gf class
class
(aake-progranmatic-class superclasses))))
Jlementation is structured into three
ne task of cre
*etandard~class ...). Since make~instance is already a user-vis
all we need to do is doct
Ie generic function,
ent the fact that class metaobjects are instances of the class
bbe returned subsequently by
keyword argument is
direct-superclasses
ags-nane on the new class metaobject. If this
, nil will be used.
Specifies that the direct
to be the list of classes whose
perclasses o
ts appear on (list). This
returned by clase-direct-auperclasses. If this keyword argument is omitted, or is
the class metaobject standard-ob ject:
new class are to be the set of|
* The valu
slots whose property
class-direct-slots w
jects. If omitted,
‘We also define and document grou
accessors. These rules it the
jalization arguments
specifications has beenon and Analysis: 69
Armed with this additional piece of metaobject protocol, it is now possible for the user
in terms of make-instance. Because the
to define make-progranmatic-class direct!
give the class a unique name for
vyoncer and glue layers are bypassed, there is
find-class to key on; here we name the
ing a list of the direct superclass names.*
(wake-instance ‘standard-class
nane (mapcar #'class-nane suporclasses)
direct-superclasses superclasses
direct-siots ()))
So, for example, issuing commands to create three instance of two different combina-
tions results in the creation of just two new class metaobjects:
=> (class-direct-subclasses (find-class 'circle))
oO
=> (setq it (make-programmatic~instance
"(circle orange top-labeled))
32 (make-progranmatic-instance
“(circle magenta botton-labeled))
43 (make-progranmatic-instance
‘(cirele orange top-labeled)))
+ (clase-direct-subclasses (find-class ‘circle))
(#eStandard-Class (CIRCLE MAGENTA BOTTOM-LABELED) 727553>
‘¥)
and method metaobjects is supported by
letails of the protocols
Programmatic creation of generic funct
jetaobject protocols for invoking make~instance;
2.5 Summary
ig the existence of metaobjects,
it possible
We have augmented
accessors for them, an
{or users to write portable browsers, program analysi
© programming language behavior.
8, such a clas is called anonymous. That is, given its name, it isn't possible to use find=class
to get the clase metaobject Hell, A special case of tmoxyious clases are those fo which clasacoane retin
8a These are called nomed clase,70 Chapter 2
CLOS behavior as
1, Neither have we changed any of the backstage mechanisms; they
serve to support the
‘metaobject protocol design isn’t simply a matter of removing the
backdrop from | theatre to allow the audience to see backstage. Instead,
a new kind of theatre in which we design a
the audience gets to see. The audiene:
we will develop metaobject
on-backstage. We
on-backstage mechanisms
Exercise 2.5 S criterion that anything that explicitly traffics in
metaobjects is @ metaob 18 else is a part of the ordinary
fe make-instance as part of the metaobject pro-
a source file containing Lisp d orms. In this chapter we saw that defining forms can be
reconstructed from metaobjects (e.g., a defclass from a class metaok
objects may alo be created
ents the assumption
tion ofthe program, and tet Lisp forms or text stringy ae simply far ways of presenting
the information associated with these objects to the programmer. Adding a metaobject pro-
tocol to a language seems to provide a basis for residential environments. Discuss the relation
between residential programming environments and metaobject protocols. (More information
about residential environments can be found in [Barstow et al. 84] and [Bobrow et al. 87).)
‘and how meta-
programming3 Extending the Language
When working in real life situations, users of highelevel programming languages en-
counter a number of common problems. Often, these can be expressed in the form of
desires: for compatibility with other related languages; for customizations to support
specific applications; for adjustments to the implementation in order to improve the
yrmance of a given application program.
se goals, programmers are driven to employ work-
they get by without a des
constructs. Occasionally they are forced to change their approach, and to
i programs in an entirely new way (often at great cost to the clarity of the
extreme cases, they can even find themselves forced to switch to
mn ofthe language, or to use a different language altogether
~ven develop their own languages from scratch).
Inherent in the notion of a metaobject protocol is an approach to this whole class of
the basic idea is to apply
extensible languages.
In order to achieve this goal, the metaobject protocol must be able to support changes,
to the default design. In contrast, the protocols deve! chapter dealt
with “looking” at a program. By providing a documented interface to the internal
ograms, we were able to give users the ability to build introspective
ograms. In this chapter, however, we will
tep in” and make adjustments, both to
e strategy will be to add a new set of
ones previously d
key aspects of the language
jercessory—to the introspect
Protocol, the standard object-oriented of subelassing and spe: on can
bbe used to create the desized specialized languages and specialized inChapter 3
3.1 Specialized Class Metaobjects
We begin by showing how familiar object-oriented techniques —particularly inheritance
ercessory metaobject protocols. ‘The frst
ea simple one of defining a variant of standard-class that can be used
lasses that count how many times they have been instantiated.
The expression
Jf (etelass couted-class (standard-class)
(counter :initfora 0)))
defines a specialized metaobject class, counted-class, as a subclass of the standard
metaobject class standard~class. The new class has one additional slot named counter,
but otherwise specifies the same behavior as its superclass. We can create instances of
counted-class (whi
of course, will themselves be class metaobjects) using make-
instance. For ex:
=> (sotf (find-class ‘counted-rectangle)
(make-instance ‘counted-class
name ‘counted-rectangle
idirect~superclasses (list (find-class ‘rectangle))
rdirect-slots ()))
rectangle are their
1¢ extra counter slot:
= (class-of (find-class 'rectangle))
‘W
= (class-of (find-class ‘counted-rectangle))
‘
=> (slot-value (find-class rectangle) 'counter)
Error: The slot COUNTER is missing from the class
‘#.
=> (elot-value (find-class 'counted-rectangle) ‘counter)
°Extending the Language mB
[As we saw in Chapter 1, some parts of the CLOS
functions operating on class metaobjects—generic functions
dard methode are specialized to standard-clase. For these parts of the
inherits the behavior of standard-class.
wwe saw that the job of creating class metaobjects is handled by the following standard
method on make-instance:
fpethod make-instance (class standard-class) #rest initargs)
(let (Cinstance (allocate-instance class)))
(apply #"initialize-instance instance initargs)
instance))
is method is also applicable when make~instance is used to
counted-rectangle as its first ar
Because of inheritance,
behavior of make-instance,
method:
(Gefmethod make-instance :after ((class counted-class) kkey)
(inet (slot-value class "counter)))
Now, when make-instance is used to create an instance of counted-rectangle, two
thods will be applicable: the standard method, specialized to standard-class, and
the supplemental aft just defined. The standard method will run and return a
new instance; t un and increment the counter. In this way, counted
wior defined for make-
1g how many instances
classes such as
=> (slot-value (find-class ‘counted-rectangle) ‘counter)
°
=> (make-instance ‘counted-rectangle)
#
“> (slot-value (find-class ‘counted-rectangle) ‘counter)
1
Finally, because the specialized method appl
havior of the rest of the system inel
lasses—remains unaffected.
stances of counted-class,
mn- and user-defined™ Chapter 3
Although almost trivially simple, this example nonetheless illustrates the three steps
of a process that underlies the use of all intercessory metaobject protocols:
specialized metaobject class (counted-class)
methods on the appropriate generic functions
tances of the specialized metaobject class (counted-rectangle) which
ir behavior from the standard metaobject class,
but is instead a class in an extended language. TI
CLOS, but has one addi
words, the first two steps of
as creating —using the ol
the set of generic functions
that the user can specialize to control the language’s behavior. This will increase the
number of aspects of CLOS which can be adjusted, giving the user increased fle
to produce variant languages based on standard CLOS.
3.2 Terminology
working with these new prot
lied det s and the standard
Janguage extensions on the other.
be important to
age on the one han
lowing terminology makes this clear:
juish between system-
and user jons and
# The classes standard-class, standard-generic-function, and standard-method
are the standard metaobjec
as subclasses of the standard metaobject classes; these will be referred to as specialized
metaobject classes. (See Figure 3.1.)
lasses. The user is free to define new metaobject classes
"Te fall MOP gives Jaborate and precise defini
these distinctions (p. 142).Extending the Language 5
t
standard-object
|
‘standard-generic—
function
FRAN
counted- Pe + + + | metaobject
class * sot tt E classes
} Standard
metaobject
— Direct superclass /subelass
Figure 3.1 Metaobject Classes,
hod on make-
Methods
nmake-instance
jects. A standard metaobject is
class. A specialized metaobject is an instance of
an instance of a standard metaobj
ized metaobject class,
ect class standard-class or one of
ss metaobject, counted-rectangle
is a specialized class metaobject.
'* A generic function metaobject is an instance of the metaobject class standard-
generic-function or one of
oA
‘hod metaobject is an instance of the metaobject class standard-method or
of its subclasses.
‘+ We refrain from using the term “metaclass” for classes like standard-class, choosing
instead to use
an option we
historical reasons
Il add to defclass called :metaclass, which has been retained for76 Chapter 3
3.3 Using Specialized Class Metaobject Classes
Before considering any substantial examples, we need to make it more convenient for
the user to use specialized metaobject classes. It is already si define specialized
classes like counted-class, but no correspondingly convenient way
exists to create specialized class metaobjects. In the previous example, we resorted to
using make~instance to create the class counted-rectangle, but have been
to use the existing facility for creating class metaobjects—the defelass
class
Unfortunately, the version of defclass d
ter always creates instances of standard-class. We modify
di
rented in the first cl
by adding a new opt
named :metaclass, to control the class of the resulting class metaobject; when
metaclass option is not supplied, it defaults to standard-class as before. Given this
extension, the example class counted-rectangle from above could be defined (and given
8 proper name) with:
(defclass counted-rectangle (rectangle)
oO
(:metaclass counted~class))
‘This change to defclass re
ss only modest changes
yure-class, whi
is handled in the same way as other lefclass form; the expansion passes the
value to ensure-class using a keyword argument. The previously mentioned (but not
discussed) canonicalize-defclass-options (287) handles this. The above defclass
form expands to;
(onsure-class ‘counted-rectangle
sdirect~superclasses (list (find-class 'rectangle))
direct-slots ()
rmetaclass (find-class ‘counted-class))
ly modified to accept the :metaclass keyword
ch the value of that argument. If omitted,
ensure-class supplies standard-class as the default value. The revised definition is:Extending the Language 7
@] (defun ensure-class (name Brest all-keys
akey (metaclass (find-class ‘standard-class))
Ballow-other-keys)
(if (find-class name nil)
(error "Can't redefine the class named “S." name)
let ((class (apply #'nake~instance
metaclass :name nae all-keys)))
(setf (find-class name) class)
class)))
Wit
ese changes,
stforms, race
cluding proper handling
le when defining specialized
ors and
1 as standard class metaobjects.
Having licensed the user to define subclasses of standard-class, itis also appropriate
to allow specialized methods on initialize-instance and print-object. This gives
the user the same control over initialization and printing in subclasses of metaobject
classes as in subclasses of any other kind of class
I i, we can
ate some of th
d for specializing
prine-object by modifying the standard method so that it displays the name of the
class's class in addition to the name of the class itself.
fnethod print-object (class standard-class) stream)
(print-unreadable-object (class stream :identity t)
(format stream "":("S") “S"
(class-nane (class-of class))
(class-nane class)))
class)
This ensures that instances of specialized metaobject classes will be automatical
tinguished from standard class metaobjects even when there is no directly corresponding
Print-object method,
=> (find-class ‘counted-rectangle)
‘*
=> (find-class ‘rectangle)
‘*Chapter 3
We now resume develop:
demonstrating how a number
ere uages, can be solved by producing variants of CLOS with different kinds
of inheritance behavior.
3.4 Class Precedence Lists
Many prospective CLOS programmers have extensive experience wi
in other Lisp-based object-oriented program
they would find the transition to CLOS more attractive if their existing code could be
easily ported
In most respects, Flavors, Loops, and CLOS are sufficiently similar that the task of
Flavors or Loops code into CLOS raightforward. Bach l
methods, generic function:
der the priority of supe
ig of Flavors or Loops programs into CLOS can f
compatible with Flavors or Loops, a major stumbling block to co}
removed.
lity would be
wasis of all inheritance
‘a change to a class's class precedence list
naking it possible to provide Flavors
yest way to give the user control over the class
In CLOS, as we saw in the model backstage arc
or Loops
precedence list?
‘The most obvious approach user to modify the class precedence
list of a class metaobject explicitly using set with class-precedence-List. With this
kind of flexibility, arranging for a given class to use alternative inheritance rules woul
straightforward. Unfortunately, however, while simple and powerful, this proposal opens
up the language more than is desirable. Consider two example scenarios.
st, suppose that a user, using this scheme, were to define a variant of CLOS in
the class precedence lists changed frequently throughout the program’s execu
This might be powerful—and the resulting programming language would certainly be
exciting to program in~-but such behavior would affect too many other parts of the
language. What, for example, should happen to extant instances if the change to theExtending the Language oy
lass precedence list causes the set of slots defined in a class to change? What should
happen if, w inning, a class precedence list changes in a way that affects
the sequence of next methods?
Second, suppose the user were to change a class’s class precedence list so that it no
longer included the classes standard-object and t. Again, this might be useful, but it
presents a serious inconvenience to the implementor. Note, for example, that an instance
of such a class mi
defined system-supplied method, specialized to standard-object, would no longer be
applicable). While the user could repair this problem by defining their own method on
In essence, these problems stem from giving the user the power to alter the language
so radically that very few of its original characteristics remain. This is outside the goals
of metaobject protocol design, which are simply to make the language flexible enough so
that it can be extended to a range of relatively similar languages. We wanted to open
nguage from a si int in design space to the surrounding region. Instead, we
have opened it almost to the entire space of program
how m
implementor both will be able to rely on i
In the case at hand, we strike this balance by requir
be a fixed property of the class; (ji) that it include all of the class's superclasses;
iat those superclasses include the classes standard-object and t. This gives
xr the power to adjust the inheritance behavior over a wide range—including that of
all the major object-oriented programming
guarantees inheritance behavior of a1
that a class's class prece-
backstage architecture, compute-class-precedence-list
‘was the function that did the real work of computing class precedence lists, and class-
Precedence-list was the function that fetched the previously computed value. We will
both of these in our metaobject protocol, allowing the user to define methods on
t only to call the latter. The user will not be permitted to use setf with
class-precedence-list
These problems are similar to those associated with change-classChapter 3
We are now ready to document the new piece of protocol!
(compute-class-precedence-List (class)) This generic function is called to compute
must be a permutation of the other superclasses of (class
of a class may not change once it is computed; the
class-precedence-List once and then store its value. The value can
by calling tion class-precedence-List. The results of destructively modify-
ing the value are undefined.
Note that this restriction of power has efficiency advantages as well. In Chapter 1
we mentioned that the basis of slot access optimization is memoization of slot locations
Under the oil, nese ogrtion of lowing citar hangs io
precedence list has been
ycations ean be computed
computed, t
once and for all
Implementing the new protocol in Closette requires only that we convert compute-
class-precedence-List to a generic function, and that we provide a standard method
to implement the normal CLOS rules. The code in the body of this method is unchanged
fe iginal definition as a function (p. 24).
Gv tGtneanesoconder(ctlect-noparctareee chee)
(topological-sort classes-to-order
Gopupend a owe- prea
tseetietrnter 0)
ordering
)
3.4.1 Alternative Class Precedence Lists
Given this new protocol, the user can wri jlement variants of CLOS that
provide the Flavors and Loops inheritance rules. Those rules can be summarized as:extending the Language al
depth-first, left-to-right root class being treated specially. They
differ from each other of a class that is encountered more than once—a join
class-—gets traversed on its first or last visit, The behavior of each language's rules, as
well as the difference between them, can be seen in the following example:
(defclass s (a b) O) S a
Getclass r (ac) 0)
(Getclass q (s x) 0)
‘The class precedence lists for the class q in each of the languages are as follows:
Flavors (qs abr c standard-object t)
Loops sb rac standard-object t)
CLOS (qs rac b standard-object t)
Tavors ordering can be Stplemented using » depthrGret, preorder traversal ofthe
e In the example,
ice in the the depth-first, preorder traversal (qs a b r a c), only
retained. The precedence ordering used by Loops retains
scurrence of any dupl
ive precedence
classes, flavors-class and loops-class.
List, specialized to each of the classes, act
A torcranetravoreciass (oandaré tess) 0
(Getclass loops-class (standard-class)
(detuethod conpute-class-precedence-list ((class flavors-class))
Gappend (renove-duplicates
(dopth-first-preorder-superclasses* class)
froa-end t)
Gist (find-class ‘standard-object)
(find-class **))))82 Chapter 3
De ee ee ene
(append. (renove-duplicates
.pth-first-preorder-superclasses* class)
fron-end ni)
(aise (Find-elase ‘standard-object)
(find-elass '0)9))
(defun depth-firet-preorder-superclassest (class)
Gf (eq class (find-class 'standard-object))
°
(cons class (zapappend #!depth-first-preorder-superclassest
(class-direct-superclasses class)
‘The user could then go on to define a Flavors class as follows:
(defclass q-flavers (sx) 0
(cnetaclass flavors-class))
Instances of this class can be created in the
sual way, with make-instance; their slots
‘can be accessed with slot-value; and they can be passed to both user and syste
defined generic functions. That is, despite the difference in inheritance behavior, this class
and its instances interoperate
as well)
3.4.2 Summary
(and with other language extensions
‘We have now presented the framework common to all intercessory
We critically in power of object-oriented programming techniques: the
ability to orgat ¢he behavior and the implementation of a system in such a way
as to enable users to modify and extend it tal ways,
In the case of metaobjeet protocols, the system in question is
ject protocols,
The effort required to cre
ved class and method definitions. The change affects al
aspects of the new language's inheritance behavior in predictable and appropriate ways:
slot inheritanc hod applicability alike.Bx 83
sof our original
ject protocol is, like the old, a cleaned up on-backstage. ‘The
udience can not only see this on-backstage; they can also
ipulate it thereby affecting what happens on-stage. Manipulating the on-backstage
seis like making minor
theatre being produced. It can now be
“go on-backstage,
to reflect the fact that
of code to create a vi
that new language. I
backstage—members of the audience who get to work with it can onl
outcome within the framework established by the real producers.
of code in
affect the play's
Exercise 3.1 In CLOS, all instanet
perclass. In this way, standard-class
standard-clase have standard-object as a si
avior for user-defined classes. In Closette this det
the class metaobject—when a class is created with no superclasses, a list
consisting of just the class standard-object is used.
Often, when defining a spe class, tis convenient to similarly define
‘a specialized default superclass as w Flavors, there is a default superclass
called vanitia-flaver. Our definition of the specialized metaobject class flavors-class
bbe more complete if we also arranged for vanilla-flaver to be a default superclass
ances of flavers-class, Show how this exte 1 behavior can be
supported in the same way as the standard defaulting behavior—with a specialized method
on initialize-instance. (You are free to use an around-method in your solution.)
‘alia
3.5 Slot Inheritance
Our next example addresses the ne users have for programming language features
that are specialized to their particular application domain, Even when most of a user's
needs could be met by an existing language, if they are not all met, the traditional ap-
Proach has required building @ complete, customized language from scratch, In contrast,
Providing an appropriate metaobject pi ‘an make it possible for users to extend
‘ existing language to p: weir special needs.
Knowledge-representat guages such as Strobe [Smi
‘xcellent example, They tend to be heavily object-oriented in
[Barth& Young 87| are an
re, and in many waysoy Chapter 3
object-oriented prograt
es. Slot attributes (or
to CLOS. The following class det
stance of the class credit-rat.ing is to have a slot named Level with two independently
settable attributes late-set and time-set.
(detclass credit-rating ()
((level :attributes (date-set tine-set)))
(:metaclass attributes-class))
‘A sample use of this extension would produce the following behavior:
=> (setq cr (nake-instance ‘credit-rating)
=> (slot-attribute cr ‘level ‘date-set)
NIL
=> (sot (slot-attribute cr ‘level ‘date-set) "12/15/90")
=> (slot-attribute cr ‘level ‘date-set)
"12/18/90"
Attributes should be inherited
associated with a sl
name in the class's
rating, the slot named
last~checked and interval:
(defclase aonitored-credit-rating (credit~rating)
((evel :attributes (last-checked interval)))
(metaclass attributes-class))
The desired extension to CLOS can be divided into three parts. Together with a rough
description of the metaobject protocol support required by each, they are:the Language 85
rust give the user control over the way in w!
stored, and how those stored values can later be accessed,
« Computing the inheritance of slot attributes. For a given slot in a given class, t
set of attribute names should be stored in the corres
metaobject. To support this, the metaobject protoc
way samenamed direct slot definition motaobjects are coalesced to produce an
1e needed in each instance to store
the values of t , the metaobject protocol must give
the user control over the process that decides how much storage to allocate per instance
of a class.
3.5.1 Slot Inheritance Protocol
user can request extra storage simply by adding extra slots,
In our model backstage architecture, these kinds of decisions are m
is used. The need for more sophisticated control
itance process suggests a two-part protocol in which gathering
‘0 inherit and combining same-named slots are handled
of slots
result is a set of effective slot defini-
is set is stored and can subsequently
function class-slots. Class slot computations are based solely
‘on the class's class precedence list and the direct slots of each class in that list. At the86 Chapter 3
Ned, th
direct slot definition metaobjects from the class and its superclasses, and then call
conpute-effective-slot-definition to coalesce these into a single effective slot
definition metaobject.
(compute-effective-slot-definition (class) (slots)) 1
to coalesce (sl
same name. The result must be a single effective slot det
Il be used for the class metaobject The (class)
consulted in the body of
can be defined. The direct slot de
with the highest pri
of origin appear in the cl
(slots) are arranged
—that is, in the same order as their classes
In addition, we allow the user to call make-direct-slot-definition and make-
effective-slot-definition to ct defini taobjects.
Closette requires only minor modifications to support this new protocol. The fi
conpute-slots is replaced with a generic fi mn the generic
function provides the specified behavior, calling compute-effective-slot-def inition,
as required, to combine same-named slots:
effective sl
[P] taetgeneric conpute-slots (class)
(Gefmethod conpute-slots ((class standaré-class))
Gets ((all-slots (napappend #'class-direct-slots
(class-precedence-list class)))
(all-nanes (renove-duplicates
apcar #'slot-definition-nase all-slots))))
(mapcar #! (lambda (nane)
(compute-ef fective-slot-definition
class
(remove name all-slots
Key #'slot-definition-nane
test-not #'eq)))
all-nanes)))
onpute-effective-slot-def inition implements the de-
protocol provides
ions, we takeBxtending the Las 87.
in Chapter 1. The
provides one, and initialization arguments are computed by taking thé
slots to be coalesced.
ation form is
F ccotgeneric conpute-eftectiversiot-definition (class direct-siot
(defnethod conpute-effective-slot-definition (class standard-class)
direct-slots)
et (Gniter (find-if-not #'null direct-slots
key #'slot-definition-initfunction)))
(make-ef fective-slot-def inition
:name (slot-definition-name (car direct-slots))
Hinitform (if initer
(slot-dofinition-initform initer)
nil)
Anitfunction (if initer
(slot-definition-initfunction initer)
nil)
‘initargs (renove-duplicates
(mapappend #'slot-definition-initargs
airect-slots)))))
3.5.2 User Code for the Slot Attributes Extension
‘We can now present the user code required to implement the slot attributes extension.
we are setting aside is
the :attributes slot option is properly handle
1o the other slot definition metaabject accestors (0.88 Chapter 3
d (defmethod compute-effective-slot-definition ((class attributes-class)
direct-slots)
Get ((normal-siot (call-next-nethod)))
Gett (slot-definition-attributes normal-slet)
(renove-duplicates
Gaapappend #'slot-definition-attributes direct-slots)))
nornal-slot))
To hold the values of attributes, storage is required in each instance, over and above the
storage needed to hold the values of the slots themselves. We do this simply by inventing
a single extra slot, which will store attribute values for all the slots. (A doubly-nested
alist is used, keyed on the slot and attribute name.) We add the extra slot, named
all-attributes,‘ and arrange for it to be initialized appropriately with a specialized
method on compute-slots.
_f nteiios comnts
(let* ((normal-slots (cal
se
(enc
dimen oot
Conn Caloe-definitionnane st
Chapcar aan atts) (eons atte si)
attributes-class))
-next-method))
normal-slots)))
(cons (make-effective-slot-definition
:name ‘all-attributes
Hinitform ‘alist
simitfunction "(lambda () alist))
normal-slots)))))
‘The effect of this method is that every instance of the class credit-rating
or monitored-credit-rating (indeed, every instance of every class whose class
attributes-class or a subclass thereof) is created with the extra slot. All that remains
to be implemented are the functions that access the attributes:
_f Wotan slot-averibute (instance slot-nase attribute)
(ear (elot-attribute-bucket instance slot-nane attribute)))xtending the Language a9
flcsten (rst soestribte)(entveeiatnce sone arta)
(ootf (cdr (slot-attribute-bucket instance slot-nane attribute))
new-value))
‘The helping function slot-attribute-bucket does all the work of finding the appro-
priate bucket
(defun slot-attribute-bucket (instance slot-nane attribute)
(let® ((all-buckets (slot-value instance ‘all-attributes))
(slot-bucket (assoc slot-name all-buckets)))
(unless slot-bucket
(error "The slot named ~S of “S has no attributes."
slot~nane instance))
(et ((attr-bucket (assoc attribute (cdr slot-bucket))))
(unless attr-bucket
(error "The slot named “S of “S has no attribute"e
named “S." slot-name instance attribute))
attr-bucket)))
With the user code complete, we can run the example code from page 84. After doing
0, examining the extra slot shows the attribute buckets and values.
=> (slot-value cr ‘all-attribu
= ((DATE-SET . "12/15/90") (TIME-SET . NIL))))
= (slot-value (nake~instance 'monitored-credit-rating) 'all-attributes)
(CLEVEL . ((LAST-CHECKED . NIL) (INTERVAL . NIL)
(DATE-SET . NIL) (TIME-SET . NIL))))
che object-oriented languages
e are markedly different ft
the analog of calling slot-value) from within the body of
Methods specialized to that class. Methods specialized to subclasses must send a message
(the anatog of calling a generic function) to access the value of the slo.90 Chapter 3
of this exercise is an exte
on to CLOS which supports
is behavior. Consider
(defetass ct
((foo :initform 100))
Gmetaclags encapeulated-class))
(aefclass c2 (et)
((foo :initform 200))
(metaclass encapsulated-class))
iat instances of ¢2 have two slots named foo. One is private to the class ct and
(defmethod mumble ((o ¢1))
(private-slot-value 0 "foo (find-class ‘c1)))
(defmethod mumble ((o ¢2))
(+ (private-slot-value © ‘foo (find-class '¢2))
(call-next-method)))
=> (mumble (wake-instance ‘c1)
100
=> (oumble (make-instance '<2))
300
lement this extension to CLOS, ine!
ig the function private-slot-value,
3.6 Other Inheritance Protocols
As another example
port, consider default i
initial subset. Default
option in the defclass forn
ever instances of the class are created.
For example, consider the definition:
language extension that metaobject protocols ean sup-
lization arguments, a feature of full CLOS omitted from our
arguments are specified with the :default~inii
le default arguments to make-instance when
(defclass frame (rectangle)
°
(:metaclass default~initarge-clase)
(:default-initargs :vidth 10))the Language a
Ifake-instance is now called to create an instance of frame, and the : width initializa-
tion is not explicitly provided in the call, the effect should be as if it had been provided,
with a value of 10. That is,
(ake-instance ‘frame :height 20)
has the same effect as
(wake-instance 'frane :height 20 :width 10)
can easily be implemented using the existing metaobject protocol
First, we assume that defeclass canon the :default-initargs option into the
direct-default-initargs option to ensure-class. The defclass form above ex-
pands to:*
(ensure-class ‘frame
direct-superclasses (list (find-class ‘rectangle))
idirect-slots ()
imetaclass (find-class ‘default-initargs-class)
idivect-default-initargs (ist ‘:vidth '10))
{ isterss detasie-incarge-cate (otndaréciass)
(Ctract-defauie-inttcee
initarg vairect-efeat-initerge
timitform ()2 Chapter 3
rect list. Because not all of the classes
class precedence list will be instances
of default-initargs-class (e.g,, standard-object and t) we define an extra method
on class-direct-default-initargs to return the empty list for other classes.
_f (dotun compute-class-default-initargs (class)
(napappend #'class-direct-default-initarge
(class-precedence-list class)))®
(defmethod class-direct-default-initargs (class standard-class))
o)
Finally, nake-instance must be extended so that it supplements the explicitly sup-
plied initialization arguments with the default ones,
_f Wofsetiod nake-instance ((class defauit-initargs-class) rest initargs)
(apply #*call-next-nethod
class
(Ceppend initargs
(compute-class-defauit-initargs class))))
‘Testing confirms the correct behavior,
=> (setq f (make-instance ‘frame :height 20))
=> (slot-value f ‘height)
20
=> (slot-value f ‘width)
10
3.6.1 Precomputing Default Initialization Arguments
‘This user extension to support default initialization arguments has the desired behavior,
ut is relatively inefficient. The source of the inefficiency is the needless duplication,
on each call to make-instance, of the computation of default initialization arguments.
Since classes and their default initialization arguments cannot be redefined, the default]
initialization arguments could be computed just once and then stored with the class}
metaobject for subsequent use by make-instance.
‘The precomputation of default initialization arguments is an inheritance-related activ-
ity similar to the class precedence list. and slot comp
ions. It is sufficiently different,
In Common Lisp, it is perm ‘The frst, or Lefont
cccurtence takes prevedence over lowing epecalized method|‘extending the Language 93
wer, that neither of those exisin
the class precedence list needs
ation arguments can be computed.
gests that we need to provide the user with a more general purpose protocol for
iheritance activities. ‘The clear suggestion from Closette is to make f inalize-
inheritance a generic function that is the focus for all inheritance-related computations
for the class.
‘This requires another simple modification to Closette, the conversion of final ize-
inheritance from a function to a generic function. The body of the standard method
is the same as in the original function definition (p. 23).
rrotocols is particularly appropriate. F
¥e computed and stored before the defau
A otgenertc finaliza-imeritance (class)
dofaothod finalize-inheritance (Celass standard-class))
pe omen
Cconpute-class-precedance-Iiet class))
Gott (Class-siots class)
(Coupute-slots class)
(values))
ce that the role of the standard method is to modify the class metaobject in
«i primary method
failed to call cal1-next-method, the class precedence list and the ful list of slots would
not be computed and stored. (We will return to this issue in greater detail in Chapter 4.)
‘The appropriate specification for this generic function is then:
(timatize-imneritance (class)) This generic function and record
all inheritance-related information associated with the class metaobject (class). The
value returned is unspecified. It is guaranteed that after the standard method runs the
claso's class precedence list and set of slots will be available. The standard method,
specialized to standard-class, cannot be overridden
is new protocol in place, the user implementation of default initialization ar-
Buments can be made more efficient. First, we replace the earlier de of default-
initargs-class (p. 91) wit defines an additional slot to hold the precomputed
default initialization ar4 Chapter 3
f (detclass default-initerge-class (standard-class)
(direct-default-initargs
initarg :direct-default-initargs
storm ()
cessor class-direct-default-initargs)
{fective-default-initargs
saccessor class-default-initargs)))
This new slot is
after the class metaobject has been otherwise finalized.
_f (otsethod finalize-inheritance :atter ((class default-initargs-class))
(sett (class-default-initargs class)
(compute-class-dofault-initargs class)))
Finally, the specialized nake-instance method (p. 92)
class-default-initargs to retrieve the precomput
class-default-initargs to compute them.
replaced with one that calls
tt instead of calling compute-
_f (otsernod nake-instance ((class default~initargs-class) rest initargs)
Capply #'call-next-nethod
class
(append initargs (class-default~initargs class))))
stclass. This suggests th
nism for extending the behavior of dof class
0: ach to this problem is to pi
o cl
seen in the following revised definition of defclass:pxtending the Language 95
J] (detnacro defclass (name direct-superclasses direct-slots krest options)
(ete ((netaclass-option
(find ‘:metaclass options :key #'car))
(netaclass-name (if metaclass-option
adr netaclass-option)
wtandard~class))
(sample-class-netact
(allocate~instanc:
(canonical-supers
(canonicalize-direct-superclasses direct-superclasses))
(canonical-siot:
(canonicalize-direct-slots direct-slots))
(canonical-options
(canonicalize-defclass-options
sample-class-netaobject
(remove metaclass-option options))))
mnsure-class *,nane
direct-superclasses ,canonical-supers
direct-slots ,cancnical-slots
lass (find-clase ',notaclass-nane)
jonical-options
‘find-class metaclass-naze)))
(defun canonicalize-defclass-options (sazple-clase options)
(napappend #' (lanbda (option)
(canonicalize-defclass-option sample-clase option))
options))
(detgeneric canonicalize-defclass-option (sample-class option))
(Gefmethod canonicalize-defclase-option
((eample-class standard-class) option)
“Unrecognized defclass option (car option)))
to handle extended opti
dard method supports no
write, as part of the default
port the :default-initargs option. Note that in order to
1e new macro an alternate96 Chapter 3
3.7 Slot Access
In an object-oriented language like CLOS, some kinds of user extensions will require
protocols that control low-level slot access. For example, such a protocol is required to
write an extension that records all accesses to the slots of an instance. In this section,
‘we develop metaobject protocols for controlling slot access.
The lowest level slot access operations are the functions slot-value, (setf slot~
slot-boundp and slot-makunbound. To design an appropriate protocol for
these operations, we first consult Closette to understand the relevant under-
entation of each operation is similar, and involves the
itself, and the slot name. (The definition of slot~value
st the class of the object is consulted to determine whether
the slot exists an location is; then the slot is fetched out of the instance.
‘We can design protocol for these ions by placing each under the direct
control of a generic function. For example, the protocol for controlling slot-value is:
class metaobject, the inst
‘can be found on
(elot-value-using-class (class) (instance) (slot-name)) ‘This generic function pro-
vides the behavior of the function slot-value. When slot-value is called, it calls
this generic fun instance) and (slot-name) are the arguments to slot-value
(class) is the class of (instance). The value returned by this generic function is returned
by slot-value. The standard method can be overridden.
Modifying Closette to support this new protocol requires redefining slot-value to
call slot-value-using-class. The body of the standard method is unchanged from
the original definition of slot-value.
] ccetun stot-vatue Gnstance slot-nane)
(slot-value-using-class (class-of instance)
instance
slot-nane)))
(detgeneric slot-value-using-class (class instance slot-nane))Bxtending the Language 7
[fteteeios ator voan-ning-clne (cen standard clan
eee
slot~nane)
(let* (location (slot-location class slot-name))
(ocal-slots (std-instance-local-slots instance)
(val (slot-contents local-slots location)))
(Gf (eq secret-unbound-value val)
(error "The slot ~S is unbound in the object ~S."
slot-name instance)
val)))
‘The modifications for (setf slot-value), slot-boundp and slot-makunbound are
similar,
3.7.1 Monitoring Slot Access
Using this last protocol, the user can easily define an extension which records all slot
_f (etclass monitored-class (standard-class) 0)
[A before-method on each slot access generic function records the accesses
CCeansnettorscias)iatane satan
(etmethod (setf slot-value-using-class) :before
(nov-value (class monitored-class)
instance slot-nane)
(note-operation instance slot-nane 'set-slot-value))
Gefaethod slot-boundp-using-class :before
((class monitored-class) instance slot-nane)
(note-operation instance slot-nane ‘slot-boundp))
(defmethod slot-nakunbound-using-clasi
((class monitored-class) in
(note-operation instance slot-nane ‘slot-nakunbound))‘The functions that manipulate access history are as follows:
Loree Cuistory-list 0
(defun note-operation (instance slot-nane operation)
(push ‘(,operation ,instance ,slot-name) history-list)
(values))
(defun reset-slot-access-history ()
tq history-list ())
(values))
(defun slot-access-history ()
(reverse history-list))
)
‘Testing of slot monitoring confirms that it behaves as desired.
(detclass foo ()
((sloti :accessor foo-slot1 :initarg :slet1)
(slot2 :accessor foo-slot2 :initform 200))
(:metaclass monitored-class))
set-slot-access-history)
#
tq i (make-instance ‘foo :slot1 100))
=> (sett (slot-value i 'sloti) (foo-slot2 4)
200
=> Ginef (foo-sloti i))
201
=> (slot-access-history)
((SET-SLOT-VALUE # SLOT1)
(SLOT-BOUNDP # SLOT2)
(SET-SLOT-VALUE # SLOT2)
(SLOT-VALUE # SLOT2)
(SET-SLOT-VALUE # SLOT)
(SLOT-VALUE # SLOT1)
(SET-SLOT-VALUE # SLOT1))
sFrom initialization
;From initialization
;From initialization
sFrom foo-slot2
iFrom sett
iFrom incf foo-slott
iFrom incf foo-slott
Chapter 399
Fis the user's need is not to change the behavior of
the language, but rather to adjust the implementation tradeoffs for application-specific
performance reasons, Imagine a situation in which the user defines a class with a large
number of slots (several hu Purther imagine that the typical
usage pattern of this class is that there are a large number of instances, but in any
iven instance only a small number of the slots are actually used. In this scenario, the
entation strategy we
ly, we look at an examp!
less storage to be allocated, and may w
problems,
‘This pattern of class definition and instance usage is ©
lead to paging and other virtual memory
ledge representa
lly provide dynamic
jon to CLOS, For sim
explores a more selective variant)
(defclass biggy (
(ai bi ct di ef fi gi ht it jt kt li at
a ol pl qi ri si ti ul vi wi xi yt zi
a2 b2 2 d2 02 £2 g2 h2 12 j2 k2 12 a2
2 02 p2 q2 r2 82 t2 u2 v2 w2 x2 y2 22
a3 b3 c3 43 e3 £3 g3 h3 13 j3 k3 13 a3
13 03 p3 q3 r3 93 t3 u3 v3 u3 x3 y3 23
a b4 cf d4 of £4 g4 hd 14 54 k4 14 04
nd of pa qd r4 sf té ud v4)
metaclass dynamic-slot-class))
used for a sparse
of all the other slots. Specialized methods on slot~
be defined to access this sparse structure, From the
i of view of the function allocate-instance it would appear that instances of the
Volue-using-class et al.
Pe100 Chapter 3
class baggy have one slot rather than 100, so th
we desire
Unfortunately, this approach does not work properly. ‘The p
‘an important consistency constraint between two
xnow claims that instances have only a single slo, wl
ances would be smaller in the way
original slot names as well. The fact
ese include: shared-initialize, to
mice; and change-class, to identify
instance. If the user defines methods
these other language operations will
which slot values are to be carried over to the
which present inconsistent views of a class's
no longer behave appropriately.
If we want the user to be able to control the way slots are stored, a more direct
protocol is required. Consulting the model backstage architecture, we can see that there
are actually two slot-like concepts. ‘The first is the on-stage slots themselves and the
second is the backstage slot storage. ‘The correspondence between these two layers is
‘maintained by the slot access generic functions and the backstage function allocate~
instance.
Providing the user with control over this correspondence must be done carefully. We
are working at a very low level, and we must be careful not to overly constrain the
implementor by documenting arbitrary implementation details. In particular, it would
"e appropriate to document the low-level representation of instances and slot storage
d-instance-slots, slot-contents etc.) as part of the protocol.
‘The solution we will use is to design the protocol so that the user can, on a per-
slot basis, disable th tion’s slot storage mechanism. Once this is done, the
storage for the slot can be implemented in whatever way is appropriate. This allows the
user to control slot storage without having to know the details of the implementation’s
storage strategy.
‘We will do this by adding a new property to direct and effective slot defini
:allecation, which indicates how storage for the slot is handl
this property is :instance, which causes the standard methods on allocate~instant
slot-value-using-class et al. to treat the slot in the usual way. Any other value caus
these methods to ignore the slot, allowing specialized methods to handle it instead. This
property is carried over from direct to effective slot definitions, with the most specific
direct slot’s allocation prevailing.
(allocate-instance (class)) This generic function is called to allocate a new instance
of the class metaobject (class). The result returned is the new instance. For each
slot in the class, slot-def inition-allocation is called to determine how its storage‘extending the Language
should be allocated. The standard method on allocate-instance will only allocate
storage for slots with :instance allocation; all other allocations are ignored. The
standard method cannot be n
(slot-def inition-allocatior lefinition)) ‘This function is called to return the
allocation of a slot, ‘The default value is : instance.
We also need to revise the specification of slot-value-using-class ct al. If the
standard method of these generic functions is run, and the allocation of the slot is not
instance, an error is signaled. A specialized method must handle these slots first, and
not invoke cal1-next-nethod.
‘Again, the changes to Closette are minor. The function allocate-instance (p. 28)
becomes a generic function. The standard method allocates storage only for those slots
with :instance allocation. The appropriate behavior for slot-value-using-class et
al, can be achieved simply by modifying the internal function slot~Location.
F] isetgenersc attocate-instance (class))
(@efaethod allocate-instance ((class standard-class))
(allocate-std-instance
class
(allocate-slot-storage (count-if #!instance-slot-p
(elass-slots class))
secret-unbound-value)))
(defun slot-location (class slot-name)
Get ((slot (find slot-nane
(class-slots class)
tkey #'slot-definition-nane)))
(4f (aul slot)
(error "The slot “S is missing from the class ~S."
slot-nane class)
et ((pos (position slot
(renove-if-not #'instance-slot-p
(class-slots class)))))
(Gf (aul pos)
(error "The slot “S is not an instance"@
slot in the class ~S."
slot~name class)
os)))))102 Chapter 3
4 (defun instance-slot-p (slot)
(eq (slot-definition-allocation slot) ':instance))
Returning to the dynamic slot example, the user code can now be written properly.
allocation can be completely overridden; storage for the slots can
Setting the allocation of each effective slot definition to :dynamic causes the standard
method on allocate-instance not to allocate storage for any slots, resulting in instances
‘that are as small as possible. ‘The actual slot values are stored in a table external to the
instance. Specialized methods on the slot access generic functions access the values from
the table.
A (otciass dynasic-siot-class (etandard-clat
>
A specialized method on coupute-effective-slot-def inition marks all the slots,
_f Woteernod conpute-ottective-slot-definition
(class dynanic-slot-class) direct-slots)
(et ((slot (call-next-nethod)))
slot-definition-allocation slot) ':dynanic)
))
(dofun dynamic-slot-p (slot)
(eq (slot~definition-allocation slot) ':dynamic))
When an instance is created, storage needs to be allocated in the external table; this is
done by a specialized method on allocate-instance
_f (efaornod altocate-instance ((class dynanic-siot-class))
Get (Cinstance (call-next-method) ))
(allocate-table-entry instance)
inatance))
Methods must also be defined on the various slot access generic functions. If the slot
does not exist, ca1-next-nethod is used to invoke the standard method and thereby the
standard behavior for signaling that the slot is missing. (Only slot-value-using-clase
is shown; the other methods are similar.)Extending the Language 103
d defmethod slot-value-using-class (class dynamic-slot-class)
instance slot-~nane)
(let ((slot (find slot-name (class-slots class)
tkey #'slot-definition-name) ))
Gf slot
(read-dynamic-slot-value instance slot-name)
(cal1-next-method))))
‘The dynamic slot value table can be implemented in any
shown here is a hash table keyed on tt
slot values keyed on slot name; unbou
iber of ways; the scheme
stance, The
slots are not expl
Let ((table (make-hash-table :test #'eq)))
(dofun allocate-table-entry (instance)
(setf (gethash instance table) ()))
(defun read-dynamic-slot-value (instance slot-nane)
(let® ((alist (gethash instance table))
(entry (assoc slot-naze alist)))
Gf (ull entry)
(error "The slot “S is unbound in the object “S."
slot-name instance))
(car entry)))
(defun write-dynamic-slot-value (new-value instance slot-nane)
(et* ((alist (gethash instance table))
(entry (assoc slot-nane alist)))
(it Gaull entry)
(push ‘(,slot-name . ,nev-value)
(gothash instance table))
(sett (car entry) new-value))
ney-value))
this scheme can provent the garbage collet
sich ita i modern Common Lisp impletn
“aclities which can be used to address this problem,
stances since the table will
fons provide “wale pointersChapter 3
A. (detun dynanic-nlot-boundp (instance slot-nane)
Gets ((alist (gethash instance table))
(entry (assoc slot-nane alist)))
(aot (aul entry))))
slot-name)
sntry alist))))
instance)
)
In essence, this extension has usurped the standard slot allocation behavior, and moved
slot storage outside of the instances. A similar approach was used in [Paepcke 90] to
extend CLOS to store instances in an object-oriented database
Exercise 3.4 As implemented, dynamic-slot~class marks all of its slots as being dynamic.
In some situations, this extension would be more useful ifit were possible to mark only selected.
slots as dynamic
Modify the implementation of dynamic-slot-class so that dynamic allocation can be
selected on a per-slot basis. For example, in the definition
(dofclass novable-rectangle (rectangle)
C(previous-height :allocation :dynamic)
(previous-width :allocation :dynamic))
(metaclass dynamic-slot-class))
the class has four slots: height, width, previous:
ly would be used in only
‘a small number of instances, should have :4ynamic allocation,
‘Assume that defclase handles the :alecation option, and provides a default value for
it is not providedExtending the Language 105
Exercise 3.5 Another feature of full CLOS which was left out of ot
define slots which are shared across i
to slot-value on any instance of
via one instance, all instances see
(defclass laboled-rectang!
((font :initforn ‘ol:
rallocation :clas:
(metaclass class-slot~cla
(eotq 11 (make-instance ‘labeled-rectangle))
(eetq 112 (nake-instance ‘labeled-rectangle))
=> (slot-value Lt‘ font)
OLD-ENGLISH-12
=> (Glot-value ‘1r2 ‘font)
‘TIMES-ROMAN-10
of a class slot should be evaluated at the time the class is
defined.
Suppose a user wanted both class and dynamic slots together. One appealing way to define
this behavior would be as a subclass of both dynamic-slot-class and class-slot-class.
For example:
Does this produce the desired behavior? If so, why? If not, why not?
3.9 Summary
The evolving met
language.
Ject protocol we are, in effect, giving them
Space represented by the standard language, but also a surrounding region of language
‘sign space. By selecting other points in the region, users ean obtain compatibility with
Other languages, add special features, or tune performance characteristics to meet their
Reeds,106 Chapter 3
As with all other language design, there are a number of basic criteria that govern
metaobject protocol design. We can see now what these are, and how our approach
meets them.
‘* Portability stems from the fact that the metaobject protocols have been designed and
documented in a way that allows it to be supported by all implementations of the
language. When the user creates a language extension, they do so without resorting
to implementation-specific details or hooks.
‘* Object-oriented techniques provide two kinds of control over the effect of any user
extension:
© Scope control is made possi resenting the program as a network of meta
id arranging for tl wr of a metaobject to depend on its class.
rd behavior can be selected in a user-defined class like rectangle with-
out affecting system-defined classes such as standard-object. Moreover, different
parts of the user's program can use different language extensions.
Operation control refers to the way in which, when defining an extension, it is
possible to adjust the behavior of some operations without affecting the behavior of
others. For example, it is possible to change the inheritance rules without affecting
slot access behavior.
‘Together, operation and scope control mean that conceptually orthogonal language
extensions can be naturally composed. For example, we can define a class that both
supports dynamic slots and uses the Flavors inheritance rules by defining a new class
metaobject class that is a subclass of the two classes that implement those two behav-
© Language extensions defined by the user
standard langu
each other and with the
differing only
jon supports a
storage allocation, and other
though the inheritance rul
aspects of the slots may vary.
© Efficiency is a topic about which we e chapter, but already we
can see how it stems from operation control and careful protocol design. For example,
in the class precedence list case, we balanced the protocol so that it gave users the
power they needed while still providing the implementor with basic guarantees on the
behavior of any language ws the implementor to use memoization
+r optimization techniques not only for the standard language, but also for any
user-defined extensions,4 Protocol Design
In practice, designing metaobject protocols is more difficult than the previous chapters
right suggest. Developing a protocol that is simultaneously powerful, easy to use, and
cient involves a number of issues that have not yet been considered. These issues
many of which are common to all object-oriented protocol design—are the focus of this
lustrate some common protocol design techniques, and also point out
‘This chapter continues the evolutionary approach and the use of a staged sequence
But now, given that the basic structure of metaobject protocols has been
established, the approach will shift somewhat. We will start by sketching a simple meta-
ject protocol for controlling the behavior of generic functions and methods. This initial
protocol, however, will suffer from a number of problems. We will use the example-driven
approach to expose these problems, to discuss the underlying issues of metaobject pro-
tocol design, and to incrementally improve the protocol,
4.1 A Simple Generic Function Invocation Protocol
‘The metaobject protocols to be developed in this chapter will operate on generie function
and method metaobjects. AAs with the class-based protocols of the last chapter, users will
need to be able to control the class of the generic function and method metaobjects used
jecialized-generic-function)
iethod-class specialized-method) )
now spet
that the class of the paint generic function metaobject should be
specialized-generic-function and that when defmethod is used to define methods
‘ie function, the resulting method metaobjects should be instances of
cialized-aethod,
‘To support this, defgeneric must pass all the options it receives on to ensure-
generic-function; for example, the expansion of the above form is:
(ensure-generic-function ‘paint
Lambda-List '(x)
eric-function-class (find-class ' specialized-generic-function)
thod-class (find-class 'specialized-method))108 Chapter 4
‘After providing reasonable default values for the :generic-function-class and
possible for users to write
often, tools are required to
imple performance analysis
rogram where performance is critical by counting,
how often specific generic functions and methods are called.
Tn Closette, the relevant backstage architecture is modeled by these five functions
(indentation reflects the calling relationship from main to subfunctions):
function
icable-methods-using-classes
specific-p
apply-methods
apply-method
The events we would like to count correspond directly to apply-generic~function and
apply-nethod. By making these functions generic, we can give the user the functionality
required to support the counting behavior.
(apply-generic-function
(apply-method (method)
a list of next methods.
(args)) Applies a generic function to arguments.
‘nezt-methods)) Applies a method to arguments and
ethod) is unchanged from the code in the bodies
generic-function and apply-nethod (pages 40 an
original definitions of apply
, respectively)
4.1.2 Example: Counting Invocations
Given this new metaobject protocol, the user code for counting generic function calls
is straightforward. A specialized generic function class and a specialized method om
apply-generic-function are all that is required:Protocol Design 109
(defclass counting-gf (standard-generic-function)
((call-count :initform 0 :accessor call-count)))
(defmethod apply-generic-function :before
((ge counting-gt) args)
Gnef (call-count gf)))
Counting method calls is done similarly:
{ (detclass counting-method (standard-method)
((call-count :initform 0 :accessor call-count)))
(defmethod apply-method :before
((method counting-method) args next-methods)
(Gincf (call-count method)))
‘The behavior of this extension can be seen in the following example:
(detgeneric ack (x)
(generic-function-class counting-gf)
(method-class counting-method) )
(defmethod ack :before ((x standard-object)) nil)
(defmethod ack (x) t)
=> (ack (make-instance ‘standard-object))
T
= (ack 1)
T
=> (call-count #'ack)
2
=> (napcar #* (lambda (method)
(ist (generate-defmethod method)
(call-count method)))
(generic-function-methods #'ack))
(CCDEFMETHOD ACK :BEFORE ((X STANDARD-OBJECT))) 1)
C(DEFMETHOD ACK (X)) 2))10 Chapter 4
Exercise 4.1 Show how the same basic approach can be wsed to make generic functions that
can be traced. For example,
generic foo (...)
generic-function-class traceable-gf))
should indicate that £00 is traceable, and
(erace-generic-function ‘foo t)
should turn tracing on. Subsequent calls to foo should cause
id the eventual return value—to be
wuld be turned off by:
ion about the call—
10 the stream *trace-
output
(trace-generic-function ‘foo ail)
4.2 Functional and Procedural Protocols
‘The approach to counting invocations illustrated above is more convenient than the
traditional scheme of adding extra statements to method bodies. This is an example of
how, by providing a second organizational structure through which to affect the beh
of the program, the metaobject protocol makes it possible to say in one place what woul
otherwise have to be spread out among a number of places.
Because these counters are intended to paint an accurate picture of the dynamic char-
acteristics of the program, we must pay careful attention to the specification of app
generic-function and apply-method. In particular, in order to guarantee that #
example code accurately counts the number of times a generic function has been called,
it is essential that the protocol specifica ulate that apply-generic-function be
called each and every time a generic function is invoked. Similar considerations apply to
apply-method and method invocation.
ment contrasts with a property that some of the
protocols developed in the last chapter were designed to allow, namely memoizal
class precedence list was needed. Each subsequent time, the initial value would be reuse
We can make sense of this disparity by distinguishing between procedural and functional
protocols.
In functional protocol, such as compute-class-precedence-list and compute-
lots, a funetion is called to compute a result, then used by other parts ofProtocol Design m
the system to produce the intended behavior. Calling a functional protocol, in other
, but it does not produce that behavior
in turn used by other parts of the system,
This aspect of functional protocols allows us to place two useful limits on their expres-
sive power. First, because the effect is only made visible through the mediation of other
ports of the system, the power is inherently limited by the behavioral repertoire of the
clients ofthe result in question. Second, the specification of functional protocols can im-
persistence of certain basic properties. Furthermore, because these restriction
effect of the context on which functional protocols can depend, they guarantee
that the results can be memoized. The implementor can easily arrange to moni
limited parts of the context that might invalidate any results that have previously been
cached
Procedural protocols, on the other hand, such as finalize-inheritance, slot-
value-using-class, and, of course, apply-generic-function and apply-method, are
called to perform some action—that is, to directly produce some part of the total system
behavior. As a res ion of a procedural protocol tends to place fewer
jon, but more restrictions on exactly when it is
restrictions on the act
call
and is expressed procedu
such protocols tend to put more power in the hands of the user. By the same token,
however, their results cannot generally be memoized. In general this makes sense, since
ing behavior, not producing an answer, is a procedural protocal’s primary purpose,
ction 4.4, however, we will see a way to separate the direct effect and procedural
expressiveness of such protocols, which will allow us to develop a third kind of proto-
, essentially a partially memoi
tain specialized situations.
ible procedural protocol, which will be appropriate in
4.2.1 Document
A
g Generic Functions vs Methods
ral issue in th
ign of object-oriented protocols is whether the specification
be phrased in terms of restrictions on the behavior of generic functions or on the
of their standard methods. The specification of a generic function governs all
'ts methods, system and user-defined alike. The specification of a standard method does
restrict user-defined methods on the generic function. There are times and places for
both,uz Chapter 4
For example, the specification of apply-generic-function states that all ensuing
explicit method invocations use apply-method (our call counting example depends on
this). Since this constraint applies to the generic function as a whole, user-supplied
methods must also obey it even when it might be more convenient not to. This means
that user code that relies on apply-method (such as call counting) can be combined with
user code that specializes apply-generic-function and still work properly.
On the other hand, constraints placed by the protocol specification on standard meth-
ods are weaker, but nevertheless useful. For examy
bound, Because this constraint does not apply to the generic function as @
1e user is free to write specialized methods for slot-value-ueing-class that
sort of corrective action when an unbound slot is accessed.
behavior of the standard method amounts to knowing the unspecialized
generic function. This is the behavior not only of standard metaobjects,
sd metaobjects for which this generic function has not been specialized.
wior produced by call-next-method when it is used to invoke the
standard method.
4.2.2 Overriding the Standard Method
The before- and after-methods of CLOS are especially well-suited to constructing special
izable procedural protocols, because they allow easy placement of additional activit
before or after the activity of the primary method. However, because neither before- nor
is have any say in what other methods run, and because neither can inspect
or affect the result returned by the primary method, they can only be used to supplement
the behavior of the standard method. If it is necessary to override the behavior of the
standard primary method, a special mary method must be used.
‘As an example of a primary method overriding a standard method, consider the fol-
lowing optimistic technique for streamlining generic function invocation. We will assume
that the generic function is always called with valid arguments, that is, there is always
hod. This means that if the generic function has only a single primary
the method lookup process can be elided—the single method can always be
lar, but somewhat more robust technique can be used to produce a CLOS
that is, a compiler which assumes that the program is no longer going
precomputes method lookups wherever possible.
The following class and method definitions implement this “trusting” behavior:
f (ofciane trusting-gf (standard-generic-function) 0)Protocol Design 3
(defaethod apply-generic-function ((gf trusting-gf) args)
(et ((wethods (generic-function-methods gf)))
Gt (and (= Gength methods) 1)
‘imary-method-p (car methods)))
(apply-nethod (car methods) args ())
(cal-next-method))))
Notice that this code does not require trusting generic functions
method to take
replacement
wer, even in the special case, the
that each and every method invo-
cation uses apply-method. We can combine our previous counting code with the new
trusting lookup code and obtain a class of generic function with both behaviors:
d (defclass trusting-counting-gf (trusting-gf counting-gf) ())
This example raises a question however: when should a protocol allow methods—the
to be overridden by more specialized
cs should overriding be prohibited?
he restrictions on the generic
apply-generic~function
is an example where it seems
‘ample, the standard method for #inal.ize-inheritance shouldn't be overridden, since
wrder to leave the implementor room to perform other operations when a class is
activities it carries out are only pat pecified. If a specialized method
prevent the standard method from running, then the standard method
e generic function is called.
Notice that prohibiting the over standard method effectively prohibits all
Method overriding, since each specialized primary method is required to invoke cal1-
Rext-method. Prohibiting overriding thus guarantees that each and every applicable
Primary method will be run each time the generic function is called.?
les a powerful mechanismn
mentation of
ibnation can be used toa Chapter 4
4.2.3 Example: Encapsulated Methods
This example extends encapsulated classes, which we saw earlier in Exercise 3.2, to be
more compatible with Common Objects. Encapsulated classes provide a way to define
the inheritance hierarchy can have
proprietary nature of the slots was reflected in the fact that access to the slot required
name of the slot but also the identity of the class to which it belongs.
languages which use encapsulated classes, it is common to enforce the propric
etary nature of private slots by requiring that access be made only from within the body
of methods defined on their class. An encapsulated method is one that provides this
access mechanism. Within the body of an encapsulated method specialized to an encap-
sulated class, the form (slot ?(slot-name)) can be used to refer to the class’s private
slot named (slot-name). This ex rated in the following example:
(defclass ci ()
(foo :initform 100))
(ometaclass encapsulated-class))
Gefclass 2 (ct)
((foo :initform 200))
(:metaclass encapsulated-class))
(detgeneric f1
generic-function-class encapsulating-gt)
(:method-class encapsulated-method))
(etmethod £1 (
(= (slot fo«
(detmethod £1 ((z €2))
(1+ (slot 'f00)))
»
Within the body of the encapsulated method specialized to ct, the form (slot ’foo)
accesses the slot named foo, private to the class c1, of the argument to £1, On the other
hand, withi body of the
named foo that is private to ¢2.
iethod specialized to 2, the same form accesses the slot
=> (£1 (nake~instance ‘c1))Protocol Design ns
=> (£1 (nake-instance ‘c2))
208
In our earlier example, where encapsulated methods were not available,
methods would have been written:
(defmethod £1 ((y ct))
(1- (private-slot-value y ‘foo (find-class 'e1))))
(defmethod £1 ((z c2))
(1+ (private-slot-value 2 'foo (find-class '¢2))))
‘These examples suggest that slot be defined in terms of the private-slot-value
function mentioned previously. At each slot form, we will need access to two pieces of
information: the class the encapsulated method is specialized to, and the value of the
method's first argument. This access is most by introducing a lexical
definition of stot into the scope of the method's body, in exactly the way call-next-
ethod and next-aethod-p were handled. 1 want to have the same effect
(detmethod £1 ((y ci)
(fet (slot (elot-name)
(private-slot-value y slot-name (find-class 'ct))))
Ge (slot 'f00))))
(defzethod £1 (Cz €2))
(flet (slot (slot-name)
(private-slot-value z slot-name (find-class 'c2))))
(1+ (slot '£00))))
Gotun stor (elot-nane)
(private-elot value sargunent® slot-nane especialize
This npproach cannot be used, however, because inthe presence of functional argument, dyn
‘aanot be reliably subelitted for lexical ones hls fs felated Caos16
Chapter 4
In the current protocol, the only way to in
new I
scope of the body of an encapsulated method is with a specialized,
on apply-nethod. But, because overriding \dard method means taki
ies, the overriding method do a great
‘ion bindings. For the Closette imp!
er needs to be able to write is:
wre than just
new lexical fut entation we have
_f (aetnerod apply-nethod
(method encapoulated-method) args next-methods)
(eval (nethod-body method)
\d-function-bindings
lot (setf slot) call-next-method next-method-p)
Gist
#* lambda (slot~name)
(private-slot-value
(car args)
slot-nane
(car (wethod-specializers method))))
#* (lambda (new-value slot-nane)
(sett (private-slot-value
(car args)
slot-nane
(car (wethod-specializers method)))
new-value))
#" (lambda (krest cnm-args)
(if (gull next-methods)
(error "No next method for the"@
generic-function “S.”
(method-generic-function method))
(apply-methods
(mothod-generic-function method)
(or cnm-args args)
noxt-nethods)))
#* (lambda () (not (null next-nethods))))
(add-variable-bindings (method-lambda-List method)
args
(mothod-environment method)))))u7
for the user to write
Making it possi method places a significant burden on all
wwolved—protocol designer, implementor and user alike.
the protocol designer must specify an enormous amount of detail about the
behavior of the standard method on apply-method: that call-next-method and next-
nethod-p must be introduced; the existence of apply-methods and that call-next-
nethod must use it to properly ‘methods; that lexical function bin«
igs are performed
to actually run the method body.
Its in a corresponding lack of freedom for the implemen-
ist not only provide the required functionality under the re-
also lose the standard method on apply-method as a place to put
to be overridden,
iting the method is a lo
ly, even with the detailed specification,
ser. It certainly seems to be more work
1g two lexical function bindings to the sé
user programs, both of which override the stand
f work for
(extra-function-bindings (method) (arguments) (nezt-methods)) ‘This generic func-
is & specializable functional protocol that returns a list of ((function-name)
Primary methods of the generic function apply-method are
to consult extra-function-bindings to find out what extra lexical function
1g should be added to the scope of a method before executing it. The standard
method on extra-function-bindings may not. over user methods are
free to add whatever new they wish onto the result of method,
‘We implement this new protocol as follows:
Getgeneric extra-tunction-bindings (aethod args next-nethods))ns Chapter 4
The standard method for extra-function-bindings
method and next-nethod-p:
1s entries for call-next-
4 (defmethod extra-function-bindings
((aethod standard-method) args next-nethods)
(ist
Gist ‘cal1-next-aethod
#*Clanbda (érest cnm-args)
Gf (null next-nethods)
(error "No next method for the"@
generic-function “S."
(mothod-generic-function method))
(apply-nethods (aethod-generic-function method)
(or can-args args)
next-nethods))))
(ist ‘mext-method-p
#'Clambda © (not (null next-methods))))))
The standard method for apply-methed must be modified to call extra-function-
bindings:
[]tertstio spy-entod (ortnd stannré-aeted) arg nexe-ethde
Grccactencsinrhndigs ethd args sextant)
cov Gntoaoty sated
Cla fmcioaieing
eopons tear etre tacindings)
Chpcns Went nee fvbodngy
(edcreriaeciniage arn aabiaist ato
i
(Seiodwnicooant ant)Protocol Design ng
n this new protocol, encapsulated methods can be implemented with a specialized,
nat non-overriding, method on extra~function-bindings:
HA (dofnethod extra-function-bindings
((method encapsulated-method) args next-nethods)
(iste
(ist ‘slot
#"Clanbda (slot-nane)
(private-slot-value
(car args)
slot-nane
(car (aethod-specializers method)))))
Gist ' (ett slot
#' (lambda (new-value slot-nane)
(sett (private-slot-value
(car args)
slot-name
(car (method-specializers method) ))
ney-value)))
(call-next-method)))
We have given the procedural protocol apply-nethod a functional subprotocol extra~
function-bindings. By mai
function-bindings, and that the standard “function-bindings not
be overridden ( discarded), we ar le way for an open-ended
set of fncton idings tobe added to & method bodys lexical environmen.
for apply-method call extra
4.3 Layered Protocols
There is a more general tee
we at work here, called protocol layering, based on the
jdea of taking a large, complex portion of the protocol and
‘Wiring that the main generic function call subsidiary generic functions to do parts of the
‘overall task. Then, even though each generic function supports extensibility along only
Single dimensi he combination supports extensibility along m
example, apply-method makes it convenient to add extra activi counting, to
the basic activity of applying a method, while extra-function-bindings makes it easy
‘o introduce extra lexical bindings into the scope of the method body. Another benefit
's that, when necessary, it is easier to override methods on the main generic function120 Chapter 4
because more of the subtasks have corresponding subprotocols. For example, an over-
riding method on apply-method can simply cal
having to write it from scratch,
It is a general characteristic of layered protocols that the upper layers (e.g., apply
method) tend to provide more powerful access than the lower layers (e.g. extra~
function-bindings). With this power comes res yy for a number of things that
may be unrelated to the aspect is to be changed. Lower layer protocols, on the
other hand, tend to be more focused and easier to use.
Protocol layering is a powerful tool for helping protocol designers meet the needs of
their users. Lower-level, highly-focused layers provide easy handles for what we expect
a fallback strategy; they may be more
yut they give the user more complete power.
, we can now revise the
xtra-function-bindings rather than
| to introduce appropriate
jeric-function at the top and apply-method at (or near) the
et, this middle realm breaks cleanly into three tasks: deter-
ig which methods are applicable to given argument classes (compute-applicable-
methods-using-classes), arranging them in decreasing order of specificity (method~
more-specific> the methods in the order dictated by the individual
methods’ qualifiers (apply-methods). Simple protocols for each are as follows (these are
sketches only):
layers between apply
bottom. As we saw ear
(coupute-applicable-methods-using-classes (af) (required-classes)) A functional
protocol that returns the list of met
ist of methods. ‘This function is called by the
standard method of apply-generic~function. 2 non-specializable protocol;
may call mld only specialize method-nore-specific-p.2
(wothod-nore-specific-p (af) (method ) (required-classes)) A fanction-
al protocol that deter is more specific than the second
relative to the given a asses. Methods on this generic function may only
dard method may be overridden,
A procedural protocol specialized on the first
argument that orchestrates the apy (using apply-method) to
the & ents. This generic function is called by the standard method of apply-
generic-function. The standard method may be overridden,
3h
MOP, which provides support
1 spotters, thisProtocol Design 121
In summary, the full set of layered protocols within generic function invocation is:
‘apply-generic-function
compute-appl:
ble-methods-using-classes
method-nore-specific-p
apply-methods
apply-method
extra-function-bindings
functional non-specializable
functional overridable
procedural overridable
procedural overridable
functional
-overridable
4.3.1. Revised Definitions
For convenience, we give the definitions of apply-generic-function, compute-
applicable-methods-using-classes, nethod-more-specific-p, and apply-nethods
corresponding to the current protocol are given in this section. The definitions of apply
method and extra-function~bindings can be found on page 118.
‘ (defgeneric apply-generic-function (gf args))
(defnethod apply-generic-function ((gf standard-generic-function) args)
(et ((opplicable-nethods
(conpute-applicable-nethods-using-classes,
gf (wapcar #'class-of (required-portion gf args)))))
(Gf (mull applicable~aethods)
(error "Wo matching method for the“@
generic function “8,70
vhen called with arguments “:S." gf args)
(apply-nethods gf args applicable-methods))))
(detun compute-applicable-nethods-using-classes (gf required-classes)
(sort
(copy-list
(remove-if-not #' (anbda (nethod)
(every #'subclassp
required-classes
(wethod-specializers method)))
(generic-function-methods gf)))
#* (lambda (mt 22)
(nethod-more-specific-p gf mi m2 required-classes))))
(etgeneric nethod-nore-specific-p
(gf method! method? required-classes))cry Chapter 4
Cit ooo pent tacetcd
cape Gina tego seed real
(anless (eq speci spec2)
Ceaeeciadinr’y see tpt septs)
ee eee
Scoeeeeaaeeiarae
an
(detgeneric apply-methods (gf args methods))
(defmethod apply-methods
((gf standard-generic-function) args methods)
(let ((primaries (remove-if-not #'primary-method-p methods)
(vefores (renove-if-not #'before-nethod-p methods))
(afters (renove-if-not #'after-method-p methods)))
(shen (null primaries)
(error "No primary methods for the"@
generic function “S." gf))
(Gdolist (before befores)
(apply-method before args ()))
(multiple-value-progi
(apply-aethod (car primaries) args (cdr primaries))
(Golist (after (reverse afters)
(apply-method after args
4.3.2 Example: Method Combination
Our CLOS subset includes only a simplified version of the full method combination
facility. Our methods have qualifiers, which indicate how they should be combined, but
‘we have only supported before- and after-methods. Full CLOS also has around-methods
(using the :around qualifier) which have a higher precedence than all applicable primary,
before-, and after-methods, When a generic function is called, the most specific around-
the remaining before-, after- and primary methods as usual123
Using the protocol we have just developed, itis an easy matter to add around-methods
to our CLOS subset.
[totes e-vstesoote (tainsgeeri-tunetin) 0)
(defun around-nethod-p (method)
(equal '(around) (method-qualifiers method)))
(defmethod apply-methods ((gf gf-vith-arounds) args methods)
(let ((around (find-if #*around-method-p aethods)))
Gf around
(apply-method around args (remove around methods))
(call-next-method))))
The full CLOS method combination facility also supports a wide variety of special-
purpose ways of combining primary methods. For instance, it is sometimes more useful
to treat each primary method as a supplier of a portion of the resu invoke all
appl ary methods, appending together their results. Although the same effect
can be achieved using normal primary methods with bodies like
append (list) (if (next-method-p) (cal1-next-aethod) ()))
fan extension can be defined which better localizes this behavior. Generic
append combination support only primary methods. When they
applicable methods and the results are appended toget
, all the
A eotciaes gtvitiappnt (etandard-geueri-tection) 0)
(defnethod apply-nethods ((gf gf-with-append) args methods)
(sapappend #' (lambda (method)
(apply-method method args ()))
nethods))
‘The function extra-funct ion-bindings is a ready example of where this form of method
combination could have been used to gather together lexical function bindings from the
Vatious specialized methods.124 Chapter 4
Exercise 4.2 Unlike most other object-oriented languages, CLOS supports 1
which are methods specialized in more than one argument posi
When there is only a single specialized argument position, two applicable methods can be
directly ordered by comparing their speciali
le specialized argument positions,
‘a more complex ordering scheme is needed. By default, CLOS uses left-to-right lexicographic
e., based on the specializer ordering of the leftmost argument
yn-class apo-gf)
jence-order (stream object)))
s of ordering the appli
fon should be considered before the firs.
Exercise 4.3 In the Bota proj
ble methods is handled different
before methods
1e method from the highest superclass. Within
ilar to a call to call-next-method except that it
jod, inner accepts no arguments,
and if called when there are no more methods, returns false rather than si
Define specialized generic function and method metaobject classes which invoke methods
js order. Also implement inner so that it has the specifi wior. (You need only
support primary methods.)
Exercise 4.4 We have now seen how several features of full CLO’
subset, can be added back using the metaobject protocol (defaul arguments,
around-methods, other kinds of method combinations, and argument precedence order), This
‘of a metaobject protocol might affect the design
of the standard language. In particular, can metanbject protocols be used to make language
left out of our
fh you are familiar, and look for features which,
given an appropriate metanbject protocol, need not be part of the standard language. What
criteria are you using to make your decisions? One ad is for certain features
to be included as part of a library of specialized metaobj features would you
ptt into such a library?Protocol Design 125
4.4 Improving Performance
As we have designed it, the generic funet col severely impairs the
inction invocation. The root of the problem.
be called during each genei
ies of generic function invoe
of the relevant protocols. The same basic techni
places: apply-generic-function, apply-nethod, and apply-nethods
4.4.1 Effective Method Functions
Consider apply-aethods, calls to which lie directly along the execution path of generic
function invocation (it is called from apply-generic-function and from call-next-
means that the performance of generic
formance of apply-nethods; if apply-methods
cannot help but be slow.
ynal language implementations, the implementor would not be constrained to
implement apply-methods literally. If it expensive to call apply-methods, the
implementation could be rewritten to dispense with it, and do things some faster way.
is not a traditional language; apply-methods is a procedural generic function in
ct protocol. The implementor is not free to dispense with calls
is procedural protoc:
be to redesign the proto
to it. Moreover, sine
Our solution
into two parts, O:
and will remain on the direct execution path. Havi
will then be able to improve performance by moving as much of the activity as possible
‘out of the second part and into the first.
Notice that the (args) argument to apply-methods is simply passed along to apply-
Rethod} it is not used in deciding the order in which the methods are invoked. ‘This
‘means, in effect, that the work being done by apply-methods is actually a function of
only the (generic-function) and (methods) arguments. It is a simple matter to rewrite
apply-nethod as a two-ar t returns a function as a result. We call
‘these functional results effective method functions, and the function that computes them
compute-ef fective-method-function. We then rewrite calls of the form:
‘idle argument
nal programming technique of currying, applied to the126 Chapter 4
Capply-methods (af) (args) (methods))
(tuncal1 (compute-ettective-method-function (gf) (methods))
{args))
‘The standard method for this new generic function can be derived directly from the
standard method for apply-methods (p. 122):
A Gefgeneric conpute-effective-method-function (gf nethods))
(defnethod compute-effective-method-function
(gt standard-generic-function) methods)
#'Cambda (args)
Get (primaries (renove-if-not #'primary-method-p methods))
(vefores (renove-if-not #'before-method-p methods))
(renove-if-not #'after-nethod-p methods)))
(when (null primaries)
(error "No primary methods for the"@
generic function “S." gf))
(olist (before befores)
(apply-aethod before args ())
(aultiple-value-progi
(apply-method (car primaries) args (cdr prinaries))
(dolist (after (reverse aft
(apply-method after args (
>
»»»)
By constraining the context conpute-eftective-method-function may use in com-
smoizable.® Given a generic
nplementation must call the
tion has previously been com-
puted for that generic function and list of methods, the implementation is free to reuse
it.
Separating the original behavior of apply-method into a memoizable part and a part
that is on the direct execution path provides the opportunity for performance improve-
ment. To take advantage of it, we must actually move as many activities as possible out
of the critical path. Looking at the above method, we can see that there is no reason
to sift through the list of applicable methods every time the effective method function is
its are somewhat more complex. Redefnition of
I MOP, the corcsponding contextual
ny od function be discarded
ction requires that any memoized tfcProtocol Design wr
called; instead, it can be done in advance. The check to ensure that there is at least one
primary method can also be done in advance. This can be expressed by rearranging the
above method, pulling code from inside the Lambda to outside it:
(defmethod compute-effective-method-function
((gt standard-generic-function) methods)
-not #'primary-method-p methods))
wve-ig-not #'after-method-p methods))))
(nen Gaull prisaries)
(error "Wo primary methods for the"@
generic function "8." gf))
#*Canbda (args)
Golist (before befores)
(apply-method before args ())
(aultiple-value-progt
Capply-nethod
(car primaries) args (cdr prinaries))
(aolist (after reverse-after:
(apply-nethod after args (
4.4.2 Method Functions
‘The above technique can also be profitably applied to apply-method. Instead of mandat-
ing that the generic function apply-method be invoked every time a method is applied,
replaced by a functional protocol, conpute-method-function,
iat computes a method function which will be whenever the invoked.
je re-express:
(apply-method
wethod) (args) (next-methods))
a
(funcal1 (compute-method-function (method))
1798)
(next-methods))128 Chapter 4
In this case, the opportunity for memoization is straightforward. The method func
tions, being function of only the method, can be computed once and stored with the
method metaobject.®
tuse a before-method as we did with apply-m«
next-method is first used to obtain the standard m
function is returned which first increments the call count, and then calls the standard
‘method function.
(defmethod compute-method-function (method counting-method))
(let ((normal-aethod-function (call-next-method) ))
#' (lambda (args next-methods)
Gincf (call-count method))
(funeall normal-ethod-function args next-methods))))
4.4.3 Discriminating Functions
Finally, this techni
lied to apply-generic-function. Again,
procedural prot protocol that returns a function which will itself be
called whenever the generic function is invoked. These functions are called discriminating
functions because their primary task is to discriminate on the arguments to determine
which methods are to be applied. The generic function that computes them is called
coupute-discriminating-function, We re-express the root call:
(apply-generic-function (gf) (args))
(apply (compute-discriminating-function (9f)) (args)
‘The standard method on this new generic function returns a function that does what
the standard method on apply-generic-function (p. 1Protocol Design 129
Fy (defgeneric conpute-discriminating-function (gf))
(Gefmethod compute-discriminating-function
(gf standard-generic-function))
#'Canbda (rest args)
et ((applicable-methods
(conpute-applicable-nethods-using-classes
st
(aapcar #'class-of
(required-portion gf args)))))
(4f (null applicable-methods)
(error "No matching method for the"@
generic function “8,70
vhon called vith arguments ~:S." gf args)
(apply-nethods gf args applicable-methods)))))
‘To make this protocol memoizable, we restrict compute-discriminating-function
to depend only on the class of the generic function and its set of methods. Whenever a
method is added to the generic function, a new discriminating function must be computed
and stored. Since discriminating functions accept the same arguments as the generic
function itself, the implementation is free to arrange for calls to a generic function to go
straight to its discriminating function
‘Again, call-counting in this new framework can be done quite simply:
_ftatarnt cogat-diacrininaing-tenction gt comting gD)
Gio (eoneacafen (cs-net-athed))
daanee Greet sg
Gncf (call-count gf))
Ceply vans att ange)?
4.4.4 Streamlined Generic Function Invocation
‘We now present, from the perspective of the implementor, a more in-depth look at the
freedom this revised protocol offers. We will show one way to take advantage of this
timization strategy is based on memoization, as allowed by the protocol, of
jethod functions and discriminating functions.
is done when the generic function metaobject,
arrange for all calls to the130 Chapter 4
Memoization of method functions is done similarly. When the method metaobject
is initialized, compute-nethod-function is called and the resulting method function is,
saved and used for all subsequent calls to the method.
‘The more complex case is memoization of effective method functions. This is done
on a per generic function basis, using a table which maps from classes of arguments to
effective method functions (along the lines of Exercise 1.1).
‘The “fast path” sequence of activities for a call to a generic function, from the time
the discriminating function is entered, is: get the classes of the arguments, use them a8
y table, and apply the effective
rhe arguments. In this way, the discrimination overhead is reduced
ing the classes of the arguments, performing the table lookup and
the effective method function.
lower path is taken when an effect
table; this involves finding out what methods are appl
and then computing an effective method function for that list of applicabl
long this route that compute-applicable-methods-using-classes, method-
\cific-p, and coupute-effective-method-function are called.
implementation involves replacing the previous method on coapute-
discriminating-function
Ff teetsetnod conpute-discrininating-function
((gt standard-generic-function))
Get ((classes-to-enf-table (nake-hash-table :test #'equal)))
#'Clanbda (erest args)
Got+ (classes (napcar #'class-of
(required-portion gf args)))
Cenfun (gethash classes classes-to-onf-table nil)))
(it eatun
(funcall entun args)
(slow-nethod-Lookup gf args classes classes-to-enf-table))))))131
A] (detun slos-nethod-2o0kup
(gt args classes clacses-to-enf-table)
pplicable-methods
(compute-applicable-nethods-using-classes gf classes))
Contun
(conpute-ef fective-method-function
gt applicable-methods)))
(eott (gothash classes classes-to-enf-table) enfun)
(funcall eafun args)))
ete
‘A number of issues remain to fully optimize generic function invocation. First, there
are a number of calls to apply-methods, which we have not yet converted into mem:
calls to compute-effective-method-function. These are all related to call-next
aethod; they include the call from he body of call-next-method itself and the
call from within the body of effective method functions. (The code in Appendix D reflects
these further optimizations.)
Another important optimization is that table lookups should be bused only on the
arguments which are actually specialized instead of on all required arguments. A related
issue is the design of the table data structures and the hashing strategy used within them.
‘we have not discussed these, the discussion above is indicative of the asic structure
‘of an optimized implementation strategy; similar approaches have proven effective in
several full CLOS implementations (Dussud 89, Moon 86, KiczalesRodriguer. 90}
4.5 Protocol Design Summary
* There are two broad categories of object-oriented protocols: functional and procedural.
Functional protocols can be designed to make memoization possible while allowing the
Uuser to control what answer is computed. On the other hand,
overridden are more flexible, but
less predictable, than ones that allow new activities to augment existing ones but not
to replace them.132 Chapter 4
s are used to make more sweeping changes, but are inevitably
more complex. On the other hand, by being defined at a higher level, they generally
avoid having to deal with details of concern to the lower layers.
Procedural protocols that lie along the execution critical path can be pulled apart into
a functional protocol that computes @ procedure. The procedure gets called on the
critical path; computing the procedure itself can either be done once and cached, or
moved off the critical path entirely by precomputing it.
se 4.5 We began the chapter with a simple metaobject protocol for co
funetion invocation: the generic functions apply-generic-function and apply:
Now, these have been replaced by compute-discriminating-function and compute-
schod-function.
‘One way to view the original protocol is to see tha
sneric functions were direct
From this same perspective,
i functional results which
fates cnptetiserinioning beh (et cosine e)
Get Cntat eee eaitent ante
“atten rant ses
Get (eaten)
Cpl Harmar)
Design and implement the rest of this protocol in terms of the protocol we have already
developed. Evaluate your design by considering how it supports each of the examples we
have seen,II A METAOBJECT PROTOCOL FOR CLOS135,
In this part of the book, we provide the detailed specification of a metaobject protocol
for CLOS. Our work with this protocol has always been rooted in our own implementation
of CLOS, PCL. This has made it possible for us to have a user community, which in turn
has provided us with feedback on this protocol as it has evolved. As a result, much of the
design presented here is well-tested and stable. As this is being written, those parts have
been implemented not only in PCL, but in at least three other CLOS implementations
wwe know of. Other parts of the protocol, even though they have been implemented in
ms, are less well worked out. Work
remains to improve not only the ease of use of these protocols, but also the balance they
provide between user exter
paring this specification, it is our hope that it the users
1d implementors who wish to work with @ metaobject protocol for CLOS. This docu-
ment should not be construed as any sort of final word or standard, but rather only as
ymentation of what has been done so far. We look forward to seeing the improve-
(chapters 5 and 6), we grant permission to prepare revisions
or other derivative works including any amount of the original text. We ask only that
you properly acknowledge the source of the original text and explicitly allow subsequent,
revisions and derivative works under the same terms. To further facilitate improvements
is work, we have made the electronic source for these chapters publicly available; it
can be accessed by anonymous FTP from the /pcl/mop directory on arisia.xerox.com.
le feedback from users of PCL, the protocol design presented
here has benefited from detailed comments and suggestions by the fol
Kim Barrett, Scott Cyphers, Harley Davis, Patrick Dussud, John
|, David Gray, David A. Moon, Andreas Paepcke, Chris
van Roggen, and Jon L, White, We are very grateful to each of them. Any remai
nnsisteneies or design deficiencies are the responsibility of the authors alone.5 Concepts
5.1 Introduction
‘The CLOS Specification [X3J13, CL
for the Common Lisp Object Syst
by defining a metaobject. protocol fr
‘an extensible CLOS program. In this
jescribes the standard Programmer Interface
(CLOS). This document extends that specification
OS—that is, a description of CLOS itself as
jescription, the fundamental elements of CLOS
grams (classes, slot definitions, generic functions, methods, specializers and method
wns) are represented by first-class objects, The behavior of CLOS is provided
ts, or, more precisely, by methods specialized to the classes of these objects.
Because these objects represent of CLOS programs, and because their behavior
provides the behavior of the CLOS language itself, they are considered meta-level objects
‘or metaobjects. The protocol followed by the metaobjects to provide the behavior of
ogtam element the
class, slot-defi
A metaobject clas
res fined if an attempt
than one basic metaobject class. A metaobject is an instance of a metaobject class.
jon required to serve its role.
fa user interface macro such as defelass or defmethod. It also
metaobjects. T
based on that of other metaobjects. As
this section presents a partial enumerati
each More detailed informal
of a metaobject is always
is interconnected structur
yrmation associated with
is presented later.
5.2.1 Classes
A
n
‘metaobject determines the st
lowing information is associate
land the default behavior of its instances.
ith class metaobjects:as Chapter 5
© The name, if there is one, is available as an object.
© The direct subclasses, direct superclasses and class precedence list are available as lists
of class metaobjects.
© The slots defined direct!
metaobjects.
e class are available as a list of direct slot definition
slots which are accessible in instances of the class are available as a
metaobjects
le as a string or nil.
‘* The methods which use the class as a specializer, and the generic functions associated
with those methods are available as lists of method and generic fun¢
respectively.
© The documentation is avs
5.2.2 Slot Definitions
slot specifiers found in defelass fo
represent information, inch
in instances of a pau
Associated with each class metaob ject is a list of direct slot defi metaobjects rep-
resenting the slots defined directly in the class. Also associated with each class met:
list of effective slot definition m of slots accessible in
ion metaobject is used to
inherited information, about a slot accessible
ect
ive slot definitions
form,
definition metaobjects.
slot in the class (it is not
inherited),
‘The function names
crated reader
ich there are
mation is available as
ff those generic functions
sr methods. ThisConcepts 139
names. Any accessors defelass
alent readers and writers in the direct slot defi
are broken down into their equiv-
Information, including inherited information, which applies to the definition ofa slot in
a particular class in which itis accessible is associated only with effective slot definition
metaobjects.
« For certain slots, the location of the slot in instances of the class is available.
5.2.3 Generic Functions
eric function metaobject contains information about @ generic function over and
fh each of the generic function's methods.
‘¢ The name is available as a function name.
methods associated with the generic function are available as a list of method
metaobjects.
© The default class for this generic function’s method metaobjects is available as a class
metanbject.
© The documentation is available as a string or nil.
The argument precedence order is available as a permutation of those symbols from
the lambda list which name the required arguments of the generic function,
© The declarations are available as a list of declarations.
‘Terminology Note:
‘There is some ambi
yy in Com about the terms used to identify the
i parts of declare special forms. In this document, the term declaration is
used to refer to an object that could be an argument to a declare special form. For
example, in (declare (special *g1*)), the list (special *g1*) is
‘declaration
5.2.4 Methods
‘A method metaobject contains information about a specific method.
© The qualifiers are available as a list of of non-null atoms,
© The speci
* The function is available as a function. This function can be applied to arguments and
a list of next methods using apply or funcall,°Wh
ject is available. A
time.
# The documentation is available as a string or nil
specializers of a method. Class metaobjects are
id for
5.2.6 Method Combinations
[A method combination metaobject represents the information about the method
nation being used by a generic
structure of method combination metaob jects.
5.3. Inheritance Structure of Metaobject Classes
tance stru
rach class marked with a “+” is an abstra
defined ifan attempt is made
of the specified metaobject classes is shown in Table 5.1
and is not intended to be instantiated.
make an instance of one of these classes
standard-class, standard-direct-slot-definition, standard-effective-
slot-definition, standard-method, standard-reader-method, standard-writer-
method and standard-generic-function are called standard metaobject classes. For
ach kind of metaobject, this is the class the user interface macros presente CLOS
Specification use by default. These are also the classes on which user spe
ass, funcallablo-standard-class and forward-referenced-
jose class metaobject classes. Built-in classes are instances of the
je-standard-class provides a special kind of
1a8 not yet been defined, of forward-
tually
Jass of the class standard-Concepts
Metaobject Class
standard-object
funcallable-standard-ob ject
+ metaobject
+ generic-function
standard-generic-function
+ method
standard-method
+ standard-accessor-method
definition
standard-effective-slot-
defi
+ special
eql-specializer
class
built-in-class
forward-referenced-class
standard-class
Direct Superclasses
mu
(t)
(standard-object function)
(standard-object)
(metaobject
funcallable-standard-ob ject)
(generic-function)
(metaobject)
(method)
(standard-method)
(standard-accessor-method)
(standard-accessor-me'
(metaobject)
(metaobject)
(standard-slot-definition
offective-slot-definition)
(class)
funcallable-standard-class
maships among the spwe Chapter 5
behavior associated with the class standard-object will be inherited, directly or indi-
rectly, by all instances of the class standard-class. A subclass of standard-class may
have a different class as its default direct superclass, but thet class must be a subclass
of the class standard-object.
‘The same is true for funcallable-standard-class and funcallable-standard-object.
‘The class speci
or captures only the most basic behavior of method specializers,
specializer reflecting the property that classes by themselves can be used as method
specializers. The class eql-specializer is used for eql specializers.
5.3.1 Implement:
ion and User Specialization
‘The purpose of the Metaobject Protocol is to provide users with a powerful mechanism
for extending and cus ing the basic behavior of the Common Lisp Object System.
As an object-oriented description of the basic CLOS behavior, the Metaobject Protocol
and implementor freedom is mediated by placing explicit,
se restrictions are general—they apply to the entire class graph and
the applicability of all methods. These are presented in this section.
‘The following additional terminology is used to present these restrictions:
‘© Metaobjects are divided into three categories. Those defined in this document are
specified; those -mentation but not mentioned in this docu-
are called implement and those defined by a portable program are
called portable.
A class I is interposed between two other classes Ci, and Cy if and o
+ superclasses, from the
ed to a class if and only
associated with the method; and the method is in the
some generic function.
« Ina given implementation, a specified method is said to have been promoted if and
So, ate defined in this specification as the
ne or more of the specializers S,, is
list of specializers
ist of methods associated with
superclass of the class given in the specification
For a given generic function and set of arguments, a method Mz extends a method My
if and only ifConcepts 43
M, and My are both associated with the given generic funetion,
‘M, and M; are both applicable to the given arguments,
the specializers and qualifiers of the methods are such that when the generic
function is called, M, is executed before M,,
‘M, will be executed if and only if call-next-method is invoked from withi
the body of Mz and
(v) call-next-method is invoked from within the body of Mz, thereby causing Mi
to be executed,
«¢ For a given generic function and set of arguments, a method Mz overrides a method
M, if and only if conditions i through iv above hold and
(v) call-next-method
venting M; from being
invoked from within the body of Mz, thereby pre-
ed.
Restrictions on Implementations Implementations are allowed latitude to mod-
ify the structure of specified classes and methods. ‘This includes: the interposit
implementation-specific classes; the promotion of specified methods
of two or more specified methods into a single method specialized to interposed
Any such modifications are permitted only s0 lo
is a subclass of one or more specified classes Cp.
mn of
as for any portable class Cp that
the following conditions are met:
scedence list of Cp, the classes Cy... C; must appear in the
order as they wor fic modifications had been made.
‘+ The method applicability of any specified generic function must be the same in terms
of behavior as it would have been had no implementation-specific changes been made.
1 les specified generic functions that have had portable methods added. In
jext, the expression “the same in terms of behavi
the same behavior as those specified are
means that methods with
specified class, any slot for which the name is a symbol accessible in the
lisp-user package or exported by any package defined in the ANSI Cor
entation-specific before- and after-methods
specified generic functions, Implementations are also free to define implementation-
Restrictions on Portable Programs Portable programs are allowed to define sub-
classes of specified classes, and are permitted to define methods on specified generica Chapter
functions, with the following restrictions. The results are undefined if any of these re-
or method com!
generic fu it have at least one specializer that is neither a specified class nor
‘an eql specializer whose associated value is an instance of a specified class.
Portable programs may define methods that extend specified methods unless the de-
scription of the specified method explicitly prohibits this. Unless there is a specific
statement to the contrary, these extending methods must return whatever value was
returned method,
* Portable programs may define n override specified methods only when the
mn of the specified method ex allows this. od is
related methods 10 be overti
this is the specified
ent, remove-dependent a
.e generic functions add-dey
map-dependents. Overriding a specified m
one of these generic functions requires that the corresponding method on the other
len as well
mn specified generic functions
J to portable metaobject
must be defined before any instances of those classes (or any subclasses) are
sd, either directly or indirectly by a call to make-instance. Methods can be de-
ined afte by allocate-instance however. Portable metaobject
classes cannot be redef
Implementation No
pose of this last restriction is to permit i
lementations to provide perfor-
istance of a metaobject
it possible to optimize calls to those specified generic functions which would have
no applicable portable methods.
Note:
have found it difficult to give specificatio 'y descriptions of the protocols in
‘makes it clear what extensions users can and cannot write, Object-
‘specification is inherently about specifying leeway, and this seems
irrent technology.Concepts M5
5.4 Processing of the User Interface Macros
All he first element is one of the symbols defelass, defmethod, def-
generic, define-method-combination, generic-funetion, generic-flet or generic
labels, and which has proper syntax for that macro is called a user interface macro
form vument provides an extended specification of the defelass, defmethod
and defgeneric macros.
‘The user interface macros defelass, defgeneric and defmethod can be used not only
to define metaobjects that are instances of the corresponding standard metaobject class,
but also to define metaobjects that are instances of appropriate portable metaobject
classes. To make it possible for portable
information appearing in the macro form, t
of the processing of these macro forms
User interface macro forms can be evaluated or compiled and later executed. The effect
of evaluating or executing a user interface macro form is specified in terms of calls to
specified funetions and gene
‘The arguments received by these functions and generie
way from the macro form.
aobject classes to properly process the
document provides a limited speci
wns are derived in a specified
Converting a user interface macro form into the arguments to the appropriate functions
and generic functions has two major aspects: the conver
tax into a form more suitable foi
syne
processing, and the processing of macro arguments,
icluding method bodies).
acto, the initform and default-initarg-
ye evaluated one or more times after t
evaluated or executed. Special processing must be done on these arguments to ensure
that the lexical scope of the forms is captured properly. Thi
function of zero arguments which, when called,
proper lexical envi
e syntax of the defmethod macro the form* argument is a list of forms that
rise the body of the method definition. This list of forms must be processed spe-
ally to capture the lexical scope of the macro form. In addition, the lexical functions
body of methods must be introduced. To allow this and any other
ecializable protocol is used for
re forms to be evaluates
In the syntax of the defelass
arguments are forms which146 Chapter 5
5.4.1 Compile-file Processing of the User Interface Macros
It is common pr
maint:
ice for Common Lisp compilers, whil afile or set of files, to
so far. Among other
which appears in a
about the state of the
called during compile-file processing of a user interface macro form is not specified. Im-
plementations are free to define and document their own behavior. Users may need to
check implementation-specific behavior before attempting to compile certain portable
programs.
5.4.2 The defelass Macro
The evaluation or execution of a defelass form results in a call to the ensure-class
function, The arguments received by ensure-class are derived from the defclass form
in a defined way. The exact macro-expansion of the defelass fo ly the
relationship between the arguments to the defelass macro and the arguments received by
the ensure-class function. Examples of typical defelass forms and sample expansions
are shown in Figures 5.1 and
© The name argument to defelass becomes the value of the first argument to ensure~
class. This is the only positional argument accepted by ensure-class; all other argu-
ments are keyword arguments.
1 direct-superclasses argument to defelass becomes the value of the :direct-
superclasses keyword argu o ensure-class.
© The direct slots argument to defclass becomes the value of the :direct-slots keyword
argument: to ensure-class. Special processing of this value is done to regularize the
form of each slot specification and to properly capture the lexical scope of the initial-
is done by converting each slot specification to a property list called
ecifcation. The resulting list of canonicalized slot specifications
irect-slots keyword argument.Concepts ur
(defclass plane (moving-object graphics-object)
initform 0 :accessor plane-altitude)
(:default~initarge :engine *jet+))
(ensire-class ‘plane
‘:direct-superclasses '(moving-object graphics-object)
‘idirect-slots (list (list ‘inane ‘altitude
eaders ' (plane-altitude)
s '((setf plane-altituc
‘:direct-default~initargs (list (List ':engine
hejere
#* Cambda © *jet*))))
and class options and an expansion of ft that
form slot option is present in the slot specification, then bath the
itfunction properties are present in the canonicalized slot spec-
‘The value of the sinitform property is the initialization form. The value
function property is a fu of zero arguments which, when called,
ralue false. In such cases, the
‘tform property, or whether it appears, is unspecified.
‘initargs property is a list of the values of each
then either the
be the empty list.
3
a
B
&
itarg slot
args property will
option, If there are n
not appear or its value8 Chapter 5
(defclass sst (plane)
((aach mag-step 2
locator sst-nach
locator mach-location
eader mach-speed
eader mach))
(smetaclass faster-class)
(another-option foo bar))
(ensure-class ‘sst
@irect-superclasses '(plane)
:direct-slots (List (list ‘:ame ‘mach
readers '(mach-speed mach)
‘mag-step
“locator '(sst-mach mach-Location)))
‘imetaclass 'faster-class
‘ancther-option '(foo bar))
Figure 5.2 A defelass form with non-standard class and slot options, and an expansion of
which results in the proper eall to ensure-elass. Note that the order of the slot options has not
affected the order of the properties in the canonicalized slot specification, but has affected the
order of the elements in the lists which are the values of those properties.
‘© The value of the :readers property is a list of the values of each :reader and
.ccessor slot option. If there are no :reader or :accessor slot 0} then either
fers property is a list of the values specified by each :writer
-cessor slot option. The value specified by a
of the slot option. ‘The value specified by a
option is just the
saccessor slot option is a two
ent is the value of
then either the
rriters property will not appear or its value
© The value of the :documentation property is the value of the :documentation slot
no :documentation s!
Il not appear or its value
t options appear as the v
Note that this includes not only the remaining standard sl
jon and :type), but also any other options and values appearing in theConcepts 9
slot specification. If one of these slot options appears more than once, the value of
the property will be a list of the specified values,
‘¢ An implementation is free to add additional properties to the canonicalized slot
specification provided these are not symbols accessible in the common-lisp-user
package, or exported by any package defined in the ANSI Common Lisp standard.
Returning to the correspondence between arguments to the defelass macro and the
arguments received by the ensure-class function:
lexical scope of the default
in the class option into a
canonicalized default initarg, od default initargs is the
value of the :direct-default~ to ensure-class.
itarg is a list of three elements. The first element is the
he actual form itself; and the thied is a function of zero arguments
ns the result of evaluating the default value form in its proper
the defelass form, becomes t
lass,
value of
defelass form, becomes the
documentation keyword argument to ensure-class.
ue of the keyword argument is the tail of the class option. An error is signaled
if any class option appears more than the defclass form,
every clement ofits arguments appears in the same left-to-
rig) element of the defclass form, except that the order of
the properties of canonicalized slot specifications is unspecified. ‘The values of properties
in eanor
alized slot specifications do follow this ordering requirement. Other ordering
relationships in the keyword arg to ensure-class are unspecified
The result of the call to ensure-class is returned as the result of evaluating or exe-
ccuting the defelass form.
5.4.3 The defmethod Macro
ion or execution of a defmethod form requires first that the body of the
‘thod be converted to a method fi ‘This process is described in the next section.
The result of this process is a function and a set of additional initialization150 Chapter 5
arguments to be used when creating the new method. Given these two values, the
evaluation or execution of a defmethod form proceeds in three steps.
‘The first step ensures the existence of a generic function with the specified name, This
is done by calling the function ensure-generic-function. The first argument in this
call is the generic function name specified in the defmethod form.
‘The second step is the creation of the new method metaobject by ci
instance. The class of the new method metaobject is determined by
function-meth
first step.
The init
ing make-
ling generic
1ass on the result of the call to ensure-generic-function from the
ization arguments received by the call to make-instance are as follows:
© The value of the :qualifiers initialization argument is a list of the qualifiers which
appeared in the defmethod form. No special processing is done on these values. The
list is the same as in the defmethod form.
ization argument is the unspecialized lambda list
from the defmethod form.
‘© The value of
specializers i
the method. For specializers
lization argument is a list of the specializers for
are classes, the specializer is the class metaobject,
itself. In the case of eql specializers, it will be an eql-specializer metaobject obtained
by calling intern-eql-specializer on the result of evaluating the eql speci
lexical environment of the defmethod form.
e value of the :declaré
the defmethed form,
argument either doesn’t appear, or appears with a value of the empty list
‘The value of the :documentation initialization argument is the documentation string
from the defmethod form. If there is no documentation string in the macro form this
initialization argument either doesn’t appear, or appears with a value of false.
Any other initialization argument produced in conjunction with the method function
are also included.
The implementation is free
these are not symbols accessible in the common-
any package defined in
ation arguments provided
spruser package, or exported by
ANSI Common Lisp standard
In the third step, add-method is called to add the newly created method to the set
of methods associated with the generic function metaobject.Concepts 151
(detmethod move :before ((p position) (1 (eql 0))
Soptional (visiblyp t)
Bkey color)
(set-to-origin p)
(unen visiblyp (shou-nove p 0 color)))
(let ((#:g001 (ensure-generic-function ‘move)))
01
(generic-function-method-class #:g001)
qualifiers '(:before)
‘:epecializers (list (find-class ‘position)
(intern-eql-specializer 0))
‘slambda-List '(p 1 optional (visiblyp t)
kkey color)
":funetion (function method-lambda)
‘additional-initarg-1
‘additional-initarg-2 '39)))
initargs returned from the call to make-method-lambda,
The result of the call to add-method is returned as the result of evaluating or exe
cating the defmethod form.
‘An example showing a typical defmethod form and a sample expansion is shown in
Figure 5.3. The processing of the method body for this method is shown in Figure 5.4.
5.4.4 Processing Method Bodies
Before a method can be created, the list of forms comprising the method body must be
converted to a method
conversion is a two step process.
step, the
erted to a lambda expression called152 Chapter 5
Get ((gf (ensure-generic-function 'nove)))
(make-nethod-Lanbda
at
(Hass-prototype (generic-function-method-class gf))
anbda (p 1 Boptional (visiblyp t) &key color)
(set-to-origin p)
jen visiblyp (show-move p 0 color)))
Figure 5.4 During macro-expansion of the defimethod macro shown in Figure 53, code
to this would be ran to produce the method lambda and additional initargs
environment is the macroexpansion environment of the defmethod macro form.
a method lambda. This conversion is based on information associated with
function definition in effect at the time the macro form is expanded.
la proceeds by calling
first argument in t is the generic function obtained
sd above. The second argument is the result of calling elass-prototype on
the result of calling generic-function-method-class on the generic function. The third
argument is a lambda expression formed from the m lambda list, declarations and
is the macro-expansion environment of the macro form; this
nment argument to the defmethod macro.
make-method- returns two values. T)
ization arguments and values. These
when the method is created.
thod lambda is converted to function which properly
lexical scope of the macro form. This is iaving the method lambda
macro-expansion as the argume special form, During
the subsequent evaluation of the macro-expansion, the result of the function special
form is the method function,
5.4.5 The defgeneric Macro
ion of a defgeneric form results in a call to the ensure-generic-
arguments received by ensure-generic-function are derived
from the defgeneric form in a defined way. As with defelass and defmethod, the exactConcepts 153
macro-expansion of the defgeneric form is not defined, only the relationship between
the arguments to the macro and the arguments received by ensure-generic-function.
# The function-name argument to defgeneric becomes the first argument to ensure
generic-function. This is the only positional argument accepted by ensure-generic-
function; all other arguments are keyword arguments.
The larhbda-list argument to defgeneric becomes the value of the slambd:
‘word argument to ensure-generic-function.
«* For each of the options :argument-precedence-order, :documentation, :generic-
function-class and :method-class, the value of the option becomes the value of the
keyword argument with the same name. If the option does not appear in the macro
form, the keyword argument does not appear in the resulting call to ensure-generic-
function.
he option declare, the list of declarations becomes the value of the sdeclarations
keyword argument. If the declare option does not appear in the macro form, the
jeclarations keyword argument does not appear i to ensure-generic-
ction.
key-
ing of the :method-combination option is not specified.
‘The result of the call to ensure-generic-function is returned as the result of evalu-
ating or executing the defgeneric form.
5.5 Subprotocols
This section provides an overview of the Metaobject Protocols. The detailed beh:
e Metaobject Protocol is presented in
6. The remainder of this chapter is intended to emphasize connections among
of the Metaobject Protocol, and to provide some examples of the kinds of
specializations and extensions the protocols are designed to support.
5.5.1 Metaobject Initialization Protocols
Like other objects, can be created by calling make-instance. The
its passed to make-instance are used to initialize the metaobjec
ual way. The set of legal initialization arguments, and their interpretation, depends
ted. Implementations and portable programs are
ion about the
of metaobject are provided in Chapter 6; this section provides
this behavior.
an overview and examples154 Chapter 5
Metaobjects Class metaobjects created with make-
ance are usually anonymous; that is, they have no proper name. An anonymous class
ing (setf find-class) and (setf class-name)
alized in the usual
use to establish the
definitipn of the class. Each ion argument is checked for errors and associated
with the class metaobject. The initialization arguments correspond roughly to the argu-
ments accepted by the defelass macro, and more closely to the arguments accepted by
the ensure-class function,
Some class metaobject their instances to be redefined. When pern
this is done by calling reinitialize-instance. This is discussed in the next secti
‘An example of creating an anonymous class directly using make-instance follows:
metaobject can be given a p
‘When a class metaobject is created wi
(flet (zero © 0)
(propelior () *propelior*))
(make-instance ‘standard-class
mame '(ay-class foo)
:direct-superclas:
(list (find-class ‘plane)
‘another-anonymous-class)
direct-slots ‘((:name x
Hnitform 0
Himitfunction ,#'zero
Hinitargs (x)
readers (position-x)
iwriters ((setf position-x)))
Gnaze y
Hinitform 0
iinitfunction ,#'zero
Himitargs (:y)
sreaders (position-y)
rwriters ((setf position-y))))
sdirect-default-initargs ‘((:engine *propellors ,#'propellor))))
Reinitialization of Class Metaobjects Some class metaobject classes allow their
instances to be reinitialized. This is done by calling rei tance. The initial-
ization arguments have the same interpretation as in clast
he class metaobject was finalized before the
inheritance will be called again once all the initi
ion arguments have been processedConcepts
and associated with the class metaobject. In addition, once finalization is complete, any
dependents of the class metaobject lated by calling update-dependent.
Initialization of Generic Function and Method Metaobjects An example of
creating a generic function and a method metaobject, and then adding the method to
the generic function is shown below. This example is comparable to the method definition
showmin Figure 5.3.
(et* ((gt (make-instance 'standard-generic-function
Hlambda-list '(p 1 optional visiblyp &key)))
(method-class (generic-function-aethod-class gf)))
(qultiple-value-bind (lambda initargs)
(nake-nethod-lanbda
et
(class-prototype method-class)
‘Gambda (p 1 optional (visiblyp t) tkey color)
(set-to-origin p)
(when visiblyp (show-move p 0 coler)))
ail)
(add-method gf
(apply #!nake-instance method-class
Hfunction (compile nil lambda)
repecializers (list (find-class ‘position)
(intern-eql-specializer 0))
squalifiers (
Hlambda-list '(p 1 optional (visiblyp t)
key color)
initargs))))
5.5.2 Class Finalization Protocol
Class final is the process of
superclasses and preparing to act
includes computing the class
stances of the class and the ful
iputing the information a class inherits from its
allocate instances of the class. ‘The class final-
, the full set of slots
ization arguments for
calling the appropriate reader. In ad& he class finalization process makes decisions
about how instances of the class will be implemented.
‘To support forward-referenced superclasses, and to account for the fact that not all
classes are actually instantiated, class finalization is not done as part of the initializationChapter 5
the class of the class metaobject; for standard-class
asses superclasses are defined, but no later than when.
ing the class precedence list. Doing this
“first allows subsequent steps to access the class precedence list. This step is performed
by calling the generic function compute-class-precedenci
from this call is associated wi
class-precedence-list gene!
‘The second step
the class metaobject and
with the class metaobject and can be accessed by calling the class-slots generic function.
‘The behavior of compute-slots is itself layered, consisting of calls to effective-slot-
definition-class and compute-effective-slot-defi
‘The final step of class finalization is computing the full set of initialization arguments
for the class, This is done by calling the generic function compute-default-initargs.
returned by this generie mn is associated with the class metaobject and
the CLOS specification calle
Forward-referenced classes, which provide a temporar
has been referenced but not yet defined, can never be finalized. An erzor is signalled if
finalize-inheritance is called on a forward-referenced class
5.5.3 Instance Structure Protocol
‘The instance structure proto
ike slot-val
responsible for implementing the behavior of the slot
there is a corresponding
function. When called;
finds the pertinent effective slot definition metaobject, calls the
corresponding generic function and returns its result. The arguments passed on to the
generic fu the class of the object argument, which
always immediately precedes the object argument.
generic functio
the slot access fun:187
‘The correspondences between slot access function and under
function are as follows:
slot access generic
Slot Access Function Corresponding Slot Access
Generic Function
slot-boundp slot-boundp-using-class
\ slot-makunbound slot-makunbound-using-class
slot-value slot-value-using-class
class)
(setf slot-value) (setf slot-value-
the instance structure protocol provides only mechanisms
le programs to control the implementation of instances and to directly access
the storage associated with instances without going through the indirection of slot access,
uested slot
In particular, portable programs can control the implementation of, and obtain direct
type t. These are called directly accessible
slots
i relevant specified around-method on compute-slots determines the implemen-
of instances by deciding how each slot in the instance will be stored. For each
t. The location can be accessed by calling
mn. Locations are non-negative integers.
re, the next paragraph, and the
where the value retuned by compute-slots is
Given the location of a directly accessible slot, the value of ti Jn an instance
can be accessed with the appropriate accessor. For standard-class, this accessor is the
function standard-instance-access. For funcallable-standard-class, this accessor is
the function funcallable-standard-instance-access. In each case, the arguments to
the accessor are the instance and the slot location, in that order. See the definition of
ach accessor in Chapter 6 for additional restrictions on the use of these functi
Portable programs are permitted to affect and rely on the allocation of locations only
in the following limited way: By first defining a portable primary method on compute-
slots which orders the returned value in & predictable way, and then relying on the defined
behavior of the specified around-method to assign locations to all directly accessible slotsChapter 5
Portable programs may compile-in calls to low-level accessors which take advantage of
this simple example implementation,
the :slot-order option is not inherited by subclasses; it controls only instances of the
class itself.
(defclass ordered-class (standard-class)
((slot-order :initfora ()
initarg :slot-order
reader class-slot-order)))
(efmethod compute-slots ((class ordered-class))
(et (Corder (class-slot-order class)))
(sort (copy-list (call-next-method) )
#'Gambda (a b)
(< (position (slot~definition-nane a) ord
(position (slot-definition-naze a) or:
»»
Following is the source code the user of this extension would write. Note that because
the code above doesn’t implement inheritance of the :slot-order 0}
distance must not be called on instances of subclasses of point;
instances of point itself.
funetion
can only be called
(efclass point ()
((x sinitform 0)
(y sinitform 0:
(:metaclass ordered-class)
(:slot-order x y!
(defun distance (point)
(eqrt (/ G@ (expt (standard-instance-access point 0) 2)
‘expt (standard~instance-access point 1) 2))
2.0)))
In more realistic uses of this mechanism, the calls to the low-level instance structure
appear textually in the source program, but rather would
be generated by a mets-level analysis program run during the process of compiling
the source program.Concepts 159
5.5.4 Funcallable Instances
Instances of classes which are themselves instances of funeallable-standard-class ot
one of its subclasses are called funcallable instances. Funcallable instances can only be
created by allocate-instance (funcallable-standard-class)
Like standard instances, funcallable instances have slots with the normal behavior.
instances in that they can be used as functions as well; that
is, they can be passed to funcall and apply, and they can be stored as the def
of fumetion name, Associated with each funcallable instance is the function which it
runs whet
function.
‘They make it possible to maintain
object
ith as the same object rather than two separate
inked, for example, by hash tables.)
(defclass constructor ()
((name :initarg :naze :accessor constructor-nane)
(fields :initarg :fields :accessor constructor-fields))
(:metaclass funcallable-standard-class))
(dofsethod initialize-instance :after ((c constructor) skey)
(with-slots (nane fields) ¢
(set-funcallable-instance-function
#'Cambda ©
Get (new (make-array (1+ (length fields)))))
(aref new 0) nane)
»)
(setq ci (nake-instance ‘constructor
mame ‘position :fields ‘(x y)))
*
(setq pt (funeall ¢1))
#160 Chapter 5
5.5.5 Generic Function Invocation Protocol
Each time the
Associated with each generi
generic
of the generic fu
received by the
is its discriminating fu
applicable-method:
functions arguments. jined by compute-effective-method
n effective method. Provisions are made t
utations. (See the dese
ly of method definitions are processed by make-method-lambda. The result
of this generic function is a lambda expression which is processed by either compile or
the file compiler to function. The arguments received by the method
function are controlled by the call-method forms appearing in the effective methods.
By defay 1d functions accept two arguments: a list of arguments to the generic
tof next methods. t of next. methods corresponds to the next
to call-method. years with additional argument
‘these cases, make-method-
lambda must have created the method lambdas to expect additional arguments.
5.5.6 Dependent Maintenance Protocol
It is convenient for portable metaobjects to be able to memoize is bout other
jerwise. Because class and generic function metaobjects
lized, and generic function metaobjects can be modified by addi
object which has been registered this way is called a dependent of the class or generic
function metaobject. ‘The dependents of class and generic function metaobjects are
d with add-dependent and remove-dependent. The dependents of a classConcepts 161
or generic function metaobject can be accessed with map-dependents. Dependents
are notified about a modification by calling update-dependent. (See the specification
of update-dependent for detailed description of the circumstances under which it is
called.)
‘two portable programs, or between portable programs
le code must not register metaobjects themselves
rograms which need to record a metaobject as a dep
encapsulate thet metaobject in some other kind of object, and record that object
rndent. The results are undefined if this restriction is violated,
lity for encapsulating metaobjects before recording.
as dependents, The facility defines a basic kind of encapsulating object: an
updater, Specializations of the basic class can be defined with appropriate special
updating behavior. In this way jon about the updating required is associated
with each updater rather than with the metaobject being updated.
Updaters are used to encapsulate any metaobject which requires updating when a
given class or generic function is modified. ‘The function record-updater is called to
both create an updater and add it to the dependents of the class or generic function.
‘Methods on the generic function update-dependent, specialized to the specific class
wt do the appropriate update work
(detclass updater ()
((dependent :initarg :dependent :reader dependent)))
(detun record-updater (class dependee dependent Arest initargs)
Get (updater (apply #'make-instance class :dependent dependent
initergs)))
(add-dependent dependee updater)
updater)
A flush-cache-updater simply flushes the cache of the dep
dated.
(Getclass flush-cache-updater (updater) ©)
(dotmethod update-dependent (dependee (updater flush-cache-updater)
rest args)
(declare (ignore args))
(flush-cache (dependent updater)))6 Generic Functions and Methods
+t describes each of the functions and generic functions that make up the
CLOS Metaobject Protocol. The descriptions appear in alphabetical order with the
exception that all the reader generic functions for each kind of metaobject are grouped
ample, method-function would be found with method-qualifiers
1d metaobject readers under “Readers for Method Metaobjects.”
‘The description of functions follows the same form as used in the CLOS specification,
‘The description of generic 8 is similar to that in the CLOS specification, but
some minor changes have been made in the way methods are presented.
The following is an example of the format for the syntax description of a generic
funetion:
sf K
1 y hoptional z kkey k
This description indicates that gfl is a generic function with two required parameters, x
ial parameter z and a keyword parameter k.
tion of a generic funetion includes a description of its behavior. This
provides the general behavior, or protocol of the generic function. All methods defined
both portable and specified, must have behavior consistent
generic-function an¢
The description of a generic function also includes descriptions of th
for that generic function. In the description of these met
to describe the parameters and parameter specializers of each method. The fol
‘an example of the format for a method signature:
sft Primary Method
(@ class) y koptional 2 Bkey k
This signature indicates that this primary method on the generic function gf has two
required parameters, named x and y. In addition, there is an optional parameter z
and a keyword parameter k. This signature also indicates that the method's parameter
specializers are the classes named class and t.
‘The description of each method includes a description of the behavior part
that method.
‘An abbreviated syntax is used when referring to a method defined elsewhere in
ocument. This abbreviated syntax includes the name of the generic fu
1164 add-dependent Chapter 6
the qualifiers, and the parameter specializers. A reference to the method with the
signature shown above is written as: gfl (class t).
add-dependent Generic Function
Syntax
add-dependent
jobject dependent
ARGUMENTS
The metaobject arg is a class or generic function metaobject.
The dependent argument is an object.
Vatuss |
The val
returned by this generic
mnction is unspecified.
Purpose,
This generic function adds dependent to the dependents of metaobject. If dependent is
already in the set of dependents it is not added again (no error is signal
\p-dependents can be called to access the set of dependents
of a class or generic function. The generic function remove-dependent can be called
to remove an object from the set of dependents of a class or generic function. The effect
add-dependent or remove-dependent while a call to map-dependents on
same class or generic function
The situations in which add-dependent is called are not specified
MeTHos
add-dependent Primary Method
ss standard-class) dependent
No behavior is specified for this method beyond that which is speci
funetion.
for the generic
wethod cannot be overridd
less the following methods are overridden 95
remove-dep i (standard-class t)
map-dependents (standard-class t)Generic Functions and Methods
165
add-dependent Primary Method
(class funcallable-standard-class) dependent
No behavior is specified for this method beyond that which is specified for the genetic
fanetion.
is method cannot be overridden unless the following methods are overridden as
wel:
remove-dependent (funcallable-standard-class t)
map-dependents (funcallable-standard-class t)
add-dependent Primary Method
(generic-function standard-generic-function) dependent
No behavior igs specified for the generic
function.
is method cannot be overridden unless the following methods are overridden as
remove-dependent (standard-generic-function t)
map-dependents (standard-generic-function t)
REMARKS:
"e the “Dependent Maint
rv.
ince Protocol” sect
n for remarks about the use of this
add-direct-method
Syntax
add-direct-method
ARGUMENTS
The specializer argument is a specializer metaobject.
‘The method argument is a method metaobject.
Vas
The value returned by this generic function is unspecified
PURPOSE,
This generic function is called to maintain a set of backpointers from a specializer to the
Set of methods specialized to it. If method is already in the set, itis not added again (no
ertor is signaled).166 add-direct-subclass Chapter 6
be accessed as a list by calling the generic function specializer-direct-
jethods are removed from the set by remove-direct-method.
The generic function add-direct-method is called by add-method whenever a
method is added to a generic It is called once for each of the specializers
of the method. Note that in cases where a specializer appears more than once in the
specializers of a method, nerie function wil led more than once with the
same specializer as argument.
The results are undefined if the specializer argument is not one of the specializers of
the method argument.
Metuops
sudd-direct-method Primary Method
Capecialaer cas)
(fretted nethoa)
‘This method in
behavior is spei
function.
ents the behavior of the generic function for class specializers, No
ied for this method beyond that which is specified for the generic
‘This method cannot be overridden unless the following
1ods are overridden as
add-direct-method Primary Method
(specializer eql-specializer)
(method method)
This method implements the behavior of the generic function for eql specializers. No
behavior is specified for this method beyond that which is specified for the generic
function.
add-direct-subclass ~~ Generic Function
SyNTAX
add-direct-subclass
superclass subclass
ARGUMENTS
‘The superclass argument is a class metaobject.Generic Functions and Methods add-method 167
The subclass argument is a class metaobject.
VaLues
‘The value returned by this generic function is unspecified.
Purpose
‘This generic function is called to maintain a set of backpointers from a class to its
fect subclasses, This generic function adds subclass to the set of direct subclasses of
superclass.
‘When a class isi
of the class.
When a class is reiniti
ed, this generic function is called once for each direct superclass
ized, this generic function is called once for each added direct
superclass of the class, The generic function remove-direct-subclass is called once for
each deleted direct superclass of the class.
MerHops
add-direct-subclass Primary Method
(superclass class)
(subclass class)
No behavior is specified for this method beyond that which is specified for the generic
function.
is method cannot be overridden unless the following methods are overridden as
n
remove-direct-subclass (class class)
class-direct-subclasses (class)
add-method Generic Function
Sywrax
add-method
generic-function method
ARGUMENTS
The generic-function argument is a generic function metaobject
‘The method argument is a method metaobject.
Vawues
‘The generic-function argument is returned.168 allocateinstance Chapter 6
PurPose
This genetic function associates an unattached method with a generic function,
‘An error is signaled if the ‘of the method is not congruent with the lambda
list of the generic function. An error is also signaled if the method is already associated
with some other generic function,
If the given method agrees with an existing method of the generic function on pa
rameter specializers and qualifiers, the existing method is removed by calling remove-
method before the new method is added. See the section of the CLOS Specification
called “Agreement on Parameter Specializers and Q\
in this context.
ig the method with the generic function then proceeds in four steps: (i) add
method to the set returned by generic-function-methods and arrange for method-
generic-function to return generie-function; \dd-direct-method for each of
the methods speci
result with set-funes
generic function.
d by the user or the implementation.
MerHops
add-method Primary Method
(generic-function standard-generic-function)
(method standard-nethod)
No behavior is specified for this method beyond that which is specified for the generic
allocate-instance Generic Function
allocate-instance
class krest initarys
ARGUMENTS
isa class metaobject.
args argument consists of alternating initialization argument names and values.
VaLups
‘The value returned is a newly allocated instance of class,Generic Functions and Methods class... 169
PURPOSE,
‘This generic function is called to create a
pretation of the concept of an “uninitialize
class.
stance depends on the class metaobject,
Before allocating the new instance, class-finalized-p is called to see if class has been
finalized. has not been finalized, finalize-inheritance is called before the new
instance is allocated.
MprHops:
allocate-instance Primary Method
(class standard-class) krest initargs
instance for each slot with allocation instance.
+h any other allocation are ignored by this method
This method allocates storage in
ese slots are unbound, Slots
(no error is signaled).
allocate-instance Primary Method
(class funcallable-standard-class) Arest initargs
‘This method allocates storage in
‘These slots are unbow
(no error is signaled).
‘The funcallable instance function of the instance is undefined—the results are un-
defined if the instance is applied to arguments before set-funcallable-instance-
function has been used to set the funcallat
instance for lot with allocatior
Slots with any other allocation are ignored by this met
allocate-instance
(class built-in-class) &rest initargs
‘This method signals an error.
Generic Function
lowing generic functions are described together under “Readers for Class Meta-
(page 212): class-default-initargs, class-direct-default-initargs, class-
direct-slots, class-direct-subclasses, class-direct-superclasses, class-finalized-p,
class-name, class-precedence-list, class-prototype and class-slots.
objects170 compute-applicable-methods Chapter 6
compute-applicable-methods Generic Function.
SYNTAX
compute-applicable-methods
‘generic-function arguments
ARGUMENTS
‘The generic-function argument is a generic function metaobject.
‘The arguments argument is a list of objects.
VALUES
‘This generic function returns a possibly empty list of method metaobjects.
Purpose
This generic function determines the method applicability of a generic function given a list
OF Required arguments. ‘The returned list of metaobjects is sorted by precedence
order with the most specific method appearing first. If no methods are applicable to the
supplied ary
ing compute-app!
methods. (Refer to the description of compute-discriminating-function for the
details of this process.)
‘The arguments arg to contain more elements than the generic func-
tion accepts required arguments; in these cases the extra arguments will be ignored. An
‘error is signaled if arguments contains fewer elements than the generic function accepts
required arguments,
The v
function.
are undefined i
Mernops
compute-applicable-methods Primary Method
(generic-function standard-generic-function)
arguments
‘This method signals an error if any method of the generic function has a spe
which is neither a class metaobject nor an eql specializer metaobject.compute-applicable-methods-using-classes 171
Otherwise, this method computes the sorted list of applicable methods according to
n the section of the CLOS Specification called “Method Selection,
‘This method can be overridden. Because of the consistency requirements
between this generic function and compute-applicable-methods-using-classes,
doing so may requite also overriding compute-applicable-methods-using-classes
(standard-generic-function t).
compute-applicable-methods-using-classes
SYNTAX
compute-applicable-methods-using-classes
‘generic-function classes
‘This generic function returns two values. The first is a possibly empty list of method
ibjects. The second is either true or false.
tion is called to attempt to determine the method applicability of a
generic function given only the classes of the required arguments,
If it is possible to completely determine the ordered list of applicable methods based
only on the supplied classes, this generic function returns that list as its first value and
true as its second value. The returned list of method metaobjects is sorted by precedence
order, the most specific method coming first. If no methods are appl
with the specified classes, the empty list and true are returned.
ly determine the ordered list of applicable methods
is generic function returns an unspecified first value
le to arguments
based only on the si
and false as its sect
‘When a generic function is invoked, the discriminating function must determine the
ordered list of methods applicable to the arguments. Depending on the generic function
and the arguments, this is done in one of three ways: using a memoized value; call-
ing compute-applicable-methods-using-classes; or calling compute-applicable-
methods. (Refer to the description of compute-discriminating-function for the
details of this process.)Chapter 6
yetween_ compute-applicable-methods-
ble-methods must be maintained: for any given
returns a second value of true, the first value must be equal to the value that w
returned by a corresponding call to compute-applicable-methods.
undefined if a portable method on either of these generic functions causes this consistency
to be violated.
‘The list returned by this generic function will not be mutated by the implementation.
‘The results are undefined if a portable program mutates the list returned by this generic
funetion.
MerHops
compute-applicable-methods-using-classes Primary Method
(generic-function standard-generic-function)
classes
if any method of the ion has a specializer which is neither a class meta-
object nor an eql specializer metaobject, this method signals an error.
In cases where the generic function has no methods with eq specializers, or has no
d be applicable to arguments of the supplied
cof applicable methods as its first value
require also
low user extensions which alter method lookup rules, but
the classes of the required arguments, to take advantage
of the class-based method lookup memoization found in many implementations. (‘There
is of course no requirement for an implementation to provide this optimization.)
Such an extension can be implemented by two methods, one on this generic function
and one on compute-applicable-methods. Whenever the user extension is in effet,
the first method will return a second value of true. This should allow the implementation
to absorb these cases into its own memoization scheme.
To get appropriate performance, other kinds of extensions may require methods on
compute-discriminating-function which implement their own memoization scheme.Generic Functions and Methods ‘compute-class-precedence-list 173
compute-class-precedence-list Generic Function
SvNTAX
compute-class-precedence-list
class
ARGUMENTS
‘The class argument is a class metaobject.
ves
value returned by this generic function is a list of class metaobjects.
fe the class precedence
the ordered direct superclasses of the superclasses of class. The results are
undefined if the rules used to compute the class precedence list depend on any other
factors.
When a class is finalized, finali
ciates the returned value with
calling class-precedence-list.
‘The list returned by this generic functio
feritance calls this generic funtion and asso-
class metaobject. The value can then be accessed by
Il not be mutated by the implementation.
The results are undefined if a portable program mutates the list returned by this generic
function,
compute-class-precedence-list
Primary Method
(class class)
ist according to the rules described in the
lled “Determining the Class Precedence List.”
if class or any of its superclasses is a forward referenced
This method can be overridden,Chapter 6
compute-default-initargs Generic Function
SYNTAX
compute-default-i
class
ARGUMENTS
‘The class argument is a class metaobject.
VaLues
‘The value returned by this generic function is a list of canonicalized default initialization
arguments.
PurPose
This generic-function is called to determine the default initialization arguments for a
isa list of canonicalized default initialization arguments, with no duplication
yn arguments
ie direct default
lefined if the rules
When a class is finalized, finalize-inheritance calls this generic function and asso-
iates the returned value with the class metaobject. The value can then be accessed by
1g class-default-initargs.
‘The list returned by this generic function will not be mutated by the implementation.
‘The results are undefined if a portable program mutates the list returned by this generic
function.
MetHops
compute-default-initargs Primary Method
(class. standard-class)
compute-default-initargs Primary Method
(class tuncallable-standard-class)
The
hods compute the defi
“Defaulting ofGeneric Functions and Methods computediscriminsting-function 175
‘These methods signal an error if class or any of its superclasses is a forward refer-
enced class.
‘These methods can be overridden.
compute-discriminating-function Generic Function
SYNTAK
compute-discriminating-function
‘generic-function
ARGUMENTS
The generic-function argument is a generic function metaobject.
VaLuss
‘The value returned by this generic function is a function.
called with the
the behavior of «
methods, determining the effective method, and running the effective method,
To determine the ordered set of applicable methods, the discriminating function
first calls compute-applicable-methods-using-classes.
methods-using-classes returns a second value of false, the dis
‘then calls compute-applicable-methods.
‘When compute-applicable-methods-using-classes returns a second value of true,
the discriminating function is permitted to memoize the first returned value as follows,
‘The discriminating function may reuse the list of applicable methods without calling
ble-methods-using-classes again provided that:
inating function
the generic function is being called again with required arguments which are
es of the same classes,
‘generic function has not been reinitialized,
no method has been added to or removed from the generic function,
their class precedence
(for any such
the required arguments
tts have not changed and
ed value, the class precedence list of the class of each of176 compute-effective-method Chapter 6
e method is done by calling compute-effective-method.
When the effective method is run, each method's f is called, and receives as
arguments: (i) a list of the argh to the generic )) whatever other
arguments are specified in the call-method form indicating that the method should be
called. (See make-method-lambda for more information about how method functions
are called.)
The generic f
installed, by add-method, remove-method, initialize-instance and reinitialize
instance.
ating-funetion Primary Method
mm standaré-generic-function)
‘No behavior is specified for this method beyond that specified for the generic
‘This method can be overridden.
compute-effective-method Generic Function
Sywrax
compute-effective-method
‘generie-function method-combination methods
ARGUMENTS
‘The generic-function argument is a generic function metaobject.
‘The method-combination argument is a method combination metaobject.
‘The methods argument is a list of method metaobjects.
VaLues
is generic function returns two values. The first is an effective method, the second is
alist of effective method options.
PURPOSE
This generic function is «
method meteobjects
‘An effective method is a form that describes how the applicable methods are to be
com of effective method forms are call-method forms which indicate that
‘ particular method is to be called. ‘The arguments to the call-method form indicate
exactly how the method function of the method should be called. (See make-method-
lambda for more details about method functions.)
to determine the effective method from a sorted list ofGeneric Functions and Methods compute-cffectivesiot-definition 177
An effective method option has the same interpretation and syntax as either the
:arguments or the :generic-function option in the long form of define-method-
combination.
‘More information about the form and interpretation of effective methods and effective
method options can be found under the description of the define-method-combination
macro in the CLOS specification.
This generic function can be called by the user or the implementation, It is called by
discriminating functions whenever a sorted list of applicable methods must be converted
to an effective method.
MerHops
Primary Method
compute-effective-slot-definition Generic Function
SYNTAX
compute-effective-slot-definition
definitions
class name direct:
ARGUMENTS
The class argument is a class metaobject.
‘The name argument is a slot
jons argument is an ordered list of direct slot definition metaob-
irect slot definition metaobject appears first in the list,
jects. Th
VaLues
‘The value returned by this generic function is an effective slot definition metaobject.
‘This generic function determines the effective slot definition for a slot in a class. It is
called by compute-slots once for each slot accessible in instances of class.
‘This generic function uses the supplied list of direct slot definition metaobjects to
compute the inheritance of slot properties for a single slot.178 compute-siots Chapter 6
definition represents the result of computing the inheritance. The name of the new
effective slot definition is the same as the name of the direct slot definitions supplied.
ize the new effective slot definition metaobject. See “Initialization of Slot Definition
Metaobjects” for details.
MetHops:
compute-effective-slot-definition Primary Method
(class standard-class)
‘This method implements the inheritance and defaulting of slot options following the
rules described in the “Inheritance of Slots and Options” section of the CLOS Speci-
fication,
‘This method can be extended, but the value returned by the extending method must
be the value returned by this method.
compute-effective-slot-definition Primary Method
(class funcaliable~standard-class)
f slot options following the
rules described in the “Inheritance of Slots and Options” section of the CLOS Speci-
fication,
‘This method can be extended, but the value return
be the value returned by this method.
the extending method must
compute-slots Generic Function
SvNTAX
compute-slots
class
ARGUMENTS
The class argument is a class metaobject.Generic Functions and Methods
VaLues
The value returned is a set of effective slot definition metaobjects.
Purpose,
‘This generic function computes a set of effective slot det
ist of effect
in instances of class.
ition metaobjects for the class
slot definition metaobjects: one for each slot that
he first step collects the full set of direct slot definitions from the superclasses of
class.
The direct slot definitions are then collected into individual lists, one list for each slot
name associated with any of the direct slot definitions. The slot names are compared
with egl. Each such list is then sorted into class precedence list order. Direct slot
from classes earlier in the class precedence list of class appear before
the class precedence list. For each slot name, the
function compute-effective-slot-definition is called to compute an effective
ition. ‘The result of compute-slots is a list of these effective slot definitions,
in unspecified order.
In the final step, the location for each effective slot definition is set. This is done by
specified around-methods; portable methods cannot take over this behavior. For more
information on the slot definition locations, see the section “Instance Structure Protocol.”
The list returned by this generic function will not be mutated by the implementation.
‘The results are undefined if a portable program mutates the list returned by this generic
tion,
MerHops
compute-slots Primary Method
standard-class)
‘This method implements the specified behavior of the generic function.
‘This method can be overridden.
compute-slots Primary Method
(class funcallable-standard-class)
This method implements the specified behavior of the generic function.
‘This method can be overridden.180 direct-slot-definit Chapter 6
compute-slots Around-Method
(class standard-class)
‘This method implements th
is method cannot be
ing and storing slot locations.
compute-slots Around-Method
(class funcallable~standard-class)
‘This method implements the specified behavior of computing and storing
This method cannot be overridden.
locations,
direct-slot-definition-class
Synrax
direct-slot-defi
class Brest initargs
ARGUMENTS
The class arg
The initargs argument is a set of initialization arguments and values.
VaLues
‘The value returned is a subelass of the class direct-slot-definition
PuRPOSE,
‘When a class is initialized, each of the
to a direct slot definition metaobject. ‘This gent
class of that direct slot definition metaobject.
‘The initargs argument is simply the canonicalized slot specification for the slot.
lot specifications must be converted
ic function is called to determine the
Meriops
direct-slot-definition-class Primary Method
(class standara-class)
krost initargs
‘This method returns the class standard-
t-slot-definition.
‘This method can be overridden.Generie Functions and effctive-slot-d 181
direct-slot-definition-class Primary Method
(class funcallable~standard-class)
brest inilargs
is method returns the class standard-direct-slot-definition,
This method can be overridden,
effective-slot-definition-class Generic Fi
SyNTAX
effective-slot-definition-class
class brest initargs
GUMENTS.
‘The class argument is a class motaobject.
The jalization arg
fargs argument is a set of nts and values.
VaLues
‘The value returned is a subclass of the class effective-slot-definition-class.
PuRPosE,
class of
1¢ resulting effective slot definition metaobject. The
of initialization arguments and values be passed to make-instance when
the effective slot definition metaobject is created,
Mpriops
Primary Method
(class standard-class)
brest initargs
Thi iod returns the class standard-effective-slot-definition,
‘This method can be overridden.
effective-slot-definition-class Primary Method
(class funcallable~standard-class)
brest initargs
‘This method returns the class standard-effective-slot-definition.
‘This method can be overridden.182 ensure-class Chapter 6
ensure-class Function
SYNTAX
ensure-class
name Bkey kallow-cther-keys
ARGUMENTS
n
ame argument is a s
Some of
‘object (as described in the section called “Initialization of Class Metaobjects"),
VaLues
The result is a class metaobject.
PURPOSE
‘This function is called to define or redefine a class with the specified name, and can be
called by the user 0 jlementation. It is the functional equivalent of defelass, and
is called by the expansion of the defelass macro.
The behavior of thi ion is actually i
‘The first argument to ensure-class-us
ig-class is computed as follows:
# If name names a class (ind-class returns a class when called with name) use that
are the complete set of keywordGeneric Functions and Methods encure-class-using-class 183
ensure-class-using-class Generic Function
SYNTAX
ensure-class-using-class
class name dkey :direct-default-initargs :direct-slots
|irect-superclasses :name
:metaclass
ballow-other-keys
ARGUMENTS
‘The class argument is a class metaobject or nil.
The name argument is a class name,
‘The :metaclass ar a class metaobject class or a class metaobject class name,
If this argument is not supplied, it defaults to the class nained standard-class. If a class
lied, jerpreted as the class with that name, If a class name is supplied,
but there is no such class, an error is signaled.
‘The :direct-superclasses argument is a list of which each element is a class metaob-
ject or a'chsés name. An error is signaled if this argument is not a proper list,
For the interpretation of additional keyword arguments, see “Initialization of Class
Metaobjects” (page 193),
VaLvES
‘The result is a class metaobject
Purpose,
generic function is called to define or modify the definition of a named class. It is
It can also be called directly.
by the ensure-class
first step performed
arguments which will be used to ereate or
arguments ar from the full set of keyword arguments received by
function as
yetaclass argument is not included in the initialization arguments.
direct-superclasses argument was received by this generic function, it is
converted into a list of class metaobjects. This conversion does not affect the structure
of the supplied :direct-superclasses argument. For each element in the :direct-
superclasses argument:
© If the element is a class metaobject, that class metaobject is used,184 ensure-class-using-class Chapter 6
© If the element
mes a class, that class metaobject is used.
© Otherwise an instance of the class forward-referenced-class is created and used.
‘The proper name of the newly created forward referenced class metaobject is set to
other keyword arguments are included direct
in the initializat
arguments.
If the class argument is, new class metaobject is created by calling the make-
ment, and the p:
newly created class metaobject is set to name. The newly created class metaobject is
returned
If the class argument is a forward referenced class, change-class is called to change
its clas to the value specified by the :metaclass argument. The class metaobject is then
lized with the pre ization arguments. (This is a documented violation
of the general constrai change-class not be used with class metaobjects.)
If the class of the class argument is not the same as the class specified by the :meta-
by calling the
returned.
MetHops
ensure-class-using-class Primary Method
(class class)
bkey :metaclass
direct-superclasses
kallow-other-keys
sments the behavior of the generic function in the case where theGeneric Functions and Methods censure-generic-function 185
ensure-class-using-class Primary Method
(class forvara- nced-class)
bkey :metaclass
direct-superclasses
kallow-other-keys
‘This method implements the behavior of the generic function in the case where the
class argument is a forward referenced class,
ensure-class-
(class null)
name
class Primary Method
tkey :metaclass
:direct-superclasses
ballow-other-keys:
the behavior of the generic function in the case where the
class argument is
ensure-generic-function Function
SyNTax
ensure-generic-function
function-name key ballow-other-keys
ARGUMENTS
‘The function-name argument is a symbol or a list of the form (sett symbol)
Some of the keyword arguments accepted by this function are actually processed by
‘ensure-generic-function-using-class, others are processed during
mn metaobject (as described in the section called
VaLues
‘The result is a generic function metaobject.186 ensure-generic-functior Chapter 6
PURPOSE
‘This function is called to define a globally named generic function or to specify or modify
options and declarations that pertain to a glol
It can be called by the user or the implementation,
It is the functional equivalent of defgeneric, and is called by the expansion of the
defgeneric and defmethod macros
‘The
generic
ately calls ensure-generic-function-using-class and returns that result as its own.
‘The first argument to ensure-generic-function-using-class is computed as follows:
ned generic fu
on as a whole.
© If function-n
signaled.
functi
© Otherwi
names @ non-generic function, a macro, or a special form, an error is
es a generic function, that generic function metaobject is used.
The second argument is function-name. The remaining arguments are the
set of keyword arguments received by ensure-generic-function,
ensure-generic-function-using-class Generic Function
SYNTAX
ensure-generic-function-using-class
generic-function
function-name
kkoy :argunent-precedence-order :declarations
documentation :generic-function-class
Slambda-list :method-class
:method-combination :name
Aallow-other-keys
ARGUMENTS
-function argument is a generic function metaobject or nil.
-name argument is a symbol or a list of the form (set symbol)
eneric-function-class argument is a class metaobject or a class name.
to the class named standard-generic-function. If
name is supplied, itis interpreted as the class with that name. Ifa class name is suppl
but there is no such class, an error is signaled.Generic Functions and Methods ensure-generic-function-using-class 187
For the interpretation of additional keyword arguments, see“
Function Metaobjects” (page 197)
jalization of Generic
VaLuEs
‘The result is a generic function metaobject.
PURPOSE
‘The generic function ensure-generic-function-using-class is called to define or modify
the definition of a globally named generic function. It is called by the ensure-generic-
function functi
‘The first step performed by this generic function is to compute the set of initialization
arguments which will be used to create or reinitialize the globally named generic function.
from the full set of keyword arguments
received by this generic function as follows:
‘¢ The :generic-function-class argument is not included in the initialization arguments.
‘ If the :method-class argument was recei t is converted
into a class metaobject. This is done by looking up the class name with find-class. If
there is no such class, an error is signalled.
© All other keyword arguments are included directly in the initialization arguments.
If the generie-function argument is nil, an instance of the class specified by the
name the generic function. The newly created generic function metaobject is returned.
If the class of the generic-function argument is not the same as the class specified by
sgeneric-function-class argument, an error is signaled.
Otherwise the generic function generic-fun
instance generic function with generie-function and the initiali
generic-function argument is then returned
jon arguments. The
Mernops
ensure-generic-function-using-class Primary Method
(generie-function generic~function)
function-name
trey :generic-function-class
kallow-other-keys
‘This method implements the behavior of the generic function in the case where function-
name names an existing generic function,188 extract-lambda-tist Chapter 6
‘This method can be overridden,
censure-generic-function-using-class Primary Method
(generie-function nuit)
function-name
key :generic-function-class
kallou-other-keys
This method im
ts the behavior of the generic fu
nnction, generic func
‘case where funetion-
macro or special form,
eql-specializer-object Function
Vatues
‘The value returned by this function is an
Pu
Iue is guaranteed to be eql to the value originally passed to intern-e
tit is not necessarily eq to that value.
‘This function signals an error if egl-specializer is not an eql specializer,
extract-lambda-list
SYNTAX
extract-lambda-list
specialized-lambda-list
ARGUMENTS
‘The specialized-lambda-list argument is a specialized lambda list as accepted by def
method.Generic Functions and Methods extractlambda-list 189
VALUES
The res
is an unspecialized lambda list.
Purpose
This function takes a specialized lambda list and returns the lambda list with the spe
alizers removed. This is a non-destructive operation. Whether the result shares any
ructure with the argument is unspecified.
the specialized-lambda-list argument does not have legal syntax, an error is signaled.
syntax checking does not check the syntax of the actual specializer names, only the
1
syntax of the lambda list and where the specializers appear.
EXAMPLES
(extract-lambda-list '((p position))) > ©)
(extract-Lanbda-List '((p position) x y)) > XY
(extract-lambda-list ‘(a (b (eql x)) ¢ Brest 1)) ==> (A BC OPTIONAL 1)
extract-specializer-names ‘Function
SYNTAX a
extract-specializer-names
specialized-lambi
ARGUMENTS
‘The specialized-lambda-list argument is a specialized lambda list as accepted by def
method.
VALUES
The result is a list of specializer names.
SE
tion takes a specialized lambda list and returns its specializer names. This is
lestructive operation. Whether the result shares structure with the argument is
unspecified. The results are undefined if the result of this function is modified.
‘The result of this function will be a list with a number of elements equal to the number
of required arguments in speci lambda-list. Specializers are defaulted to the symbol
t
If the speciatized-lambda-list argument does n an error is signaled,
‘This syntax checking does not check the syntax of the actual specializer names, only the
syntax of the lambda list and where the specializers appear.190 Chapter 6
EXAMPLES
(extract~specializer-nanes '((p position))) ==> (POSITION)
(extract~specializer-nanes '((p position) x y)) ==> (POSITION T 7)
(extract~specializer-nanes ‘(a (b (eql x)) ¢ frest i)) ==> (T (EQL x) T)
inheritance Generic Funct
final
Syntax
finalize-inheritance
After finalize-inheritance returns, the class metaobject is finalized and the result of
calling class-finalized-p on the class metaobject will be true.
MerHops
finalize-inheritance Primary Method
(class standard-class)
finalize-inheritance Primary Method
(class funcallable~standard-class)
No behavior is specified for these methods beyond that which is specified for the generic
function,
finalize-inheritance Primary Method
(class forward-re: ynced-class)
‘This method signals an error.Generic Functions and Methods find-method-combination
find-method-combination ~~ Generie Function
syntax
find-method-combination
sgenerie-furction
ARGUMENTS
‘The generic-function argument is a generic function metaobject.
The method-combination-type-name argument is a symbol which names a type of
smethod combinat
The method-combination-options argument is a lst of arguments to the method com-
bination type.
VaLues
‘The value returned by this generi
ction is a method combination metaobject
Purpose,
This generic function is called to determine the method combination object used by a
seneric function.
REMARKS
Further details of method combination metaobjects are not specified,
funcallable-standard-instance-access Function
SYNTAX
funcallable-standard-instance-access
instance location.
ARGUMENTS
ince argument is an object,
rion argument is a slot location.
‘The result of this function is an object.192 generic-funetion-. Chapter 6
PURPOSE,
alled to provide direct access to a s
it in an instance. By usurping the
Jot lookup protocol, this function is intended to provide highly optimized access
associated with an instance.
ng restrictions apply to the use of this function:
‘* The instance argument must be a funcallable instance (it must have been returned by
allocate-instance (funcallable-standard-class)).
‘© The instance argument cannot be an non-updated obsolete instance.
must be a location of one of the directly accessible slots of the
© The slot must be bound,
The results are undefined if any of these restrictions are not met,
generic-function-... ~ Generic Function
ved together under “Readers for Generic
\-argument-precedence-order,
generic-function-declarations, generic-functi
method+class, generic-function-method-combi
‘ods and generic-function-name,
lambda-list, generic-function-
jon, generic-function-meth-Generic Functions and Methods jon of Class Metaobjects 193
Initialization of Class Metaobjects
A class metaobject can be created by calling make-instance. ‘The initialization argu-
‘ments establish the definition of the class. A class metaobject can be redefined by callin
tance. Some classes of class metaobject do not support redefini
ing make-instance and allow-
tion of a class metaobject must be done by
ize-instance. Portable programs must not call ize-instance di-
ize a class metaobject. Portable programs must not call shared-initialize
directly to initialize or reinitialize a class metaobject. Portable programs must not call
change-class to change the class of any class metaobject or to turn a non-class object
into a class metaobject.
Since metaobject classes may not be redefined,
havior is specified for the result
of
‘aobjects may not be changed, no behavior is specified for the result of calls
to update-instance-for-differ
During initialization or rei ent is checked for er-
rors and then associated with the class metaobject. The value can then be accessed by
calling theappropriate accessor as shown in Table 6.1
This Section begins with a description error checking and processing of each
initialization argument. This is followed by a table showing
1e generic functions that
can be used to access the stored iavior specific
to the different specified class
ization,
In these description ‘means that when
that initialization argument is not alization is performed
4s if value had been supplied. For some initialization arguments this could be done by the
use of default initialization arguments, but whether it is done this way is not specified.
Implementations are free to define default i tion arguments for specified class
+ classes. Portable programs are free to define default tion arguments
ing reinitialization, if an ini-
argument is not suppl xreviously stored value is left unchanged.
irect-default-
arguments.
itargs argument is a list of canonie:Initialization of Class Metaobjects Chapter 6
An error is signaled if ti
not a canonicalized def
If the class metaobject is b
‘The :direct-slots argument
signaled if this
not a canonicalized slot specific
direct slot definition metaobject class. Sec-
definition metaobject class and the
‘canonicalized slot specification. This conversion could be implemented as shown in the
following code:
(defun convert-to-direct-slot-definition (class canonicalized-slot)
(apply #'make-instan
ct-slot-definition-class
canonicalized-slot)
canonicalized-slot))
If the class metaobject is being
Once the direct slot definiti
and writer methods are creat
writer-method-class are called to determine
created.
The :direct-superclasses argument is
not support multiple inheritance signal an
element.
An error is signaled if
applied to class and any el
lized, this argument defaults to the empty list.
jects have been created, the specified reader
1e classes of the method metaobjects
t of class metaobjects. Classes which do
or if the list contains more than one
is value fa proper list or if validate-superclass
ent of this list returns false.
When the class metaobject is being initialized, and this argument is either not
supplied or is the empty list, this argument defaults as follows: if the class is an
instance of standard-class or one of its subclasses the default value is a list of the
class standard-object; if the class ance of funcallable-standard-class or
one of its subclasses the default value is list of the class funcallable-standard-ob ject.Generic Functions and Methods Initialization of Class Metaobjects 195
After any defaulting of the val
once for each element of the list.
When the class metaobject is being rei
jeneric function remove-direct-subclass is called once for each class metaobject in
Previously stored value but not in the new value; the generic function add-direct=
subclass is called once for each class metaobject in the new value but not in the
previously stored value,
+ The :documentation argument is a string or
An error is signaled if this value is not a string or nil
If the class metaobject is being initialized, this argument defaults to nil.
© The mame argument is an object.
If the class is being i
the generic function add-direct-subclass is called
d and this argument is supplied, the
nt defaults to nil.
arguments described above, the
class metaobject. These values
1g generic function. The correspondences
can then be accessed by calling the corresp¢
are as follows:
ization Argument Generic Function
class-direct-default-initargs
class-direct-slots
class-direct-superclasses
documentation
class-name
‘6:1 Initialization arguments and accessors for clase metaobjects.—
Instances of the class standard-class support multiple inheritance and reinitialization.
Instances of the class funcallable-standard-class support multiple inheritance and
ialization. For forward referenced classes, all of the initialization arguments default
in classes cannot be created or reinitialized by the user, an error is signaled
if initializo-instance or rei i
derived instance of the class built-in-class.
‘METHODS:
It is not specified which methods provide the initialization and reinitialization behavior
described above, Instead, the information needed to allow portable programs to specialize196 Tnitialization of Class Metaobjects Chapter 6
metaobject when either all or none of the specified initialization has taken effec.
jions govern the methods that a portable program can def
ions initialize-instance, reinitialize-instance, and shared-
ns apply only to methods on these generic functions for which the first
is a subclass of the class class. Other portable methods
t affected by
# Portable progr
© For ize-instance and reinitialize-instance:
# Portable programs must not define primary methods.
© Portable programs may define around-methods, but these must be extending, not
overriding methods.
* Portable before-methods must assume that when they are run, none of the initial-
ization behavior described above has been completed.
‘The results are undefined if any of these restrictions are violated.lization of Generic Function Metaobjects 197
Initialization of Generic Function Metaobjects
A generic function metaobject can be created by calling make-instance. The i
ization arguments establish the definition of the generic funct
metaobject can be redefined by calling re stance. Some classes of generie
taobject do not support redefinition; in these cases, reinitialize-instance
‘@ non-generic-function object into a generic
nce metaobject classes may not be redefined, no behavior is
calls to update-instance-for-redefined-class on generi
the class of a generic function metaobject may not be changed, no behavior is speci-
fied for the results of calls to update-instance-for-different-class on generic function
metaobjects.
During initialization or rei
ified for the result of
unction metaobjects, Since
argument is checked for er-
‘aobject. The value can then be
accessed by calling the appropriate accessor as shown in Table 6.2.
n of the error checking and processing of each
”n argument. This is followed by a table showing the generic functions that
ed to access the stored
ese descriptions, the phrase “this argument defaults to valu
that initialization argument
ization arguments t
be done by the
+hether it is done this way is not specified.
ialization arguments, but
Implementations are free to define default, initialization arguments for spet
metaobject classes. Portable programs are free to define defa
‘arguments for portable subclasses of the class generic-funt
fie note to the contrary, then during rei
10t supplied, the previously stored value198 ation of Generic Function Metaobjeets Chapter 6
‘* The sargument-precedence-order argument is a list of symbols.
‘An error is signaled if this argument appears but the :lambda-list argument does
not appear. An error is signaled if this value is not a pr
of the symbols from the required argi
«a legal declaration.
When the generic
defaults to the
© The :documentation argument is a string or nil
‘An error is signal
If the generic functior
# The lambda-list argument is a lambda lis.
‘An error is signaled if this value is not a proper generic function lambda list.
‘When the generic function is being initialized, and this argument is not supplied, the
generic function's lambda list is not
either when the first method is added to the generic function, or a later reinitialization
of the generic function,
‘* The :method-combination argument is a method combination metaobject.
+ The :method-class argument is class metaobject.
‘An error is signaled if this value is not a subclass of the class method.
‘When the generic function is being initialized, and this argument is not supplied, it
defaults to the class standard-method.
© The sname argument is an object.
If the generic function is being it
is not supplied, it
ialized, this argument defaults to nil.
After the processing and defaulting of initialization arguments described above, the
value of each initialization argument is associated with the generic function metaobject-
‘These values can then be accessed by calling the corresponding generic function. ‘The
correspondences are as follows:Generic Functions and Methods ization of Generic Function Metaobjects 199
Initialization Argument Generic Function
:argument-precedence-order_generic-function-argument-precedence-order
sdeclarations generic-function-declarations
:documentation documentation
slambda-list generic-function-lambda-list,
:method-combination generic-function-method-combination
smethod-class generic-function-method-class
mame generic-function-name
‘Fable 6.2 Initialization arguments and accessors for generic function metaobjects
MerHops
It is not specified which methods provide the initialization and reinitialization behavior
described above. Instead, the information needed to allow portable programs to specialize
this behavior is presented as a set of restrictions on the methods a portable program
can define. The model is that portable initialization methods have access to the generic
function metaobject when either all or none of the specified initialization has taken effect.
‘These restrictions govern the methods that a portable program can define on the
generic. functions instance, reinitialize-instance, and shared-initialize.
restrictions apply only to methods on these generic functions for which the first
specializeris'& Subclass of the class generic-function. Other portable methods on these
generic functions are not affected by these restrictions.
© Ports
© For
programs must not define ialize
jalize-instance and reiniti
‘* Portable programs must not define primary methods.
© Portable programs may define around-methods, but these
overriding methods.
© Portable before-methods must assume that when they are run, none of the
ization behavior described above has been completed.
* Portable after-methods must assume that when they are run, all of the init
behavior described above has been completed.
ist be extending, not
‘The results are undefined if any of these restrictions are violatedization of Method Metabjects Chapter 6
Initialization of Method Metaobjects
‘A method metaobject can be created by calling make-instance.
method. A method metaobject cannot be
rance signals an error.
metaobject must be done by calling make-f
ize-instance. Portable progran
tance directly to initialize a method metaoject. Portable programs must not call
shared-initialize directly to initialize a method metaobject. Portable programs must
not call change-class to change the class of any method metaobject or to turn a non-
method object into a method metaobject.
Since metaobject classes may not be redefined, no behavior is specified for the result of
calls to update-instance-for-redefined-class on method metaobjects. Since the class
of a method metaobject cannot be changed, no bebavio ified for the result of calls
iethod metaobjects.
ation argument is checked for errors an
‘appropriate accessor as shown in Table 6.3
This section begins with a description of the error checking and processing of each
initialization argument. This is followed by a table showing the generic functions that
can be used to access the stored ation arguments. The section ends with a set of
restrictions’on portable methods affecting method metaobject initialization.
In these descriptions, the phrase “this argument defaults to value” means that when
tilization argument is not supplied, initialization is performed as if value had
ion arguments this could be done by
initialization arguments, but whether
tions are free to define default i
classes. Portable programs are free to
subelasses of the class method.
arguments for specified method n
fine defay jalization arguments for portable
‘© The :qualifiers argument is a list of method qualifiers. An error is signaled if this
value is not a proper list, or if any element of the list is not a non-null atom. This
argument defaults to the empty list.
© The slambda-list argument is the unspecialized la ist of the method. An error
is signaled if this value is not a proper lambda list. If this value is not supplied, an
naled.
1r8 argument is a list of the specializer metaobjects for the method. An
led if this value is not a proper list, or if the length of t fers from202 Initialization of Method Metaobjects Chapter 6
‘These restrictio
a portable program can define on the
generic functions ize-instance, and shared-
‘These restrictions apply only to methods on these generic functions for which
specializer is a subclass of the class method. Other portable methods on these generic
functions are not affected by these restrictions.
* Portable programs must not define methods on shared-initialize or reinitialize-
instance.
« For initialize-instance:
Portable programs must not define primary methods.
Portable programs may define around-methods, but these must be extending, not
overriding methods.
t when they are
leted,
behavior described above has been completed.
‘The results are undefined if any of these restrictions are violated.Generic Functions and Methods Initialization of Slot Definition Metaobjects 203
Initialization of Slot Definition Metaobjects
A slot definition metaobject can be created by c
arguments establish the defini
cannot be redefined; ci
'6 make-instance. The initialization
the slot definition. A slot definition metaobject
ize-instance signals an error.
etaobject must be done by calling make
tialize-instance. Portable programs must not call
initialize slot definition metaobject. Portable programs must
not call shared-initialize directly to initialize a slot definition metaobject, Portable
brograms must not call change-class to change the class of any sit definition metaabject
or to tur a non-slot-definition object into a slot de
Since metaobject classes may not be redefined, n specified for the result of
calls to update-instance-for-redefined-class on slot definition metaobjects, Since the
‘metaobject cannot be changed, no behavior is specified for the
nnce-for-different-class on slot definition metaobjects
In these {descriptions, the phrase
that
initialization argument one this way is not specified. Implementatior
ization arguments for specified slot definition metaobject
classes. Portable programs are free to define default initialization arguments for Portable
bclasses of the class slot-definition.
iname argument is a slot name. An error is signaled if this argument is not a
‘can be used as @ variable name. An error is signaled if this argument is
* The sinitform argument is a form. Thi An error
is signaled if the sini l, initfunction argument is
not supplied,Chapter 6
.. An error is signaled
form argument is not supplied.
type argument is a type spet
ype argument defaults to the sy
location argument is a symbol. An error is signaled otherwise. The -alloca-
bols. An error is signaled if this argument is
not a proper list, or if any element of this list is not a symbol. The :initargs argument
defaults to the empty
The :readers argu
is a list of function names. An error is signaled if it is not
proper list, or if any element is not a vs tion name. It defaults to the empty
list. An error is signaled if this argument is supplied and the metaobject is not a direct
slot defi
» The :wri
srs argument is a list of function names. An error is signaled if it is not a
proper list, or if any element is not a valid function name. It defaults to the empty
list. An error is signaled if this argument is supplied and the metaobject is not a direct
slot definition.
documentation argument
or nil. An error is signaled otherwise. The
jocumentation argument def
tion arguments described above, the
ciated with the slot definition metaobject.
‘the corresponding generic function. The
These values can then be accessed by
correspondences are as follows:Generic Functions and Methods Initialization of Slot Definition Metaobjects 205
Generic Function
slot-de
slot-de!
slot-det
slot-det
slot-det
slot-de
slot-de!
writers slot-definition-writers
:documentation documentation
‘Table 6.4 Ini fion arguments and accessors for slot definition
metaobjects
MeTHoDs
Itis methods provide the initi
described above. Instead, th
this behavior is presented as
define. The model is th
‘metaobject when either
tion and reinitialization behavior
weeded to allow portable programs to specialize
ds a portable program can
ion methods have access to the slot definition
or none of the specified initialization has taken effet.
‘These restrictions govern the methods that a portable program can define on the
generie-functions instance, reinitialize-instance, and shared-initialize.
‘These restrictions apply only to methods on these generic fanctions for wl
specializer is a subclass of the class slot-definition. Other portable methods on these
generic functions are not affected by these restrictions.
* Portable programs must not define methods on shared-initialize or rei
instance,
** For initialize-instance:
* Portable programs must not define pri
* Portable programs may define aro
overriding methods.
* Portable before-methods must
ion behavior described above has been completed,
ortable after-methods must assume that when they are run, all of the initialization
shavior described above has been completed.
ary methods.
thods, but these must be extending, not
that when they are run, none of the initial-206 © make-instance
The results are undefined if any of these restrictions are violated.
intern-eql-speci
Function
Synrax
intern-eql-specializer
object
ARGUMENTS
‘The object argument is any Lisp object.
izer metaobject for obje
izer with eql arguments
creating one if
Il return the same
necessary. Two
(ie., eq) value.
REMARKS
The result of calling eql-specializer-« of a call to interneql-
specializer is only guaranteed to be eql to the original object argument, not necessarily
eq.
make-instance Generic Function
SYNTAX
make-instance
class brest initargs
nt is a class metaobject or a class 1
The initargs argument is a list of alternating initialization argument names and values.
Vatuss
‘The result is a newly allocated and initialized instance of class.
PuRPOsE,
‘The generic function make-instance creates and returns a new instance of the given
class, Its behavior and use is described in the CLOS specification.Generic Functions and Methods make-method-lambda 207
MerHops:
make-instance Primary Method
(class symbol) krest initargs
‘This method simply invokes make-instance recursively on the arguments (find~
lass class) and initargs
make-instance Primary Method
(class standard-class) arest initargs
make-instance Primary Method
(class funcallable-standard-class) krest initarys
‘These methods implement the behavior of make-instance described in the CLOS
specification section named “Object Creation and Initialization.”
make-method-lambda Generic Function
SywTax
make-method-lambda
generic-function method lambda-erpression environment
ARGUMENTS
‘The generic-function argument is a generic function metaobject.
‘The method argument is a (possibly uninitialized) method metaobject.
‘The tambda-expression argument: is a lambda expression,
The environment argument is the same as the &environment argument to macro
expansion functions.
VaLurs
This generic function returns two values. The first is a lambda expression, the second is
4 list of initialization arguments and values.
Purpose
This generic function is called to produce a lambda expressi
produce a method function for a method and generic function with the specified classes.
‘The generic function and method the method function used with are not required
to be the given ones. Moreover, the method metaobject may be uninitialized.
the special form function or the function coerce must
nivert the lambda expression a method function. The method function itself
can be applied to arguments with apply or funcall.
which ean itself be used to208 make-method-lambda Chapter 6
is actually called by an effective method, its first. argument will be a
list of the arguments to the generic function. Its remaining arguments will be all but the
first argument passed to call-method. By defaul hod functions must accept two
arguments: the list of arguments to the generic function and the list of next methods.
For a given generic function and method class, the applicable methods on make-
method-lambda and compute-effective-method must be consistent in the following
way: each use of call-method returned by the method on compute-effective-method
must have the same number of arguments, and the method lambda returned by the
method on make-method-lambda must accept a corresponding number of arguments.
Note that the system-supplied implementation of call-next-method is not required to
handle extra arguments to the method function. Users who define additional arguments
to the method function must either redefine or forego call-next-method. (See the
example below.)
When the method metaobject is created with make-instance, the method function
must be the value of the jon initialization argument,
arguments, returned as the second value of this generic funetion, n
this call to make-instance.
3t also be passed in
MetHops
make-method-lambda Primary Method
(generic-function standard-generic-function)
iethod standard-method)
ibda-expression
environment
‘This method returns a method which accepts two arguments, the list of ar-
guments to the generic funetion, and the list of next methods. What
arguments may be returned in the second value are unspecified.
‘This method can be overridden,
Exampl
This example shows how to define a kind of method which, from within the body of
the method, has access to the actual taobject for the method. This sim-
plified code overrides whatever method combination is specified for the generic func
tion, implementing a simple method combination supporting only primary methods,
call-next-method and next-method-p. version ofGeneric Functions and Methods make-method-lambda 200
Notice that the extra lexical function bindings get wrapped around the body
before call-next-method is called. In this way, the user's definition of call-next-
method and next-method-p are sure to override the system’s definitions.
(dotclass ay-generic-function (standard-generic-function)
°
G:default-initargs :method-class (find-class ‘ay-method)))
(defclass ay-nethod (standard-nethod) (0)
(defmethod make-nethod-Lambda ((gf ny-generic~function)
(method my-method)
‘Lambda~expression
environment)
(declare (ignore environment)
“(lambda (args next-methods this-method)
( (call-next-nethod gf method
“(lambda , (cadr lanbda-expression)
(flet ((this-method () this-method)
(call-next-nethod (krest cnn-args)
(funcall (method-function (car next-methods))
(or cnm-args args)
(cdr next-method:
/ (car next-method:
(next-method-p ()
(not (null next-method:
s@(cddr lanbda-expression)))
environment)
args next-nethods)))
(detnethod compute-effective-method ((gf ny-generic-function)
method-combination
methods)
‘(call-method ,(car methods) ,(cdr methods) , (car methods)))210 _map-dependents Chapter 6
map-dependents Generic Function
SYNTAX
map-dependents
rmetaobject function
ARGUMENTS
‘The metaobject argument is a class or generic functi
‘The function argument is a function which accepts one argument.
VaLvES
‘The value returned is unspecified,
PURPOSE,
This generic function applies function to each of the dependents of metaoby
in which the dependents are processed is not specified, but function is applied to each
dependent once and only once. If, during the mapping, add-dependent or remove-
dependent is called to alter the dependents of metaobyect, it is not specified wl
the newly added or removed dependent will have function applied to it.
METHODS
map-dependents Primary Method
(metaobject standard-class) function
_/ This method has no specified b
function.
‘This method cannot be overridden unless
jor beyond that which is specified for the generic
following methods are overridden as
add-dependent (standard-class t)
remove-dependent (standard-class t)
map-dependents Primary Method
(metaobject funcallable-standard~class) function
‘This method has no specified behavior beyond that which is specified for the generic
fun
ion
This method cannot be overridden unless the following methods are overridden as
well:
add-dependent (funcallable-standard-class t)
remove-dependent (funcallable-standard-class t)Generic Functions and Methods method. aun
map-dependents Primary Method
(metoobject standaré-generic-function) function
This method has no specified behavior beyond that which is specified for the generic
function.
This method cannot be overridden unless the following methods are overridden as
well:
add-dependent (standard-generic-function t)
remove-dependent (standard-generie-function t)
REMARKS
See the “Depen
facility.
mnt Maintenance Protocol” section for remarks about the use of this
Generic Function
generic functions are described together under “Readers for Method
‘page 218): method-function, method-generic-function, method-
, method-specializers, method-qualifiers and accessor-method-slot-212 Readers for Class Metaobjects Chapter 6
Readers for Class Metaobjects
In this and the immediately following sections, the “reader” generic functions which
simply return informati
together. Gener
of each, and ending
associated with a particular kind of metaobject are presented
ted first, followed by a description of the purpose
ch the specified methods for these generic functions.
The reader generic functions which simply return information associated with class
metaobjects are presented together in this section
Bach of the reader generic functions for class metaobjects has the same syntax, accept
ing one required argument called class, which must be an class metaobject; otherwise, an
error is signaled. An error is also signaled ifthe class metaobject has not been initialized
These generic functions can be called by the user or the
For any of these generic functions which returns a list,
by the implementation. The results are undefined if a portable program allows such a
list to be mutated
Generic Function
ization arguments for class, Each element of this list
jon argument. The empty list is returned if class
ization finalize-inheritance calls compute-default-initargs to com-
ents for the class. That value is associated with
the class metaobject and is returned by class-default-initargs.
‘This generic function signals an error if class has not been finalized.
class-direct-default-initargs Generic Function
class
ization arguments for class. Each element of
lization argument. The empty list is returned if
class has no direct default initialization arguments. This is the defaulted value of theReaders for Class Metaobjects 213,
class-direct-slots Generic Function
class
Returns a set of the direct slots of class
definition metaobjects
is the defaulted ization argument that was associated
with the class during ization,
irect-subclasses Generic Function
Returns a set of th asses of lass.
jon this class among
has no direct subclasses. value is maintained by the generic
functions add-direct-subclass and remove-direct-subclass,
this set are class
ir direct superclasses. The empty set
class-direct-superclasses Generic Function
class
Returns a
metaobjects. ‘The empty This
is the defaulted value lization argument that was
associated with the class during
class-finalized-p Generic Function
class
Returns true if class has been finalized. Returns false otherwise. Also returns false if
the class has not been initialized.
class-name Generic Function
class
Returns the name of class. This value ean be any Lisp object, but is usually a symbol,
or nil if the class has no name. This is the defaulted
argument that was associated with the class during init
(Also see (setf class-name).)Readers for Class Metaobjects Chapter 6
class-precedence-list Generic Function
class
Returns the class precedence list of class. The elements of this list are class metaob-
jects.
During class finalization finalize-inheritance calls compute-class-precedence-
list to compute the class prec
the class metaobject and is returned by class-precedence-list.
‘This generic function signals an error if class has not been finalized.
class-prototype Generic Function
class
of prototype instance.
‘This generic function signals an error if class has not been finalized.
class-slots Generic Function
class
Returns a possibly empty set of the slots accessible in instances of class. The elements
ince calls compute-slots to
he class metaobject and
Meruops
‘The specified methods for the class metaobject reader generic functions are presented
below.
Each entry in the table indicates a method on one of the reader generic functions,
specialized to a specified class, ‘The number in each entry is a reference to the full
description of the method. The full descriptions appear after the table.Generic Functions and Methods Renders for Class Metaobjects 215,
eee
standard-class forward- built-in
and referenced- class
funcallable- class
standard-class
ss-default-initargs 2 3 4
s-direct-default-initargs 1 4 4
1 4 4
class-direct-subclasses 9 9 7
class-direct-superclasses 1 4 7
class-finalized-p 2 6 5
class-name 1 1 8
class-precedence-list 2 3 7
class-prototype 10 10 10
class-slots 2 3 4
1. This method returns the val
ation.
the value associated with the class metaobject by finalize-
inheritance (standard-class) or finalize-inheritance (funcallable-standard-
class).
3. Th
which was associated with the class metaobject during
method signals an error.
is method returns the empty list.
method returns true.
6. This method returns false.
7. This method returns a value derived from the information in Table 5.1, except that
Jementation-speci
tation and User Speci
8. This method returns the name of the built-in class,
9. This methods returns a value which is maintained by add-direct-subclass (class
class) and remove-direct-subclass (class class). This method can be overridden
only if those methods are overridden as well
10. No behavior is specified for this method beyond that specified for the generic function,
ons are permitted as described in section “Implemen-216 Readers for Generic Function Metaobjects Chapter 6
Readers for Generic Function Metaobjects
The reader generic functions which simply return information associated with generic
funetion metaobjects are presented together in this section,
Each of the reader generic func generic function metaobjects has the same
syntax, accepting one required argument called generic-function, which must be a generic
funetion metaobject; otherwise, an error is signaled, An error is also signaled if the
generic function metaobject has not been initialized
These generic functions can be called by the user or the implementation.
The list returned by this generic function will not be mutated by the implementation.
The results are undefined if a portable program mutates the list returned by this generic
fan
generic-function-argument-precedence-order Generic Function
generie-function
Returns the argument precedence order of the generic fu is value is list of
ion of the required parameters in the lambda list of the generic
faulted value of the :argument-precedence-order initial-
generic-function-declarations Generic Function
generie-function
Returns a possibly empty
ist are declar
ist of the declarations of the generic function. The elements
ion metaobject during
generic-function-lambd: Generic F
generic-function
defaulted value of the
ction
ion or reinitialization. An error is signaled if the lambda
list has yet to be supplied.Readers for Generic Function Metaobjects 217
generic-function-method-class Generic Function.
generic-function
Returns the default method class of the generic
of the class method. This is the defaulted value of the tmethod-cl
argument that was associated with the generic function metaobject during initialization
generic-function-method-combination Generic Function
generic-function
Returns the method combination
metaobject. This is the def
argument that was associated wi
or reinitialization
generic function. ‘This is a method combination
the generic function metaobject during initialization
generic-function-methods Generic Function
generie-function
Returns the set of methods currently connected to the generic function, This is a set of
method metaobjects. This value is maintained by the generic functions add-method
and remove-method.
generic-function-name Generic Function
‘generic-fur
Returns t} of the generic function, or nil if the generic function has no name.
This is the defaulted value
jon argument that was associated
ation or reinitialization, (Also see
ye genetic function metacbject
(otf generic-function-name).)
thods for the generic function metaobject reader generic functions are
presented below.218 Readers for Method Metaobjects Chapter 6
generic-function-argument-precedence-order Primary Method
(generic-function standard-generic-function)
generic-function-declarations Primary Method
(generic-fun tandard-generic-function)
generic-function-lambda-list Primary Method
(generic-function standard-generic-function)
generic-function-method-class Primary Method
(generic-function standard~generic~function)
‘generic-function-method-combination Primary Method
(generic-function standard-generic~function)
generic-function-name Primary Method
(generic-function standard-generic-function)
No behavior is specified for these methods beyond that which is specified for their
respective generic functions
generic-function-methods Primary Method
(generic-function standard-generic-function)
No behavior is specified for this method beyond that which is specified for their re-
spective generic functions.
‘The value returned by this method is maintained by add-method (standard-
function standard-method) and remove-method (standard-generic-
function standard-method).
Readers for Method Metaobjects
‘The reader generic functions which simply return informat
metaobjects are presented together here in the format described under
Metao .
Bach of these reader generic functions have the same syntax, accepting one required
argument called method, which must be a method metaobject; otherwi
signaled. An error is also signaled if the method metaob ject. has not bee
‘These generic functions can be called by the user or the implementation.
‘Readers for ClassGenerie Functions and Methods Readers for Method Metaobjects 219
For any of these generic functions which returns a
by the implem
list to be mutated,
, such lists will not be mutated
stion. ‘The results are undefined if @ portable program allows such a
method-function Generic Function
method
Returns the method functi
n of method. This is the defaulted value of the sfunction
initialization argument that was associated with the method during initialization,
method-generic-function Generic Function
method
Returns the generic function that method is currently connected to, or
currently connected to any generic function. This
if it is not
is either generic function
t connected to any generic
is first created it is
method-lambda-list Generic Function.
‘method
Returns the (unspecialized) lambda list of method. This value is a Common Lisp
lambda list. ‘This is the defaulted
‘that was associated with the met
alization argument
method-specializers Generic Function.
thod
Returns a list of the specializers of method. 1 is a list of specializer metaob-
jects. This is the default rs initialization argument that was
associated with the method during
method-qualifiers Generic Function.
method
ers of method, This value is a list of non-
wualifiers initialization argument that
Returns a (possibly empty) list of the qual
of the220 Readers for Method Metaobjects Chapter 6
accessor-method-slot-definition Generic Function
method
This accessor can only be called on accessor
nition metaobject that defined this
initialization argument associated with the method during init
MeTHops
The specified methods for the method metaobject readers are presented below
method-funetion Primary Method
(method standard-nethoa)
method-lambda-list Primary Method
(method standard-method)
method-specializers Primary Method
(method standard-nethod)
method-qualifiers Primary Method
(method standard-method)
No behavior is specified for these methods beyond that which is specified for their
respective generic functions.
method-generic-function Primary Method
(method standard-method)
No behavior is specified for this method beyond that which is specified for its generie
function,
‘The value returned by this method is maintained by add-method (standard-
generic-function standard-method) and remove-method (standard-generic-
function standard-method).
accessor-method-slot-definition Primary Method
(method. standaré-accessor-nethod)
No behavior is specified for this method beyond that which is specified for its generic
function.Functions and Methods
Readers for Slot Definition Metaobjects
‘The reader generic functions which simply return information associated with slot defi-
nition metaobjects are presented together here in the format described under “Readers
for Class Metao "
has the same syntax,
‘These generic functions ean be called by the user or the implementation.
For any of these generic functions which returns a list, such lists will not be mutated
by the implementation. The results are undefined if a portable program allows such a
list to be mutated.
GeNERIC FUNCTIONS
slot-definition-allocation
slot
slot-definition-initform Generic Function
slot
Returns the form of slot. This can be any form. This is the defaulted
value of the
inition metaobject du
form initialization argument that was associated with the slot def-
When slo lization form, the
value returned is unspecified (however, slot-definition-initfunction is guaranteed to
return nil).222 Readers for Slot Definition Metaobjects Chapter 6
slot-definition-initfunction Generic Function
ft
Returns the of slot. This value is
guments, or sk
a function of no ar-
has no initialization function. This is th
defaulted val mn argument that was associated with
the slot det ion
slot-definition-name Generic Function
slot
Returns the name of slot. This value is a symbol that can be used as a variable name.
ization argument that was associated with the
slot-definition-type Generie Function
slot
Ret alloca
value of the mame
metaobject during
. This is a type specifier name. This is the defaulted
ation argument that was associated with the slot definition
ation.
MetHops
‘The specified methods for the slot definition metaobject readers are presented below.Generic Functions and Methods Readers for Slot Definition Metaobjects 223,
slot-definition-allocation Primary Method
(slot-definition standard~slot-def inition)
itargs Primary Method
form Primary Method
ym standard-slot-def inition)
slot-definition-initfunction Primary Method
(slot-definition standard~slot-def inition)
slot-definition-name Primary Method
definition standard-slot~definition)
slot-definition-type Primary Method
(slot-definition standard-slot-def inition)
No behavior is specified for these methods beyond that which is specified for their
respective generic functions.
Direct Stor DrFiNtTiON MeraopyecTs
‘The following additional reader generic functions are defi
metaobjects.
d for direct slot definition
slot-definition-readers Generic Function
direct-slot
Returns a (possibly empty) set of readers of the direct slot. This value is
that was associated wi224 reader-method-class Chapter 6
slot-definition-readers Primary Me
yn standard-direct-slot-definition)
rs Primary Method
mn standard-direct-slot-definition)
No behavior is specified for these methods beyond what is specified for their generic
functions,
TON METAOBIE
Generic Function
Retums the location of effect ‘The meaning and interpretation of
this value is described in the sec lled “Instance Structure Protocol.
slot-definition-locatic
Ceffective-slot-defir
This method returns tt jpute-slots :around (standard-class)
and compute-slots :around (funcallable-standard-class)
Primary Method
_standard-ef fective-slot-definition)
by e:
reader-method-class Generic Function
SYNTAX
reader-method-class
class direct-slot krest initargs
AnGuMEnts
‘The class argument is a class metaobject:
‘The direct-slot argument is a direct si
‘The initargs argun
metaobject.
ation argumer
VaLues
he value returned is a class metaobject.Generic Functions and Methods remove-dependent 225
PURPOSE
is generic function is called to determine the class of reader methods created during
class tion and reinitialization. The result must be a subclass of standard-
reader-method.
‘The initargs argument must be the same as will be passed to make-instance to create
the reader method. The initargs must include :slot-definition with slot-definition as
its value,
METHODS:
reader-method-class Primary Method
(class standard-class)
reader-method-class Primary Method
(class funcallable~standard-class)
direct-slot standard~direct~slot-def inition)
798
These methods return the class standard-reader-method. These methods can be
overridden,
remove-dependent
SyNTAX
remove-dependent
metaobject dependent
ARGUMENTS
‘The metaobject argument is a class or generic function metaobject.
‘The dependent argument is an object.
VALUES
‘The value returned by this generic function is unspecified.
Purpose,
This generic function removes dependent from the dependents of metaobject. If dependent
ts of metaobject, no error is signaled.
led to access the set of dependents
yn add-dependent can be called to
add an object from the set of dependents of a class or generic function. The effect of226 remove-dependent Chapter 6
calling add-dependent or remove-dependent while a call to map-dependents on
the same class or generic function is in progress is unspecified.
‘The situations in which remove-dependent is called are not specified,
Merops
remove-dependent Primary Method
(class standard-class) dependent
No behavior is specified for this method beyond that which is specified for the generic
funetion,
This method cannot be overridden unless the following methods are overridden as
well:
add-dependent (standard-class t)
map-dependents (standard-class t)
remove-dependent Primary Method
(class funcallable~standard-class) dependent
No behavior is specified for this method beyond that which is specified for the generic
function,
‘This method ¢
well
add-dependent (funcallable-standard-class t)
less the
not be overri wing methods are overridden as
map-dependents (funcallable-standard-class t)
remove-dependent Primary Method
Caeneric-fu wtandard-generic-function) dependent
No behavior is specified for this method beyond that which is specified for the generic
fi
is method cannot be overridden unless the following methods are overridden as
wel:
add-dependent (standard-gener
REMARKS
See the “Dependent Maintenance Protocol” section for remarks about the use of this
faciGeneric Functions and Methods remove-direct-method 227
remove-direct-method 7 Generic Function.
SynTax
remove-direct-method
specializer method
lizer argument is a
The method argument is am
alizer metaobject
hod metaobject.
VALUES
‘The value returned by remove-direct-method is unspecified,
PURPOSE
This generic function is called to maintain a set of backpointers from a specializer to the
set of methods specialized to it. If method is in the set itis removed. If it is not, no error
is signaled.
This set can be accessed as a lst by calling the generic function spectalizer-direct-
methods. Methods are added to the set by add-direct-method.
‘The generic function remove-direct-method is called by remove-method whenever
method is removed from a generic function. It is called once for each of the specializers
of the method. Note that in cases where a specializer appears more than once in the
specializers of a method, this generic function will be called more than once with the
same specializer as argument.
The results are undefined if the specializer argument is not one of the specializers of
the method argument.
“MerHops
remove-direct-method Primary Method
(specializer class)
(method method)
‘This method implements the behavior of the generic function for class specializers. No
behavior is specified for this method beyond that which is specified for the generic
function.
‘This method cannot be overridden unless the following methods are overrid
add-direct-method (class method)
t-generic-functions (class)
specializer-direct-methods (class)228 —remove-direct-subelass Chapter 6
remove-direct
\ethod Primary Method
(specializer eql-specializer)
(method method)
This method implements the behavior of the generi
behavior is specified for this method beyond that which is specified for the generic,
funetion
remove-direct-subclass Generic Function
SyNTAX
remove-direct-subclass
supe
36 subclass
ARGUMENTS
‘The superclass argument is a class metaobject.
‘The subclass argument is a class metaobject.
VaLues
‘The value returned by this generic function is un
OSE
‘This generic functi
called to maintain a set of backpointers from a class to its direct
8 from the set of direct subclasses of superclass, No error
lass is not in this set.
ized, this generic functi
is called once with each deleted
remove-direct-subclass Primary Method
(superclass class)
(subclass class)
No behavior is specified for this method beyond that which is specified for the generic
cannot be overridden
less the following methods are overridden 38
add-direct-subelass (class class)
class-direct-subclasses (class)Generic Functions and Methods remove-method 229
remove-method "Generic Function
SyNTAX
remove-method
generic-function method
ARGUMENTS
‘The method argument is a method metaobject.
VaLues
The generic-function argument is returned.
Purpose
This generic function breaks the association between a generic function and one of its
methods,
No error is signaled if the method is not am
Breaking the association between the method and the generic function proceeds in four
steps: (i) remove method from the set returned by generic-function-methods and ar-
range for method-generie-function to return nil; (ji) call remove-direct-method
et the generic function
install its result wit
dents of the generic function,
1
tion,
MerHiops
remove-method Primary Method
(generic-function standard-generic-function)
(method standard-nethod)
No behavior is specified for this method beyond that which is specified for the generic
funetion,230 (setf classname) Chapter 6
set-funcallable-instance-function ” Function
Sywrax
set-funcallable-instance-function
{funcallable-instance function
ARGUMENTS
instance (it must have been returned
le-standard-class) ).
‘The function argument is a function.
VaLuss
‘The value returned by this function is unspecified.
Purpose,
‘This fu
will run the new function,
(setf class-name) Function
SywTax
(setf class-name) Generic Function
ARGUMENTS
‘The class argument is a class metaobject.
‘The new-name argument is any Lisp object,
Resuurs
This function returns its new-name argument.
Purpose
‘This function changes the name of class to new-name, ‘This value is usually a symbol,
‘This function works by calling
‘the symbol :name as its second arg
stance with class as its first argument,
vew-name as its third argument.Generic Functions and Methods name) 231
(setf generic-function-name) ” Function
SYNTAX,
(setf generic-function-name) Generic Function
new-name generic-function
ARGUMENTS
‘The generic-function argument is a generic function metaobject.
The new-name argument is a function name or nil.
Resuits
‘This function returns its new-name argument.
PURPOSE
This functio e name of generic-function to new-name.
a function name (ie., a symbol or a list of the
function is to have no name.
value is usually
(setf sym
he generic
T unction works by calling rei \lize-instance with generic-function as its first
argument, the symbol :name as its second argument and neu-name as its third argu-
ment.
(setf slot-value-using-class) Generic Function
Sywrax
(setf slot-value-using-class)
new-value class object slot
ARGUMENTS
‘The new-value argument is an object.
‘The class argument is a class metaobject. It is the class of the object argument.
‘The object argument is an object.
ive slot definition metaobject.
‘The slot argument is an eff
VaLuEs
jeneric function returns the new-value argument,
Purpose
we generic function (setf slot-value-using-class) implements the behavior of the
(setf slot-value) It is called by (setf slot-value) with the class of object232 (setf slot-value-using-class) Chapter 6
as its 8
argument.
‘The generic function (setf slot-value-using-class) sets the value contained in the
given slot of the given object to the given new value; any previous value is lost.
‘The results are undefined if the class argument is not the class of the object argument,
or if the slot argument does not appear among the set of effective slots associated with
the class argument,
ive slot definition metaobject as its fourth
MetHops
(setf slot-value-using-class) Primary Method
new-value
(class standard-class)
object
(slot standard-effective-slot-def inition)
(setf slot-value-using-class) Primary Method
ue
funcallable-standard-class)
standard-effective-slot-definition)
se methods implement the full behavior of this generic function for slots with
ted, b require overriding other methods
standard implementation of the slot access protocol
(setf slot-value-using-class) Primary Method
new-value
(class built~in-class)
object
slot
‘This method signals an error.Generic Functions and Methods slot-boundp-using-class 298
slot-boundp-using-class Generic Function
SYNTAX
slot-boundp-using-class
class object slot
ARGUMENTS
‘The class argument is a class metaobject. It is the class of the object argument.
‘The object argument is an object
‘The slot argument is an effective slot definition metaobject.
VALUES
‘This generic function returns true or false
OSE,
‘This generic function implements the behavior of the slot-boundp function. It is called
by slot-boundp with the class of object as its first argument and the pertinent effective
slot definition metaobject as its third argument.
‘The generic function slot-boundp-using-class tests whether a specific slot in an
instance is bound
‘The results are undef
if the class argument is not the class of the object argument,
or if the slot argument does not appear among the set of effective slots associated wi
the class argument.
MeTHops
slot-boundp-using-class Primary Method
(class standard-class)
object
(slot standard-effective-slot-def inition)
slot-boundp-using-class Primary Method
(class funcallable-standard-class)
object
(slot standard-effective-slot-def inition)
1ods implement the full behavior of this generic function for slots with
‘the supplied slot has an allocation other than
but may require overriding other methods
in the standard implementation of the slot access protocol.234 slot-makunbound-using-class Chapter 6
slot-boundp-using-class Primary Method
lass built-in-class)
object
slot
‘This method signals an error.
REMARKS
In cases where the class metaobject class does not distinguish unt
be returned,
slot-definition-... : Generic Function
‘The following generic functions are described together under “Readers for Slot
mn Metaobjects” (page 221):
slot-definition-initfort
location, slot-definition-name, slot-defi
and slot-definition-type.
n-readers, slot-definition-writers
slot-makunbound-using-class Generic Function
Syntax
slot-makunbound-using-class
class object slot
ARGUMENTS
‘The class argument is a class metaobject. It is the class of the object argument.
‘The object argument is an object.
‘The slot argument is an effecti
definition metaobject.
Vavu
‘This generic function returns its object argument,
Purpose
This generic function implements the behavior of the slot-makunbound function. It
is called by slot-makunbound with the class of object as its first argument. and the
pertinent effective slot definition metaobject as its third argument,
The generic function slot-makunbound-using-class restores a slot in an object to
its unbound state. The interpretation of “restoring a slot to its unbound state” depends
con the class metaobject classGoneric Functions and Methods slotwvalueusing-class 285
The results are undefined if the class argument is not the class of the object argument,
or if the slot argument does not appear among the set of effective slots associated with
the class argument.
MerHops:
slot-makunbound-using-class Primary Method
(class standard-class)
object
(slot standard-effective-slot-definition)
slot-makunbound-using-class Primary Method
(class funcal lable-standard-class)
object
(slot standard-ef tective-slot-detinition)
1ods implement the full behavior of this generic function for slots with
allocation :instance and :elass. If the supplied slot has an allocation other than
tance or :elass an error is signaled.
Overriding these me yermitted, but may require overriding other methods
in the standard implementation of the slot access protocol.
slot-makunbound-using-class Primary Method
(class buiit-~in-class)
object
sl
‘This method signals an error.
slot-value-using-class Generic Function
Syntax
slot-value-using-class
class object slot
ARGUMENTS
lass argument is a class metaobject. It is the class of the object argument
object argument is an object.
The slot argument is an effective slot definition metaobject.
Vavues
‘The value returned by this generic function is an object.286 slot-value-using-class Chapter 6
PURPOSE
This generic function implements the behavior of the slot-value function. It is called by
slot-value with the class of object as its frst argument and the pertinent effective slot
definition metaobject as its third arg
the value contained in the given
ibject. If the slot is unbound slot-unbound is called.
defined i is not the class of the object ary
argument does not appear among the set of effective slots associated with
MErHops:
slot-value-using-class Primary Method
lass standard-class)
4
standard-effective-slot-def inition)
slot-value-using-class Primary Method
(class funcallable~standard-class)
standard-offective-slot-def inition)
n for slots with
than
‘These methods implement the full behavior of this generic funet
ied slot has an alloc
may require overriding other methods
access protocol.
slot-value-using-class Primary Method
(class built-in-class)
object
slot
‘This method signals an error.Generic Functions and Methods
specializer-direct-generic-functions
Generic Punction
izer-direct-generic-functions
specializer
ARGUMENTS
The specializer argument is a specializer metaobject.
VALUES
‘The result of this generic function is @ possibly empty list of generic function metaobjects
PURPOSE,
‘This generic function returns the possibly empty set of those generic functions which have
fa method with specializer as a specializer. The elements of this set are generic function
Tneteobjects. This valve is maintained by the generic functions add-direct-method
and remove-direct-method.
Merxos
specializer-direct-generic-functions Primary Method
(specializer class)
No behavior is specified for this method beyond that which is specified for the generic
function.
‘This method cannot be overridden unless the following methods are overridden as
well:
add-direct-method (class method)
remove-direct-method (class method)
specializer-direct-methods (class)
specializer-direct-generic-functions Primary Method
(specializer eqi~specializer)
No behavior is specified for this method beyond that which is specified for the generic
function.238 specializerdirect-methods Chapter 6
specializer-direct-methods ~ Generic Function
SyNTAX
specializer-direct-methods
specializer
ARGUMENTS
er argument is a specializer metaobject.
VALUES
e result of this generic function is a possibly empty list of method
aobjects
generic
method metaobjects.
dat
-ect-methods Primary Method
r class)
No behavior is specified for this method beyond.
function.
at whieh is specified for the generic
This method cannot be overridden unless the followin
n
add-direct-method (class method)
remove-direct-method (class method)
specializer-direct-generic-functions (class)
hods are overridden as
specializer-direct-methods Primary Method
(specializer eql-specializer)
No behavior is specified for t
funetis
1d beyond that which is specified for the genericGeneric Functions and Methods standard-instance-access 239
standard-instance-access Function
SYNTAX
standard-instance-access
instance location.
ARGUMENTS
‘The instance argument is an object.
‘The location argument is a slot location,
Vawues
‘The result of this function is an abject.
PURPOSE
‘This function is called to provide direct access to a slot in an instance, By usurping the
lookup protocol, this function is intended to provide high!
to the slots associated with an instance.
‘The following restrictions apply to the use of this function:
normal nized access
‘* The instance argument must be a standard instance (it must have been returned by
stance (standard-class)).
‘The results are undefined if any of these restrictions are not met.
update-dependent Generic Function
Synrax,
update-dependent
‘metaobject dependent Brest initargs
ARGUMENTS
‘The metaobject argument is a class or generic function metaobject. It is the metaobject
being ri ed or otherwise modified,
‘The dependent argument is an obj
‘The initargs argument is a list of the i
definition.
is the dependent: being updated.
ization arguments for the metaobject re-240 —_validate-superclass Chapter 6
Vavurs
‘The value returned by update-dependent is unspecified
Purpose,
‘This generic function is called to update a dependent of metaobject.
When a class or a generic
ization arguments received
When a method is added to a a
dents is updated. ‘The initargs ar
method, and the method that was added,
When a method is removed from a generic function, each of the generic function’s
dependents is updated. The initargs argument is a list of two elements: the symbol
remove-method, and the method that was removed
In each case, map-dependents is used to call update-dependent on each o!
dependents. So, for example, the update of a generic function's dependents when a
‘method is added could be performed by the following co
depen-
of two elements: the symbol add-
(map-dependents generic-function
ambda (dep)
(update-dependent generic-fun
dep
‘add-method
new-method)))
Mernops
‘There are no specified methods on this generic function.
See the “Dependent Maintenance Protocol” section for remarks about the use of this
facility.
validate-superclass Generic Function
AX
validate-superclass
class superclass
ARGUMENTS
The class argument is a class metaobject.Generic Functions and Methods
241
‘The superclass argument is a class metaobject.
VaLuEs
‘This generic function returns true or false
PURPOSE,
This generic function is called to determine whether the class superclass is suitable for
use as a superclass of class.
This generic function can be
ing class
by the implem for user code. It is called
4 aobject initialization and reinitialization, before the direct superclasses
are stored. If this generic function returns false, the in
signal an error.
ization or reinitialization will
MetHops
validate-superclass Primary Method
(class class)
(superclass class)
This method returns true in three situations:
he superclass argument is the class named t,
if the class of the class argument is the same as the class of the superclass
one of the arguments is standard-class and the class of the
‘This method can be over
REMARKS:
Defining a method on validate-superclass
protocol followed by each of the two class metaobject classes. A meth
superclass which returns true for two different class metaobject classes declares that
they are compatible.242 Chapter 6
writer-method-class Generic Function
Sywrax
writer-method-class
class direct-slot krest initargs
ARGUMENTS
‘The class argument is a class metaobject.
The direct-slot argument is a direct slot definition metaobject.
‘The initargs argument is a list of initialization arguments and values.
Vatues
‘The value returned is a class metaobject.
function is called to determine the class of writer methods created during
class initialization and reinitialization. The result must be a subclass of standard-
writer-method.
il be passed to make-instance to create
lot-definition with slot-definition as
‘The initargs argument must be the same as
the reader method. The initargs must i
its value,
writer-method-class Primary Method
(class standard-class)
standard-direct~slot-definition)
798
writer-method-class Primary Method
(class funcallable-standard-class)
lot standard-direct-slot-definition)
rest initargs
These methods returns the class standard-writer-method. These methods can be
overridden.A An Introduction to the Common Lisp Object System
is appendix, we provide a brief introduction to the basic concepts and syntax of
the Common Lisp Object System (CLOS) for people who already have some familiarity
with object-oriented programming. On a topic by topic basis, we compare the concepts
and vocabulary of CLOS with those of two other popular object-oriented languages,
Smalitalk[Goldberg&Robson 83] and C++} For each topie there
is an overview, language specific terminology, a description of the relevant features of
CLOS, followed by brief contrasting descriptions of Smalltalk and C++. A summary
chart comparing terminology and features of CLOS, Smalltalk, and C++ can be found
on page 253,
A.1 Classes and Instances
In C++, instances are called objects of a class and slots are called
member data elements.
ns can specify, for each slot, as little as just the name, or can
ide a number of slot options. For example, here is a CLOS definition for a class
named rectangle:
(defclass rectangle ()
((height :initarg :start-height
Hinitforn 5
accessor rectangle-height)
(width :initarg :start-~widen
Anitform 8
accessor rectangle-width)))
This definition specifies that each instance of rectangle will contain two slots named
height and widen. Each slot is specified by a list starting with its slot name, followed
by a number of slot options. A slot option is a keyword (a reserved symbol starting with
‘ colon) followed by the option value. For example, the slot named height is followed
by three options, with keywords :initarg, :initfora, and :accessor. The first twogeneric function. This generic provides access to this slot for instances of this
class. For example, rectangle-height fetches the value of the height slot of instances
of rectangle
New instances of CLOS classes are created by calling the function make-instance with
the class name as frst argument:
(eetq ri (make-instance ‘rectangle :start-height 50 :start-width 100))
‘The result of evaluating this expression is to set the variable r1 to a newly created
instance of rectangle. Because of the :initarg
height and :start~width in this form can serve as keywords to initialize the slots height
d width to 50 and 100, respectively. If an initial value is not provided on a call to
\d width would be 5 and 8 respectively.
talk, only the names of the instance variables are
Instances are created by sending the special
can be included
des initial val
at method. It is more typical to provide a creation message which
to be sent to the class; this allows, for example, a Rectangle to
and 100 by sending a message like
be created and initi
Rectangle startHeight: 50 startWidth: 100
ang a
In C++, he name and type of member data elements
class definition. Default initializ -pressions can be supplied for each member data,
clement, Objects of class are created by calling an automatically defined constructor
fun 1 the class, C++ has mechanisms for defining initial value arguments for the
constructor function; if no value is pri he default initialization
expression in the class definition is used to determine the initial value for a new instance's
member data clement.
A.2. Structure Inheritance
Object-oriented programming languages support the specification of a new class as an
incremental modification of other previously defined classes, A new class is said to inherit
from these other classes, means that its effective definition is a combination of
‘what is explicit in its own definition and what is in those of classes that it inherits from.Introduction to CLOS 245
In CLOS and Smalltalk, the terminology used is that a new subclass is defined as a
specialization of a previously existing superclass. In C. terminology used is that
a new derived class is defined in terms of a previously existing base class.
n this way, if the same slot name
8 as to how many
gle slot is
name that appears in its class or in any
e definition with the same slot name, the
For example, consider this definition of
color-rectangle as a subclass of rectangle:
(detclass color-rectangle (rectangle)
((color :initform 'red
initarg :color
raccessor color)
(clearp :initform nil
Hinitarg :clearp
raccessor clearp)
(height :initform 10)))
Given the earlier definition of rectangle, this definition causes instances of color-
rectangle to have four slots: the newly-defined slots color and clearp, and the slots
height and width inherited from rectangle. The last line of the color-rectangle class
definition does not define a new height slot, but provides a new default initial value for
slot of that name inherited from rectangle. Furthermore, this example illustrates
in C++, declaring a like-named member data element in a
member data element of that name to be part of any object of that class, even if another
iption, we omit Smalltalk clase variables which are accessible to all members of a
lags, We azo omit the coresponding cla variables of CLOS, and sate data elements of C++ classes,246 Appendix A
declaration with the same name appears in a base class. Bach declaration carries its own
type specification and its own initialization.
‘Some of the most complex issues having to do with inheritance only arise when multiple
inheritance is allowed; that is, when a new class can be made to inherit from more than
‘one superclass. Both CLOS and C++ support: use of multiple inheritance. Some versions
of Smalltalk have experimented with multiple inheritance; however, it is not a generally
supported feature.
‘One typical way multiple inheritance is used is to define mizin classes that capture
‘useful independent fragments of structure and behavior. Such mixins are usually not
designed to be instantiated on their own. Instantiable classes are then defined i
of one principal class and one or more these mixins. For example, @ color-mixin class
might capture the 1: and structure peculiar to colored objects, and be used in the
following alternative det
nn of color-rectangle
(defclass color-mixin ()
((color :initform ‘red
Hinitarg :color
accessor color)))
(defclass color-rectangle (color-mixin rectangle)
((elearp :initform nil
Anitarg :clearp
accessor clearp)
(height sinitform 10))
This definition of color-mixin
associated with colored objects (for exat
rporates a description of the slot colors
changing the color) would also be associated
with this mixin,
When mixins are not independent, rules for combinations must be used. In CLOS,
the combination rules depend on use of a linearization of the set of superclasses
will not be described here. In C++, users are forced to explicitly resolve clashes from
multiple base classes.
A.3. Classes and Operations
ented programming languages, operations are composed of independently
defined implementations specific to particular classes. In CLOS and Smalltalk, the inde-
pendently defined implementations are called methods. In C++, they are called virtualIntroduction to CLOS 17
member functions. When an operation is invoked, a runtime dispatch selects the appro
priate implementation. In CLOS, the terminology for invoking an operation is calling a
alk, sending a message; in C++, calling a member function,
ges differ on how methods are associated with a
rt
is defined on both a subclass and one of its
superclasses, the first is said to shadow the second, meaning that only the more specific
one will automatically be executed. All object-oriented programming languages provide
‘some way for the more specific method to invoke the shadowed method explicitly. If the
shadowed method is executed, the more specific method is said to eztend the shadowed
method; if the shadowed method is not executed, then the more specific method is said
to override the shadowed method,
CLOS, Smalltalk and C++ use significantly different ways of associating a method
with a class, syntax for invoking a generic operation, mechanisms for dispatch to the
appropriate implementation, and syntax for invoking a shadowed method,
In CLOS, methods are associated as much with generic functions as with classes. Sep-
arately defined methods implement the generic function's behavior for different. classes.
A generic function definition specifies the interface common to all methods, namely, the
generic function name and argument list:
(defgeneric paint (shape medium))
A method is associated with a class if a parameter of the method is specialized to that
class. Semantically, what this means is that an arg round to that parameter must
be an instance of that class (or any of its subclasses). Here are two methods for paint,
one specialized to rectangle and another to circle (the definition of the class circle
is not shown):
(defmethod paint ((shape rectangle) medium) 5 method 1
(vertical-stroke (rectangle-height shape)
‘tangle-width shape)
ium) )
(defmethod paint ((shape circle) mediun) ; method 2
(draw-circle (radius shape)
‘nediun))
In this notation, the argument list to the method indicates how the method is specialized.
Bach element of the argument list is either a parameter name (e.g., medium), ot a pair
consisting of a parameter name and the name of a class (e.g., shap248 Appendix A.
‘method 1 to be applicable, that is used on a call to the generic function, the parameter
bound to shape must be an instance of the class rectangle (or any of its subclasses).
In this use, the class name rectangle is called a specializer.
A call to a generic function looks exactly like a call to an ordinary function’
(paint ri sstandard-display*)
If rt is bound to an instance of a rectangle, this call to the generic function
1 specialized for rectangle. Ifit were an instance of eirele, dis
kes the applicable method.
CLOS, the function cal1-next-nethod is used within
the body of a method. For example, in CLOS we might have
(dofnethod paint ((shape color-rectangle) medium) 3 method 3
(unless (clearp shape)
(cal1-next-nethod)))
This method, specialized on color-rectangle, shadows the method on rectangle. Un-
less (cLearp shape) is true, the shadowed method is called, passing the sam
In Smalltalk, a method is usually associated with a class through a visual browser, since
Smalltalk is almost always used in a re The operation associated
entified by a sy lector. When behavior is
a message to an
ist of the selector and
other arguments for the operation, The method to be run is located using the selector
8 a key in a method table associated with the class of the object. If the selector is not
found in that table, the corresponding search is made in the superclass of the class.
‘The Smalltalk syntax for sending a message emphasizes the receiver of the message,
patting the object first, followed by the selector, and then any other arguments; 6
ri paint: standardDisplay
in a method, to send a different message to the same object, a message is sent
to the pscudo-variable self. To invoke a shadowed method, a message is sent to the
pseudo-variable super:
super paint: standardDisplay
A Sipalltalk fle based syntax is defined but is almost never used by programm
system when saving definitions for reloading,Introduction to CLO 249
‘The only difference between sending a message to self and super is that the search for
the appropriate method associated with the selector skips the class of the object itself
when the message is sent to super.
C++ is a language based on static scoping rules. Specific virtual member functions
are associated with a class by appearing within the name scope of that class definition,
ial member functions support runt
language,
appropriate imp! only an indexed lookup
before dispatch to the specific code. Not all member functions in a C++ are declared
virtual. For polymorphic non-virtual functions, choice of implementation is made at
compile time based on declarations. Because of this com ie choice, these non-
virtual member functions do not support object-oriented specialization.
‘The C++ syntax emphasizes the selection of the function based on the object, with
other arguments being in standard position. To invoke paint on r1, for example, C++
ri. paint (standard_display)
This isthe same syntax used to invoke other kinds of member functions. If another virtual
member function is to be invoked on the same object, the pseudo-variable this can be
a shadowed virtual member function, a qualified name involving the
3s must be used (2g, if rectangle is the base class in which there is a shadowed
‘member function for paint, then the qualified name rectangle: :paint can be
used to refer to that virtual member function
4 derived class, shadowed methods in any or
appropriately qualified name,
re is more than one base class for
I base classes can be invoked using the
A4 Multiple argument dispatch
For some operations, itis often natural for the implementation to depend on the class of,
more than one argument. In our example for paint, the code lepend on both the
shape and the medium. Code for painting a circle on a bitmap is clearly different from
that for drawing @ circle in a page-description language, and is different from that for
painting a rectangle on either.
CLOS, method definitions can directly specify the required classes of more than one
argument. Methods where more than one argument has an explicit specializer are called
-methods. As an example, consider the following code sketch:
(defmethod paint ((shape rectangle) (medium vector-display)) ; method 4
draw vectors on display ...)250 Appendix A
jefmethod paint ((shape rectangle) (medium bitmap-display)) ; method 5
paint pixels on display ...)
(defmethod paint ((shape rectangle) (medium pdl-stream)) j method 6
create PS lines...)
(efmethod paint ((shape circle) (medium pdl-strean)) j method 7
create PS circle ...)
Both arguments to paint are specialized; for a multi-method to be applicable, each
parameter must be bound to an instance of the associated specializer class. For example,
for method 4 to be applicable, the first argument must be an instance of rectangle, and
nd, an instanee of vactor~display.
methods are a useful extension of the notion of object-oriented programming,
mary place where CLOS breaks the intuition that a method belongs to
exactly one class. Multi-method dispatch for generic functions allows a finer breakdown
of the operation into appropriate pieces.
In Smalltalk, selection of an implementation based on the types of more than one
argument to a method ean be accomplished with a “chained dispatch” [Ingalls 86). In this
example, each method for paint: would be specialized to a shape; all shape-specialized
methods send a secondary message whose selector incorporates the shape name; so the
paint: method on circle sends a paint-circle: message to the medium with self as
the argument.
medium paint-circle: self
‘The code for paint-circle: is then specialized for the medium; the selector implies the
shape is to be painted,
‘The corresponding pair of virtual member functions
run-time multi-argument selection in C++.
be used to achieve this
A.5 Structure Encapsulation
In object-oriented programming languages, the structure of an object is intended to
be opaque outside of certain restricted scopes. Access to the structure is provided by
operations associated with the class of the object. Of course, each language provides
loopholes that allow access from outside that scope.
In CLOS, the intended public access to a slot is provided by the automatically gen-
erated access functions that can be specified in class definition using the slot optionIntroduetion to CLOS 21
saccessor. For example, in the del
height slot option causes aut nition of t
height, to read the value of the slot height, and
the value of that slot. Examples of the use of these ft
for rectangle, the :accessor rectangle-
generic functions: rectangle-
rectangle-height),? to set
(rectangle-height r1) ifetches the value of the height slot
(setf (rectangle-height ri) 75) ;sets the value of height slot to 75
Direct access to named slot
are defined in terms of slot-val
provided by siet-value. Accessor generic functions
. Here are examples of the use of slot-val
(slot-value ri ‘height)
(setf (slot-value ri *height) 75)
Direct access using slot-value can be used in any context. The only requirement is that,
the slot name be known. However, it is better practice to use the accessors since they
provide the intended public access. In addition, this allows these generic fun
, replacing direct access by a computation, without requ
In Smalltalk, instance variables specified in a class Cor any superclass of C are accessi-
ble by name in methods defined on the class C. Outside of such methods only user-defined
methods and low-level implementation loopholes can access instance variables.
Because C++ classes define a name scope, virtual member functions defined within
that name scope have access to the member data elements by name. By default, virtual
member functions in derived classes do not have access to member data elements declared
in base classes. ‘To extend this private naming scope to derived classes, C++ provides a
protected specifier for member data elements; to extend the named access to all scopes, @
corresponding public specifier is available. C++ has many different loopholes to break
encapsulation. However, the preferred style for public access is through user-defined
virtual member functions for the same reason that accessor functions are preferred in
Los.
A.6 Methods in combination
ined the name of» generic function, en example of «composite name now wed in Common
Lisp for functions that set values of ela pe recone252 Appendix A
10 define an independent method that sets up initial conditions for painting, or does some
cleanup afterwards,
In any language, write the general method for paint so that it calls prepaint
and postpaint around a call to primary-paint. One could then define default methods
ions to just add preparation operations,
‘This pattern of coding was used frequently in earlier Lisp multiple inheritance object-
oriented programming languages. Language support for this idiom is provided by CLOS.
is such as those we have seen, CLOS allows the definition
methods that play specialized roles in the invocation of a
in CLOS we can define a before-method specialized on
color-mixin that will set the brush color before painting by writing:
(defmethod paint :before ((shape color-mixin))
(set-brush-color (color shape)))
In general, when a generic f
called, then the most specific primary method,
cause of the order in which they are run,
checking and/or preprocessing, and after-meth and/or auxiliary
processing, The automatic invocation of before-methods, after-methods, and primary
methods in the pattern described is called standard method combination.
Neither Smalltalk nor C++ any facilities for automatic method combination.
is invoked, all the applicable before-methods areA.7 Summary
253
accessor function
tializer keywords
GLos Smalltalk | C++
Gass class cas
[instance instance abject of ass
Instance instance variable’ | member data
elements
Tnheritance ‘subclass: subclass: derived class
direct superclasses _| superclass tage classes
Slot mame name name |
descriptions initial value forms type i
ial value forms
Slot
inheritance _
fone per name
‘one per
Invoking an
generic function
‘method method
function
specializers through browser | defined in
class seope
chained dispateh
‘chained dispatch
reference
self
thie
Tnvocation of
shadow
‘message to super
call virtual member |
function with, |
qualified name
uuser methods
‘user methods
estate public declaration
Direct access ‘slot-value by name within] by name within
to instance state method scope class scope
‘Automatic method || yes no, no
combinationB Solutions to Selected Exercises
Exercise 1.1 (p. 45) ‘The computation performed by c¢
using-clasees depends only on the generi
it can be memoized using these two values as keys. The mem
each generic function is a table whic
classes to a list of applicable methods. For simplicity, the tabl
hash table.
‘The definition of standard-generic-function is updated to include a slot for the
table.
.te-applicable-methods-
list of classes. ‘This means
tion is done in two
‘maps from a list of
just a Common Lisp
[] ccotcrass standard-genersc-tunction ©
(name :initarg :nane :accessor generic-function-nane)
Gambde-List tinitarg :lambda-list
generic-function-lanbda-List)
(methods :initforn ()
raccessor generic-function-methods)
(table :initform (make-hash-table :test #'equal)
accessor classes-to-applicable-methods-table)))
apply-generic-function is then rewritten as follows:
4 jefun apply-generic-function (gf args)
(ete ((required-classes
(eapcar #'class-of (required-portion gf args)))
(applicable-nethods
(or (gothash required-classes
(classes-to-applicable-nethods-table gf)
ail)
(sort (gethash required-classes
(classes-to-applicable-aethods-table gf)
(conpute-applicable-nethods-using-classes
st
required-classes)))))
(it (aull applicable-nethods)
(error “No matching aethod for the"@
generic function ~S,-@
vwoen called with arguments ~:8." gf args)
(apply-aethods gf args applicable-nethods))))256 Appendix B
‘The memoized values may become invalid when 8 new method is added to the generic
function. In the subset of CLOS we are working with, no other events can invalidate
these entries.' This means that add-method must be modified to clear the table.
[7] cata acne (ef antboo
(ett (nethod-generic-function method) ef)
(push nethod (generic functson-aethods f))
Golist (epecializer
(pushnew eth
(clrhash (classes-to-applicable-nethods-table gf))
ethod)
Note that these modifications cannot be made to the complete Closette in Appendix D
the system is kept in some global variable,
initialize-instance for class metaobjects
list rather than create the direct subclass
two lines of that method
(page 23) is modified to push
links. This is done by replacing tl
4 (dolist (superclass (class-direct-superclasses class))
(push class (class-direct-subclasses superclass)
with
] coum cass eanr-ctassese)
‘Then class-direct-subclasses can be computed from the global list as follows:
] (aetun caase-airect-subel
(remove-if-not # (lambda
(wouber class (class-direct-superclasses ¢)))
sall-classese))
(class)
‘This strategy depends on the freedom provided by the second rule since each call to
class-direct~subclasses constructs and returns a fresh list structure.
Exercise 2.2 (p. 58) A class is at the apex of a diamond if it has two distinct direct
subclasses which themselves have a common subclassSolutions to Selected Exercises 257
Af (setun nas-dianond-p (class)
(sone #' (lanbda (pair)
(not (null (conmon-subclasses* (car pair)
(cadr paix)))))
(all-distinct-pairs (class-direct-subclasses class))))
(defun comnon-subclasses* (clas:
Gntersection (subclasses* cl:
ass-2)
) (subclasses* class-2)))
‘The helping function a11-distinct~pairs returns the set of all distinct two-element
subsets of a given set.
Af intsa micdstinctpaire (ee
it can sen
oO
Chop (enpcas # Cae (es)
Ghee (ar eat) nee
(cdr set))
(all-distinct-pairs (cdr set)))))
2.3 (p. 64). Es lly, the exercise is to produce a form which describes
18 which apply-generic-function and apply-methods would actually take.
‘The solution is a straightforward adaptation of those functions. Free use le of
several internal functions from Closette, all of which could have been defined using the
documented metaobject accessors.
4 (dotun display-effective-method (gf args)
(let ((applicable-methods
(compute-applicable-nethods-using-classes
gt (mapcar #'class-of (required-portion gf args)))))
(print
Gf (null applicable-methods)
"(error "No applicable methods.")
(generate-effective-nethod gf applicable-methods)))))258 Appendix B
Te
Geciare (ignore #2)
(abels ((generate-method (method)
Mwethod ,0(cdr (generate-defnethod
method :show-body t))))
Ggenerate-call-nethod (nethod next-nethods)
*(eall-nethod
"(generate-nethod method)
“Gaapear # generate-nethod next-nethods))))
(let ((primaries (enoverif-not #'primary-nethod-p methods))
Goetores. (ronove-if-not #before-nethod-p nethods))
(afters (renoverif-not #'after-aethod-p nethods)))
(af Gault primaries)
‘(error "No primary method")
“grog
"eaapear
¥' Clanbda (aothoa)
(Generate-call-nethod method ())
betores)
(muitiple-velue-progt
merate-cell-nothod (car primaries)
(cdr primaries))
,@(mapear
#" (Lambda (method)
(generate-call-nethod method ()))
(reverse afters))))))))
Exercise 2.4 (p. 66) The first approach can be implem:
slot to method metaobjects. When its value is reader,
value is writer, the method is a writer; and when its v
a reader nor a writer.
ed by adding a si
reader; when its
, the method is neither
| ccotciass standard-neothod ©
ce
(reader/writer :initfora nil
initarg :reader/uriter)
ySolutions to Selected Exercises 259
fe this slot, the functions add-reader-method and add-vriter~
nethod (p. 39) must be updated to supply the :reader/writer initialization argument.
The predicates can then be implemented as:
[ent unroetaep 0
Ce TRESS anectane ‘uancartantbod)
Teer. eteereseen reason)
(defun writer-method-p (x)
(and (eq (class-of x) (find-class 'standard-nethod))
(eq (slot-value x 'reader/uriter) 'writer)
‘The second approach requires two new class definitions, standard-reader-method and
standard-vriter-nethod. Each is defined as a subclass of standard-method, In this
case, add-reader-nethod and add-writer-method must be modified to create
of these new classes rather than standard-method. This is the previous
code for these functions with the code for ensure-nethod (p. 38), ‘add-reader~
‘method is shown, the definition of add-writer-method is analogous.)
4 (detun add-reader-nethod (class fo-nane slot-nane)
(add-method
(onsure-generic-function fn-nane :lambda-list ‘ (object))
(nake-instance ‘standard-reader-method
Lambda-list ' (object)
rqualifiers
specializers (list class)
ody ‘(slot-value object ',slot~nane)
environment (top-level-environnent)))
(valves)
Bach strategy is effective at capturing a difference between objects. In the first case,
the difference is captured in the value of a slot. In the second, it is captured in the class
of the object. Each strategy is appropriate in certain situations, and often the choice of
Gihich to use is simply a matter of taste. But, in general, using the class of the object
vvll mako the implementation more extensible. Notice, for example, that the eq test on
he class of the object in the frst solution effectively prohibits subclassing of standard
tethod, In the full MOP, the latter solution is taken; there are special classes for reader
‘and writer method.
Exercise 3.1 (p. 83) As suggested in the exercise, an around-method is used to
default the :direct-superclasses initialization argument. When the supplied value260 Appendix B
t, a list of just vanilla-flavor is used instead. ‘The class vanilla~
d as a subclass of standard-object, so the class precedence list
will end in (... vanilla-flavor standard-object t). To
avoid circularity problems, the class vanilla-flavor itself is defined as an instance of
standard-class.?
f (deteiass vanilie-tiaver 0 0)
(defmethod initialize-instance :around (class flavors-class)
brest all-keys
akey direct-superclasses)
(apply #'cal1-next-method
class
:direct~superclasses (or direct-superclasses
(list (find-class 'vanilla-flavor)))
all-keys))
3.2 (p. 89) The solution is based on two important properties of the desired
iat slots in encapsulated classes are effectively named by a pair of val-
1d class names from the defclass macro. Second, there
suggests a way to use the exist
behavior. First, each direct slot is assigned
in the defclass form. A mapping is maintained from pairs of (pretty-
10 the unique names. Because each direct slot has a unique name, no
ion of (what would otherwise be) like-named slots is done. Given a pretty name
and a class, the unique name can be determined, and access can then be done in the
normal way.
‘The earliest convenient point to assign unique names is during initialization of the
class metaobject. A specialized around-method edits the slot property lists before calling
call-next-nethod.’
preserve the previous niquejons to Selected Exercises 261
# Glanbda (slot-properties)
Get ((pretty-nane (getf slot-properties ':nane))
(new-properties (copy-list slot-properties)))*
(setf (gett nev-properties ':nane) (gensyn))
(eott (gett nev-properties ':pretty-nane) pretty-nane)
now-properties))
direct-slots)))
(apply #'call-next-aethod class
:direct-slots revised-direct-slots
all-keys)))
‘The pretty name is hung on the :pretty-name property, with the assumption that it
will be stored with the direct slot definition metaobject where it can later be retrieved
with the function slot-definition~pretty-nane. These accessors could be defined in
the same way as elot-definition-initforn (290).
‘The function private-slot-value and
slot-nane which searches a class's list
name, returning the unique name of the s!
doesn't,
‘are all defined in terms of private-
ching roe wth agen rely
WH nds oe and reporting ear
_f tetea evra siot-vaive Gnttance slot-ane claee)
Gclo'vtun intene (rivece-siot-naw elton cast)
(dofun private-slot-nane (slot~nane class)
(let ((slot (find slot-nane (class-direct-slots class)
key #'slot-definition-pretty-name)))
Gf stot
(slot-definition-name slot)
(error "The class “S has no private slot naned “S."
class slot-nane))))
Not by renaming the slots before
lization, any automatically generated
“The cnane property necds to be changed. But, the
modifications ofthe arguments, o's copy must be mide262 Appendix B
Exercise 3.3 (p. 94) _ The specialized method handles the :default~initargs op-
tion, expanding it according to the rules described earlier. If the option is not :default-
initargs, call-next-method is invoked which will cause the standard method to
the appropriate error.
d (efmethod canonicalize-defclass-option
((sample-class default-initargs-class) option)
(case (car option)
default-initargs
‘(direct-default-initargs
(ist ,0(apply #'append
(napplist® #' (lambda (key value)
“C key ,value))
(cdr option))))))
(t (cal1-next-method))))
Note that bootstrapping concerns mean that the Cosette source code in Appendix D
of defclass. One simple way to add it
Exercise 3.4 (p. 104) The solution simply assumes the existence of an :allecation
option, which defaults to :instance. Given this, the previous specialized method on
compute-effective-slot-def inition is no longer needed since the standard inheri-
tance allocation is what is desired.
‘The key difference of the change is that some slots of the class will have :€ynamic allo-
cation while others have :instance allocation. This means that the previous specialized
method definitions need to be revised to check the allocation of slots rather than simply
assuming they are all dynamic. allocate-instance is modified to only allocate a table
entry when one or more slots are dynamically allocated.
instance ((class dynamic-slot-class))
‘cal-next-method)))
‘some #'dynamic-slot-p (class-slots class))
e-table-entry instance))
The specialized methods on the slot access generic functions are revised so that they
do not interfere with access to non-dynamic slots. Notice that by invol
method not only when the slot is not dynamic, but also when there is
1g call-next-
The non-standard mapping eappliet (281) mops over property ist.Solutions to Selected Exercises ES
given name, we allow the standard method to handle the missing slot error. (Only
slot-value-ueing-class is shown, the others are similar.)
Jf otaethod slot-value-using-class ((class dynanic-siot-class)
snatance slot-nase)
(let (alot (find slot-nane (class-slots class)
key #'slot-definstion-nane)))
Gt (and stot (Gynanic-siot-p slot)?
(read-dynanic-slot-value instance slot-nane)
(eall-next-nethod))))
Exercise 3.5 (p. 105) — This solution is similar to
slots except that instead of using a separate table for the
the class metaobject. Furthermore, as defined in CLOS,
slots are evaluated at the time the class is defi
Jlementation of dynamic
lots, they are stored in
ization forms for class
f (otclass class-slot-class (standard-class)
((class-allocated-slots
Hnitfora ()
taccessor class-allocated-slots)))
(etun class-slot-p (slot)
(eq (slot~definition-allocation slot) ':class))
Evaluation of the i
by a specialized method
that a class slot is unbound.
tion forms and allocation of the class slot storage is handled
imitialize-instance. A special value is used to record
_f etpcasterwbomn-chseot (GintMasbou cate so)264 Appendix B
(class class-slot-class) &key)
(sett (class-allocated-slote class)
(aapear
#' (lambda (slot)
Get (Cinitfunction
(slot-definition-initfunction slot)))
(cous (elot-definition-nane slot)
Gf (aot (aul initfunetion))
meal initfunction)
unbound-class-s1ot))))
Cenoverif-not #'class-slot-p
(claso-direct-elote class)))))
Specialized methods on the slot access generic functions first check whether the slot is a
class slot, and if itis they access the class slot storage. (Only slot-value-using-class
is shown, the others are similar.)
_f (éoteothod slot-value-using-class ((class class-slot-class)
instance
slot-nane)
Get ((stot (find slot-nane (class-slots class)
yy #'slot~definition-nane)))
Gt (and slot (class-slot-p slot))
(class-slot-value class slot-nane)
(call-next-nethod))))
‘The class slot storage is found by searching, in order, the direct slots of each class in the
class precedence listSolutions to Selected Exercises bea
_f (dotun class-slot-value (class slot-nane)
Golist (super (class-precedence-list class))
(et ((slot (find slot-nane
(Class-direct-slots super)
key #'slot-definition-name)))
(when slot
Get ((value (cdr (assoc slot-nane
(Class-allocated-elots super)))))
(when (eq value unbound-class-slot)
(error "The class slot “S is unbound in the class “S."
slot-nane class))
(return-fron class-slot-value value))))))
In full CLOS, change-class and update-instance-for-different-class handle
class slots in a special way which this solution does not support. In particular, they
provide reasonable behavior for what happens when the allocation of a slot changes.
How would you modify the existing protocol so that these operations could be extended
to handle class slots?
110) A flag stored with the generic function metaobject keeps track
iis currently on or off. A method on apply-generic-function checks
the flag, and when it is set, prints out appropriate informs
f deteiass traceable-gt (standard-goneric-function)
(Ceracing :initform nil :accessor tracing-enabled-p)))
(defun trace-generic-function (gf-name nev-value)
Get ((gf (fdefinition gf-name)))
(sett (tracing-enabled-p gf) new-value)))266 Appendix B
_f tetartand epieryemiceteceion (gt teaser) arg)
(Gif (not (tracing-enabled-p gf))
(call-next-nethod)
(progn
(format +trace-output+
“Entering generic function “S"@
with arguments “:8.-X" gf args)
Get ((results (multiple-value-List (call-next-nethod) )))
Gormat #trace-outpute
"Leaving generic function “S"@
value(s) being returned are: “:8.~%" gf results)
(values-List results)))))
Note that this solution cannot be added directly to the version of Closette that appears
in Appendix D because the code there reftects the revised version of the generic function
ocol developed late in Chapter 4. (Also see the remarks at the beginning
of Appendix D concerning fdefinitio:
Exercise 4.2 (p. 124) The solution is to store the argument precedence order in
the generic function metaobject, and then specialize method-aore-specific-p so that
it consults the stored value when ordering methods.
f ietclass apongt (standard-generic-function)
((argunent-precedence-order
Hinitarg ‘argument-precedence-order
raccessor argunent-precedence-order)))
‘The argument precedence order is initialized from the :arguaent-precedence-order
option to defgeneric; the value is a permuted list of the required argument names. The
logical default value is the required portion of the generic function’s lambda list, which
means that any defaulting must be done after the lambda list has beet
_f (doteotnod snitilize-inatance :after ((gf apo-gt) bkey)
(unless (slot-boundp gf ‘argunent-precedence-order)
(ott (argunent-precedence-order gf)
(ge-required-arglist gf))))
‘The specialized method on nethod-more-specific~p operates as before; the difference
is that the order of the comparison now comes from the argument precedence order,Solutions to Selected Exercises
267
_f (otaotnod nethod-nore-specitic-p
C(gf apo-gf) method! method2 required-classes)
(flet (apo-permute (list)
(mapcar #'(lanbda (arg-nane)
(ath (position arg-nane
(gf-required-arglist gt)
list))
(argunent-precedence-order gf))))
ici spec? arg-class)
(unless (eq spect spec2)
(return-from method-more-specific-p
(sub-specializer-p spect spec? arg-class))))
(apo-permute (method-specializers methodi))
(apo-permute (method-specializers method2))
(apo-permute required-classes))
(nape #' (lambda
nil)
3 (p. 124) Beta invokes primary methods in the opposite order of CLOS;
t-specific-first order. This effect can be achieved simply by reversing the
sense of method-more-specific-p, which will cause compute-applicable-methods~
using-classes to sort the list of methods in least-specific-irst order.
Af (otclass veta-gt (standard-generic-tunction) 0)
Gdefclass beta-nethod (standard-nethod
(defmethod method-nore-specific-p ((gf beta-gt) methodi method? classes)
(if (equal (wethod-specializers method)
(gothod-specializers method2))
nil
(not (call-next-method))))
Alternatively, apply-methods can be spe
of the list:
dt pall methods of he oppose ed
Af interioaappy-nesinds gt betagt) ares antats
Ginn (stl avin
(error "No primary methods for the"@
generic function “S." gf))
(apply-method (car (last methods)) args (butlast methods)))268 Appendix B
Either way, inner is implemented just like call-next-method. A specialized method on
extra-function-bindings returns an entry for inner,
Af (otaetnod extra-function-bindings ((nethod beta-nethod)
args next-nethods)
Gist
(ist ‘inner
#'Gambda ()
(Gf (null next-nethods)
ail
(apply-methods (method-generic-function method)
args
next-nethod
2
e solutions can be added directly to the version of Closette
Appendix D because the code there reflects the revised version of the
ce in Chapter 4.ce Living with Circularity
work if simply typed
the names were changed to avoid clashes
preters, the program developed in the body
ing CLOS implementation (even if
h existing symbols). This program contains
several kinds of vicious circularities; e.g., objects that must exist before they can be
created, and recursions without base cases
consider what would be involved in executing the defelass form on
page 1.3, which is intended to create the class metaobject for the class standard-class.
This new class metaobject is itself supposed to be an instance of standard-class; in
effect, we are asking the system to evaluate something like
(make-instance ‘standard-class :name ‘standard-class ...)
chicken-and-egg problem: an object cannot be created
class metaobject needs to be an instance of itself. Clear!
further thought to see how to get an actual implems
As a second example, cons
rectangle such as the door
value-using-class, whi ls slot-location
slot within the instance. Then 81 cation calls clas:
color-rectangle to get the class's list of effective slot di
can locate the slot named width:
jon slot-value calls slot
ascertain the location of the
Lots on the class metaobject
metaobjects so that it
| cntsnetot-vaae (nstance slots
Ceoe"vlancmingoriny (canto instance) taatans sot-nae?
(defun slot-value-using-class ((class standard-clase) instance slot-nane)
. (slot-location class slot-nane) ...))
(defun slot-location (class slot-nane)
(class-slots class) ...)
In this case, the applicable method on class-slots is a reader method specialized to
standard-class, whose body consists of a single call to slot-value to access the class
taobject’s slot named effective-slots:
otto chneciote (clase stndré-elase)
But now we are back to slet-value, which wi
the original. ‘we have is a non-well-foundes
implementation to loop indefinitely.
evaluated in the same manner as
recursion path, which would cause the70 Appendix C
Although, for reasons of
cannot be run as given, some!
we program presented in the main text of Part I
very close to it can be run on a bare Common Lisp
yystem), and that something is presented in Appendix D. In
the origins of the problematic circularities, and explain how to
get around them.
‘The heart of the matter is that the introduction of metaobject protocols makes CLOS
into a procedurally reflective language [Smith 84] and the code present
isa reflective processor program for CLO’
‘cossor program. The examples
that arise in procedurally rfl
in the main text
sn a conventional metacircular pro-
en above illustrate two general categories of circularity
systems:
Bootstrapping issues which are involved with how to get the system up and running in
e first place, and
Metastability iss
8 which have to do with how the syst
indamental aspects of the implement
wanages to run, and to stay
jon are being changed.
The issue of how the class metaobject for standard-class comes into existence is a
bootstrapping issue; once this class metaobject exists, the problem evaporates. How
slot-value will manage to avoid the appar
does not go away even after the system has been bootstrapped. Another metas
issue has to do wit ute-discriminating-function, When a method is added
to this generic funct criminating function becomes invalid and so much be
recomputed, which requires calling compute~discriminating-function.
Developing a correc jon involves finding and breaking all
but the techniques differ for the two kinds of cases. Because they occur before any user
code has to be run, bootstrapping issues can often be dispatched by quick and dirty
means. Addressing the metastability issues, on the other hand, requires taking care to
ensure the (necessary) shortcuts do not invalidate the correctness of any user code. In
spite of these differences, however, approaches that address one kind of issue sometimes
rovide solutions to the other kind; fortunately, that is the case for Closette, as the rest of
is appendix will explain. A more thorough discussion of the problems associated with
implementing procedurally reflective languages can be found in (des Rivitres&Smith 84]
C.1 Bootstrapping Issues
Bootstrapping Closette involves two general
ization tasks: creating the initial class
along with their standard methods.
is that there are only a finite number
can all be figured out in advance (byus, the implementors). This means that these metaobjects can all be created by special
hand-coded mechanisms. The only real challenge, in fact, is to find a way to create these
initial metaobjects without having to write too much code that serves no other purpose.
The first circularity in the class hierarchy that must be dissolved is the fact that the
class standard-class must exist before any metaobjects can be created with allocate
Instance, which expects a class metaobject as an argument. The solution is to create
standard-class entirely by other means; it can then be used
class metacbjects.
he creation of the other
from the fact that classes and generic functions
of classes requires the prior existence of certain
ly, the creation of generic functions and methods requires the
classes. These circularities need to be broken.
this, note that the reasons metaobject protocols are based on
generic functions is that the method lookup mechanism provides both flexibility and
yy. Neither property, however, is needed during initialization, since the
metaobjects are always instances of the standard metaobject classes. Extensibility,
generic functions; conver
prior existence of cert
To see how we can
-an save us from the problem that the
t of the system is created, An
solution: carry out calls to generic functions without using the method
Jookup mechanism until all classes, generic functions, and meth
‘There are a variety of ways in which this strategy is achieved. We need to be able
to execute a defclass form for one of the initial classes while steering clear of the
‘method lookup mechanism (the same story applies to defgenerics and defmethods),
‘The documented execution of a defclass involves a call to ensure-class,
arrive at a si
g,, to allocate-instance and
make~instance. Notice, however, that
passing the class metaobject class as the first
we can predict exactly which methods of
\ctions will be invoked. ‘This allows us to modify ensure-clase to catch
jon make-instance-standard-class with exactly the
net behavior as that call to make~inetance. In other words, the defini
[B] Gotan ensore-ctass (...)
apply #'nake-instance metaclass ...) ...)272 Appendix C
can be changed to
Ef ceotun ensure-class (...)
+» (apply (if (eq metaclass (find-class ‘standard-class))
# nake-instance-standard-class
#'vake-instance)
notaclass ...) ...)
‘Mostly, what make~instance-standard-class docs is to initialize slots from initial-
ization arguments; this can be accomplished with inline calls to the metaobject slot acces-
sors. Other initialization is performed by a class-specific after-method on initialize-
instance y finalize-inheritance. We can avoid unnecessary code duplication
by moving most of the code from the standard methods into independently callable
functions. E.g., the standard method for finalize-imheritance:
FF ceetentnod nahie-iberieance (clase etandad-ease)
(body))
can be split into a
inheritance th
alls a normal function named std-finalize-
1 work:
[P] (aetmetnod tinalize-inheritance ((class standard-class))
(std-finalize-imheritance cl
(ofun std-finalize-inheritance
cases when it is known that only the standard method would be applicable.
the call
(finalize-inheritance class)
can be rewritten as
(funcall (if (eq (class-of class) (find-class '‘standard-class))
#'std-finalize-inheritance
#*finalize-inheritance)
class)
because it is a su instances of etandard-class will
way
finalized in the regularLiving with 273
standard-class, the full method
Since 1s during startup are instances
lookup mechanism is bypassed during startup.
subclass of standard-class comes along later, the real method lookup mechani
finalize-inheritance wil
The bulk of the generic function
slots). There is no particular reason in Closette why these accessors need to be generic
functions at all, so we have rewritten them as regular funetions. This leaves only about
generic functions calls that need to be special-cased in the way described above.
aobject slot accessors (e.g., class
a doz
C.2 Metastability Issues
implementation is up and running, it is still a trick to keep it running. In a
normal, closed implementation, this is not a problem; in a system with metaobject proto-
cols that allow the implementation to be extended, there is the potential for spectacular
tions are not properly an
f the examples we looked at above, one step in accessing a slot in a standard
relevant effective slot defi metaobject. The list of
ugh is stored
it access to retrieve. Thus
ested chain of,
(slot-value door "width)
(slot-value-using-class (class-of door) door ‘width)
(elot-location (class-of door) 'width)
(class-slots (class-of door)
(slot-value (class-of door) 'effective-slots)
(slot-value-using-class (class-of (class-of door))
(elass-of door)
‘ef fective-slots)
(slot-location (class-of (class-of door))
‘effective-slots)
(class-slote (class-of (class-of door)))
(slot-value (class-of (class-of door) ‘effectiv
s)
the slot named
is, by ensuring,
slot-location (282) by special-ci
class metaobject for standard-class.
This loop can be broken
effective-slots of t
q
(slot-location (find-class ‘standard-class) ‘effective-slots)24 Appendix
returns the known and fixed location of this particular slot of this particular class meta
object without recourse to further slot accesses. Cat
the circularity because standard-class is always guaranteed to turn up eventually in
the sequence
(class-of (2)
(class-of (class-of (z)))
(class-of (class-of (class-of (z))))
fac lass standard-class sits at the top of all class-of chains is a crucial
property of the MOP—without it, there would not be any obvio
nique applies to most other issues of metastability as well
system-defined generic functions with standard metaobjects as arguments for example,
computing the class precedence list of an instance of standard-class, computing the
the method
somehow guarantee that all these base cases can be handled no matter what sper
izations the user may later introduce. This is one of the reasons why users ¢:
allowed to add new methods that would be applicable to standard
such a restrictio
To see how this
‘matters, consider what is su
method is added to compute-discriminating-f
example, with the following call
(add-nethod #' compute-discriminating-function (new-metho
be called to compute thi
jlementation needing to make t!
(compute-discriminating-function #' compute-discriminating-function)
Ifthe implementatic
to come up with the
tried to call compute-discriminating-function in order
ig function for compute-discriminating-function,Living with Circularity 5
the game would of course be over. O1 \ce compute-discriminating-
1 of standard-goneric-function, this is a base case, which can
be handled by the function std-compute-discriminating-function. As long as std-
conpute-discriminating-function is called in assuming that
it is guaranteed to return a value, this metastability issue has been resolved. Thus we
see how the calls, already recast in the form
(funcali (it (eq (class-of gf) (find-class ‘standard-generic-tunction))
#'std-compute-discriminating-function
#'compute-discriminating-function)
ef)
for bootstrapping reasons, also solve Closette’s issues of metastabilityD A Working Closette Implementation
lix contains the complete source code for Closette, as presented in Part I. Tt
port for CLOS.' Except for some differences dealing with
the code b letely in line with the older versi
Besides the features of basic CLOS, this implementation i
metaobject protocols developed in Chapters 2 and 3 of Part I. The streamlined meta-
object protocols for generic funetion invocation that were presented in Section 4.4.4
are included in liew of the apply-... versions from the early part of Chapter 4 (the
latter are retained as normal fun terms of the fast ones).
ing functions are precomputed and stored with the generic functior
object. Effective method functions are computed on the cri
les themselves are stor
ing stored in lexical variables vi
Method functions are precompu
ta
al path and memoized as
with
metaobject (as opposed to
criminating functio
metaobject. Regular
extra-method-bindings protocol.
This implementation also handles around-methods, method redefinition, and
describe-object.
The only other noticeable difference from the code for
isp only allows true fi
yur generic function metaobjects are not true functions
they are a kind of structs p refuses to store them there, We
work around this by storing the disc unction value,
and putting the generic function metaobject into an auxiliary table where it can be
retrieved by name with a function called find-generic-function (exactly analogous to
find-class). This solution does not support anonymous generic functions, and trouble
can arise if a named generic function is passed as a functional argument. You must write
(fdefinition ‘paint) or #’paint when you need something that can be funcalled
or apply'd, but you must use (find-generic-function ’paint) when you really need
‘The remaining differences betwoen this code and that presented in Part I happen truly
backstage, and have to do with the issues of circularity discussed in Appendix C.
ro Common
Lucid/Sun Common Lisp 3.0.1 (Sunt
fommon Lisp
‘nd Shmbolice Genera Version 80.278 Appendix D
There are two files: closette.1isp, which contains all of the code for Closette; and
novel. lisp (page 313), which contains some definitions that allow the first file to run
in older versions of Common Lisp.
}ivt-Mede:LISP; Package: (CLOSETTE :USE LISP); Base:10; Syntax:Common-Lisp -*-
Closette Version 1.0 (February 10, 1991)
Copyright (c) 1990, 1991 Xerox Corporation.
AN] rights reserved.
Use and copying of this software and preparation of derivative vorks
based upon this softvare are permitted. Any distribution of this
software or derivative works must comply vith all applicable United
States export control lays.
This software is made availabl
warranty about the softwar
specification.
AS 1S, and Xerox Corporation makes no
its performance or its conformity to any
CLosette is an implementation of a subset of CLOS with a metacbject
protocol as described in "The Art of The Metacbject Protocol",
MIT Press, 1991,
‘This program is available by anonymous FIP, from the /pcl/sop
directory on arisia.xerox.com.
$1 This is the file closette.lisp
(n-package ‘closette :use '(1isp))
Waen running in a Common Lisp that doesn't yet support function names 1ike
(setf foo), you should first load the file nevcl.lisp. This next little
bit imports stuff from there as needed
f-conera
(port ' (newcl :print-unreadable-ob ject))
fcenera,
adoving-import '(nevcl:defun newcl:fboundp nevel:feakunbound
novel :fdefinition))
tGenera
(export '(newel:defun nevel:fboundp nevel:faakunbound nevel:fdefinition))A Working Closette Implementation 279
#sGenera
‘shadoving-import ‘(future-conmon-Lisp:setf
future-comnon-1
future-comnon-)
{uture-conmon-1igp:print-unreadable-object))
tconera,
Cexport '(future-common-lisp:setf
future-comnon-Lisp:fboundp
{uture-comnon-Lisp:fuakunbound
future-comnon-Lisp:fdefinitio
‘future-connon-1isp:print-unreadable-object))
(aetvar exports
"(dofclass dofgeneric dofnethod
find-clase clase-of
call-next-nethod next-nethod-p
slot-value slot-boundp slot-exists-p slot~makunbound
nake-inetance change-class
snitialize-inetance reinitialize-inetance shared-initialize
update-instance-for-different-class
print-object
svandard-generic-function standard-method
class-direct-superclasses class-direct-slots
clase-precedence-List clase-slote class-direct-subclasses
clase-direct-nethods
yric-function-nane generic-function-lanbda-List
e-function-nethods generic~function-discriminating-function
ie-function
nethod-lanbda-list method-qval jecializers method-body
nothod-environnent nethod-generic-function method-function
slot-definition-nane elot-definition-initfunction
slot-definition-initforn slot-definition-initargs
slot-definition-readers slot~definition-w
slot-definition-allocation
Class-related metacbject_ protocol
compute-class-precedence-list compute-slots
coupute-effactive-slot-definition280 Appendix D
fsnalize-inheritance allocate-instance
slot-value-using-class slot-boundp-using~
slot-exists-p-using-class slot-salumbound-using-class
$1 Generic function related metaobject protocol
compute-discriminating-function
compute-applicable-nethods-using-classes method-nore-specific-p
conpute-effective-nethod-function conpute-method-function
apply-uethods apply-aethod
find-generic-function ; Necessary artifact of this implementation
»
(export exports)
veilities
js push-on-end ie Like push except it uses the other end:
(defmacro push-on-end (value location)
“(gett ,location (nconc ,iocation (List value!
5 Gott gout
is Like (eetf getf) except that it always changes the list,
which must be non-nil
(defun (gett getf+) (new-value plist key)
(block boay
(do ((x plist (eddr 3)?
(aul 2)
(nen (eq (car
seturn-from body new-value
push-on-end key pl:
jpash-on-end nev-value pli
new-value))
sppend is Like sapcar except that the results are appended together:
fun napappend (fun brest args)
Gf (one #*aul args)
(append (apply f
Copply #1
sapear #'car
sppend fun‘A Working Closette Implementation aes
} mapplist is eapear for property lists:
(defun napplist (fun 2)
Gt Gull 2
(cons (funcall fun (car x) (cadr ¥))
Gaapplist fun (cad
j Standard instances
11 This implementation uses structures for instances, because they're the only
; Kind of Ligp object that can be easily made to print vhatever vay ve vant
constructor allocate-std-instance (class slots))
predicate std-instance-p)
(:print-function print-std-instance))
(detstruct (std-instance
class
slots)
(Gefun print-std-insvance instance strean depth)
(declare (ignore depth))
(print-object instance strean))
Standard instance allocation
(defparaneter secret-unbound-value (List "slot unbouné"))
(dofun instance-stot-p (slot)
(eq (Slot-definition-allocation slot) ‘:instance))
(defun std-allocate-instance (class)
(allocate-std-instance
clase
(allocate-slot-storage (count-if #*instanc
secret-unbound-val
ot-p (class-slots cla
114 Simple vectors are used for slot storage
(defun allocate-slot-storage (size initial-value)
(wake-array size :initial-elenent initial-value)
Standard instance slot access
s1i N.B. The location of the effective-slots slots in the class aetacbject for
ii} standard-class must be determined without making any further slot282 Appendix D
$y references
(detvar the-slots-of-standard-class) ;standard-class's clase-slote
(astvar the-class-standard-class) _;standard-class's clase netaobject
(defun siot-location (class slot-nane)
foctive-slots)
.98-standard-clas
(position ‘effective-slots the-slots-of-standard-clas
key #'slot-definition-name)
Get (slot (Find stot-
Gt (qui sto)
(error "The slot “S is missing from the class “5.
slot-nane class)
Get ((pes (position slot
(ronove-if-not #" instance-slot-p
(class-slots class)))))
Gt (aunt pos)
(error "The slot “S is not an instance"@
slot in the class “S."
jot-name class)
Pe
fun slot-contents (slots location)
‘evret slots location))
(Gefun (setf slot-contents) (nev-value slots location)
(set! (avref slots location) nev-value))
-slot-value (instance slot~nane)
Location (elot-location (class-of instance) slot-nane))
slots (std-inetance-slots instance))
val (slot-contents slots location!
sq Secret-unbound-value val)
ror "The slot “S i# unbound in the object “S."
slot-nane instance)
vai)))
(detun slot-value (object slot-nane)
Gf (eq (class-of (class-of object)) the-class-standard-class)
(std-slot-value object slot-nano)
(slot-value-using-class (class-of object) object slot-naze)))|A Working Closette Implementation es
‘std-elot-value object slot-name) nev-value)
jatf-elot-value-using-class
nev-value (class-of object) object slot-name)))
(defun etd-slot-boundp Cinstanc
(class-of object) object slot-nane)
(@efun std-slot-sakunbound (instance slot-nane)
(et (Cecation (slot-lecation (class~of instance) slot-nane))
‘atd-instance-slots instance)))
ot-contents slots location) secret-unbound-value))
instance)
(@efun elot-naxunbound
at
spject slot-name)
(class-of (class-of object)) the-class-standard-cl
{td-slot-nakunbound object slot-nane)
(elot~nakunbound-using-class (class-of object) ob;
>
“t slot-nane)))
(defun std-slot-exists-p (instance slot-nane)
(aot (aull (find slot-nane (class-slots (class-of instance))
key #'#lot-definition-name))))
(defun slot-exists-p (object slot-name)
Gf (eq (clase-of (clase-of object) the-class-standard-class)
(etd-slot-existe-p object slot-nane)
(elot-existe-p-using-clags (class-of object) object slot-name)))
+} class-of
(@ofun class-of (x)
Gf (etd-instance-p
(etd-instance-class x)
(buitt-in-class-of «)))
N.B. Tais version of built~in-class-of is straightforvard but very slov.
fun built-in-clase-of (x)
(typecese x284 Appendix D.
(find-elass ‘ul1))
nd-clase '
(find-clase
(sind-clase
(find-clase +
(find-clasa ‘character))
(find-clase thash-table))
(not (or string vector)))
jot vector))
(or vector list!
(function
&
isi subelassp and sub-specializer-p
(@etun subclassp (ci €2)
(not (null (find c2 (class-precedence-list ¢1
(defun sub-specializer-p (ct c2 cvarg)
Get (cpl (class-precedence-liet ¢-arg)
(not (aul (find c2 (cdr (aenber ct cpl
or
staobjects and standard-class
lefparaneter the-defclas
“(@efclass standard-class
CQmane :initarg :nane) } saccessor class-nane
(irect-superclesses } taccessor class-direct-superclasses
imitarg :direct-superclasses)
(airect-slots)
(class-procedence-List)
(eftective-siots)
(direct~subclasses :initf
(direct-nethode :initfora
standard-class ;standard-class's defclass form
5 secessor clas-direct-slote
accessor class-precedence-list
} taccessor class-slete
accessor class-direct-subclasses
sor class-direct-aethods
Defining the astaobject slot accessor function as regular functionsA Working Close lementation 285
isi greatly simplifies the implementation without removing functionality.
(std-slot-value clase ‘nane))
(sov-value class)
(defun (sett class-direct-supercla:
(seri (slot-velue class 'direct-superclasses) nev-valu
se-value class)
(sect (slot-value class ‘direct-slots) nev-value))
(detun class-precedence-List (class)
(stun class-siots (c
(lot-value class ‘effective-siets))
(aefun (set! class-slots) (nez-value class)
fective-siots) nev-value))
(detun class~direct-subclasses (clas
(elot-value cl:
(dofun (setf clas t-subclasses!
(sett (Glot-value class 'direct-subclas
direct-eubclass
jue class
now-value))
Gofun class-direct-nethods (class)
(elot-value class 'direct-aethods))
(@ofun (sett class-direct-nethods) (nev-value cli
(setf (slot-value class 'direct-methods) neu-value))
is dofclase
(aetaacro defclass (nt
&
direct-superclasses direct-slote
options)
jsure-clase
‘ect -superclasses
(canonicalize-direct-superclassee direct-superclasses)
ect-slots
(canonicalize-direct-elote direct-elots)
,O(canonicalize-defclass-options options)))286 Appendix D
fun canonicalize-direct-slots (direct-slots)
List ,a(mapcar #*canonicalize-direct-slot direct-siots)))
(defun canonicalize-direct-slot (spec)
Gf (eymbolp spec)
“Qast :name ' spec)
et
initform
etq initfunction
“function (lambda ()
(eotq initforn ‘*, (cadr oli
Cinivarg
(push-on-ond (cadr olist) initargs))
reader
(car olist) other-option
‘adr olist) other-optioi
+(vhen initfunction
‘Ginitform ,initfors
Amitfunction ,initfunction))
s@(vhon initar
sO(vhen write
sGother-option
(dofun canonicalize-direct superclass:
“(ist ,@(eapear #' canonical ize-di‘A Working Closette Implementation 287
(Gefun canonicalize-direct~superclass (class-naze)
(find-class
(defun canonicalize-defclass-options (options)
(aapappond #'canonicalize~defclass-option options))
jofclass-option (option)
" direct-default-snitarge
"(Clist ,@Caapappend
#°Canbda (x) 2
(napplist
#' Cama
(e Gist ‘(car option) *
isi find-class
(let ((class-table (make-hash-table :test #'eq)))
(defun (sett find-class) (nev-value syzbo)
(eetf (gethash eymbol class-table) nev-value))
(dofun forget-all-classes ()
(clrhash clasa-table)
jend let class-table
sis Basure class
(defun ensure-class (nane Brest all-keys
key (netaclass the-claes-standard-class)
Rallow-other-keys)
(Gf (find-elase name ail)288 Appendix D
(error "Can't redefine the class naned “S." naze)
(et ((class (apply (if (eq netaclass the-clase-standard-class)
#'make-instance-standard-claes
#'make-instance)
netaclass :nane nane all-keys)))
‘ind-class nane) clase)
make-instance-standard-class creates and initializes an instance of
standard-class vithout falling into method lookup. However, it cannot be
called until standard-clasa itself exists.
(defun nake-instance-standard-clase
aclass key nane direct-superclasses direct-slote
ballov-other-ke
(declare (ignore netaclas:
Get ((class (std-allocate-instance the-class-standard-class)))
(sett (class-nane class) name
(etd-atter~initialization-for-clasees class
direct-slote direct-slots
direct~superclasses direct-superclasses)
class)
(Getun std-after-initialization-for-classes
key direct-superclasses direct-slots kallov-other-keys)
(or direct-superclasses
(ist Gind-class ‘etandard-object)
-direct-superclasses class) 2%
(dolist (superclass supers)
(class-direct-subclasses superclass))))
(eapcar #'Clanbda (elot-properties)
(apply #*zake-direct-slot-definition
(dolist (reader (slot-definition-readere direct-slot))
(ada-reader-method
class reader (slot-definition-naze direct-slot
(dolist (writer (slot-definition-wraters direct-slot)
(add-vriter-nethod‘A Working Closette Implementation 289
Slot definition metacbjects
N.B, Quietly retain all unknovn slot options (rather than signaling an
error), 0 that it's easy to add mew ones.
(defun make-direct-slot-def inition
(rest properties
fekey nane (initarge ()) Cinitform nil) (initfunction nil)
(readers Q)) (writers ()) (allocation :instance)
ballov-other-keys)
(et ((elot Ccopy-List properties))) ; Don't want to side effect Brest list
(sett (gett slot ':nane) naze)
(sotf (getf+ slot ':initargs) initargs)
slot ‘:initform) initform)
simitfunction)
slot ':
slot
slot ':allecation) allocation)
slot
(defun make-etfective-slot-definition
(arest properties
axey nae (initarges ©) (initform nil) Cinitfunction nil)
(allocation :instance)
ballov-othor-keys)
jot (copy-List properti
getf* slot ':nane) nane)
initargs)
initform)
‘ranitfunction) initfunction)
‘allocation) allocation)
; Don't vant to side effect #rest List
(detun slot-definition-name (slet)
(gett slot ':nane))
(defun (eet slot-definition-nane) (nev-value slot)
(sort (gett slot ‘:nane) nev-value))
(defun slot-desinition-initfunction (#let)ct (gett slot "sinitfunction) nev-value))
(detun sict-definition-initform (slot)
ett slot ':initform))
fun (Getf slot-definition-initforn) (nev-value slot)
(eett (gett slot ':initforn) new-value))
(defun slot-definition-initargs (slot)
(gett slot *:initargs))
(defun (setf slot-definition-initargs) (nev-value slot)
‘sinitarge) nev-value))
(eett (gett elot ':writers) nev-value))
(detun slot-definition-allocation (slot)
jetf slot ':allocation))
fun (setf slot-def inition-allocation)
(getf* slot ':allocation) nev-val
$1 finalize-inheritance
(funcali (if (oq (clase-of class) the-class-standard-class)
#'etd-coupute-slote
#'compute-slots)
clase))
(vatues))
iss Clase precedence lists
Appendix Dlogical-sort clas
(eenove-duplicates
(napappend #° local-precé
a
#'ged-tie-bresker-rule)))
} topological-sort implements the standard algorithe for topologically
} X must precede elexent ¥.
is necessary to choose from
; candidates and the ordering
(efun topological-sort (elenents constraints tie-breaker)
(let ((remaining-constraints constraints)
(ronaining-clenents clezents)
Gesutt 0))
rezaining-elenents)
(when (gull sininal-ele
Gf (qull resaining-elezents)
(ceturn-from topological-sort result)
(error "Inconsistent precedence graph.")))
Get (Cehoice (if (null (car ainimal-olezents))
(car minimal-elenents)
(funcall tie-breaker
zinizal-elenents
eq result (append result (1ist choice
sningrelenente
jove choice reuaining-elexents))
ming-constraints
(renove choice
renaining-const
; In the event of a tie while topologically sorting class precedence lists,
} the CLOS Specification says to “select the one that ha a direct subclass
291292
rightaost in the class precedence list computed so far." The same result
is obtained by inspecting the partially constructed class precedence list
from right to left, looking for the first minimal element to show up among
‘the direct superclasses of the class pri 1e List constituent
(There's a lesma that shovs that this
yields a unique result.)
fun std-tie-breaker-rule (nininal-elenents cpl-so-far)
(dolist (cpl-constituent (reverse cpl-so-far))
(ots ((eupera (class-direct-superclasses cpl-const:
(common (intersection minizal-elenents super:
(when (not (null common))
(return-from std-tie-breaker-rule (car coanoi
j) This version of collect-superclasses+ ion't bothered by cyclos in the class
hierarchy, which sometimes happen by accident
(defun collect~superclassess (class)
(labels ((all-superclasses-loop (seen superclasses)
(et ((to-be-processed
(set-difference superclasses seen)))
(3£ (aul to-be-processed)
superclasses
Get ((class-to-procesi
(car to-be-processed)))
clase-to-proce:
superclasse:
(all-superclasses-loop () (List clas:
ji The local precedence ordering of a class C vith direct superclasses C1,
HH C.2. vs, Cm de the eet ((C CA) (C1 6.2) ...(Cm-t Cn)
(defun local-precedonce-ordering (class)
(wapear #'List
(cons class
(butlast (class-direct-superclasses cl
(class-direct-sup class)
ii Slot inheritance
-d-compute-slots (clas:
(all-slots (eapappend #'class-dire
yrecedence-list clase)))
(eli-nanes (ronove-duplicatesA Working Closette Implementation
(eapcar #'slot-definition-naze all-slot
(wapcar #'(lanbda (naze)
(funeant
293
Gf (eq (class-of class) the-class-standard-class)
class
-compute-effective-slot-def inition
#*compute-effectiv
yt-def inition)
(rezove nane all-slots
e-def ini tion-nane
‘test-not #¥eq)))
(aefun std-conpute-effective-slot-definition (class direct-slots)
(declare (ignore class))
(Get (Giniter (find-it-not #'mull direct-slots
key #'slot~def inition-initfunction)))
(nake-oftective-slot-def inition
imitfore (if initer
jot-definition-name (car direct-slots))
(elot-detinseion-initforn initer)
ail)
initfunction (if initer
ail)
initargs (ronove-duplicates
(slot-definition-initfunction initer)
(wapappend #'slot-definition-initargs
direct-slot
allocation (slot-definition-allecation (car direct-slots!
Generic function metacbjects and standard-generic-function
(dotparancter the-defclass-standard-generic-function
Hinitarg :method-class)
(Giscripinating-function)
(elasses-to-eat-table
initfors (eake-hash-table
accessor generic-function-name
accessor generic-function-lambda-list
accessor generic-function-methods
accessor generic~function-nethod-class
accessor generic-function~
~discriminating-function
‘eo-enf-table294 Appendix D
Gotvar the-class-standard-gf) jstandard-generic-function's clase metaobject
(@ofun generic-function-name (gf)
(slot-value gf ‘name))
Gofun (sett generic-function-naze) (nev-value gf)
(setf (siot-value gf ‘nane) nev-value))
fun generic~function-Lanbda-list (gf)
jot-value gf 'Lanbda-list))
wtf generic-function-Lanbda-List) (nev-value gf)
wtf (slot-value gf ‘lambda-list) nev-value))
(defun generic-function-netheds (gf)
(elot-value gf ‘nethods))
jofun (sett generic-function-uethods) (neu-value gf)
tf (slot-value gf ‘methods) new-value))
jun goneric-function-discriminating-function (gf)
slue gf 'discriminating-function))
generic-function-discriminating-function) (new-value gf)
lot-value gf ‘discriminating-function) nev-value))
jefun generic-function-method-class (gf)
(stot-value gf ‘nothod-class))
(defun (set generic-function-aethod-clags) (new-value gf)
(setf (slot-value gf ‘zethod-class) nev-value))
iii Internal accessor for effective method function table
s-tovenf-table (gf)
} taecessor method-lanbda-List
r method-qualifiers
r method-specializers
i yr method-body
(environment :initarg environment) :accessor method-environzent
(generic-function :initfors nil) } ‘accessor sethod-generic-functionA Working Closette Implementation
(funetion)))) } taccessor method-function
(dofvar the-class-standard-nethod) _standard-nethod’s class metaobject
(dotun method-Lanbda-List
Gofun (sett nethod-lambda-ist) (nev-value
(sett (elot-value method ‘lanbda-List) new-value))
tun nethod-qualifiers (method) (elot-value method ‘qualifiers))
4 method-qu: 2) (new-velue method)
jot-value method ‘qualifiers) nev-value))
(defun nethod-specializers (aethor
(defun (set method-specializers) (nev-valu
jot-value method ‘specializers) nev-value)
jod-body (method) (slot-value method ‘body))
od)
“ic-function) (nev-value method)
sd. "generic-function) new-value))
(dofun method-function (nethod)
(eet! nethod-function) (new-value meth
setf (elot-value nethod ‘function) nev-value
lot-value method 'function))
jofgeneric
Jtgeneric (function-nane lambda-list rest options)
jure-generic-function
fonction-nane
jambda-list
(canonical:
Janbda-List
-defgeneric-options options)))
(Gefun canonical ize-defgeneric-options (options)
(aapappend # canonicalize-defgeneric-option options))
Jfgenersc-option (option)
jenoric-function-class296 Appendix D
Gist ‘:generic-function-class
"(cade option)
(cade option))))
( Gist ",(car option) ‘*, (cadr option)))))
find-goneric-function looks up a generic function by mane. It's an
artifact of the fact that our generic function metaobjects can't legally
be stored a aymbol's function value:
st ((generic-function-table (nake-hash-table :test #*equal)))
(Gofun find-generic-function (syabol koptional (errorp
Get ((gt (gethash symbol generic-function-table nil)))
(if (and (aul gt) errorp)
‘or "No generic function named “S." syabol)
a)
f find-generic-function) (nev-val
thash syubol generic-function-tat
(detun forget-all-generic-functions ()
(elhash generic-function-table)
(values)?
send let generic-function-table
>
eneure-generic-function
jofun ensure-generic-function
(fanction-nane
terest all-keys
key (goneric-function-class the-clase-etandard-gf)
(eethod-class the-class-standard-nethod)
ballow-other-keys)
(Gf (finé-generic-function function-name nil)
(Gind-generic-function function-nane)
Get (gt Gpply Gf (eq generic-function-class the-class-standard-gf)
#*nake-instance-standard-generic-function
#*make~instance)
generic-function-class
mmane function-nane
‘nethod-class method-c
all-keys
(sett (tind-generic-f
jon function-nane) gf)‘A Working Closette Implem 207
finalize-generic-tunction
N.B. Same basic idea as finalize-inheritance. Takes care of recomputing
31} and storing the discriminating function, and clearing the effective method
iis function table,
Hinalize-generic-function (gf)
(otf (generic~function-discriminating-funct:
(foncall Gt
(generic-function-nane gt))
(generic-function-discriminating-function gf))
(cimnash (classes-to-enf-table gf))
(values))
make-inatance-atandard-generic-function creates and initializes an
instance of standard-generic-function without falling into method lookup.
However, it cannot be called until standard-generic-function exists.
fun make-instance-standard-generic-function
(genoric-function-class Akey nane lanbda-list method-class)
(let (ge (etd-allocate-instance the-class-etandard-g£)))
(setf (generic-function-naze gf) nané
(sett (generic-function-lanbda-list gf) laubda-List)
(sett (generic-function-nethods gf) 0)
(setf (generic-function-nethod-class gf) method-class)
-to-oaf-table gf) (eake-hash-table :test #'equal))
sric-function gf)
dofzethod
sfnacro defnethod (Brest args)
(eultiple-value-bind (function-naze qualifiers
lambda-List specializers body)
(parse-defmethod args)
“Censure-nethod (find-generic-function ',function-naze)
Lanbda-List 'lambda-List
qualifiers ifiers
specializers , (canonicalize-specializers specializers)
body "body
‘environment (top-Level-environnent))))298 Appendix D
(@ofun canonicalize-specializers (specializers)
"Cist ,@(zapcar #'canonicalize-specializer specializer
(@efun canonicalize-specializer (epecializer)
“(find-class ',specializer))
(aolist (arg
(ecase parse-stat
(qualifiers
Ge (and (atom arg) (not (null arg)))
body)?
(body (push-on-end arg body))))
(values tn-spec
alifiers
extract-lanbda-list specialized-lasbda-list)
isi Several tedious functions for analyzing lambda liste
squired-portion (gf arge)
(nunber-required (length (gf-required-arglist g£))))
jen (< length args) nuzber-required)
rror "Too fey argusents to generic function ~S." gf)
10q args 0 number-required
(@ofun gf-required-arglist (gt)
Get ((phise
(analyze-lanbda-List.
(generic-function-Lanbda-list gf))))
(getf plist‘ :required-arge)))
(Gofun extract-Llambda-List (specialized-Lanbda-List)
(et« ((plist (analyze-Lanbda-List specialized-lanbda-List))‘A Working Closette Implementation 200
ireds (gotf plist ' :required-nanes))
(detun analyze-Lambda-List (lanbda-List)
Gabels ((zake-keyyord
Gntern (symbol-nane symbol)
(fina-package "keyvord)))
(get-keyvord-froa-arg (arg)
Gf (istp arg)
Ge Gietp (car azg))
(eake-keyvord (car arg)))
(nake-keyuord arg))))
i Just the keywords
argument specs
jariable names
1 spocializers
specializers
(allow-other-keys nil)
(state :parsing-required))
(dolist (arg lanbda-List)
Gr HE arg, lanbda-List-keyvords)
(ecase arg
(optional
(setq state :parsing-optional))
sparsing-rest))300 Appendix D
stq state :parsing-key))
lou-other-keys,
tq allow-other-keys. *t
(parsing-required
(push-on-end arg required-args)
GE (istp arg)
(progn (push-on-end (car arg) required-nanes)
(parsing-optios
(Cparsing-rest
required-args required-args
specializers specializers
reat-var rest-var
skeyvords keys
key-args key-args
auxiliary-args auxs
soptional-args optionals
allow-other-keys. allov-other-key
(efun ensure-rethod (gf Brest all-keys)
(let ((new-nethod
(apply,
GE (oq (goneric-function-nethod-class gf)
‘the-class-standard-method)
# nake-instance-etandard-nethod
#*make-instance)
(generic-function-nethod-class gf)
ali-keys)))
(ada-nethod gf new-aethod)
nev-nethod))
315 makevinetance-standard-nethod creates and initializes an instance of
$5) standard-method without falling into method lookup. However, it cannotA Working Closette Implementation
be called until etandard-nethod exists.
fun make-instance-standard-aethed (aethod-class
‘key lambda-liet qualifiers
specializers body environment)
Geclare (ignore method-clas:
(let (method (etd-alleeate-instance the-class-standard-nethod) ))
£ (gethod-lanbda-list method) lambda-list)
sthod-qualifiers xethod) qualifiers)
thod-specializers method) specializers)
sthod-body method) body)
schod-environnent method) environsent)
sthod-generic-function method) n11)
sthod-function method)
:d-coupute-nethod-function method))
5 add-method
| N.B, This version firet renoves any existing method on the generic function
| with the eame qualifiers and specializers. It's a pain to develop
; prograns without this feature of full CLOS.
sehod-qualifiers method)
sd-specializers method) ni1)))
(when old-method (remove-method gf old-nethod)))
(eet (aethod-generic-function method) gf)
(push method (generic-function-nethods gf))
(olist (specializer (nethod-specializers nethod))
(pushnew method (clase-direct-nethods specializer)))
(tinal ize-generic-function gf)
nethod)
jeric~function-nethods gf)))
yetion eethod) mil)
od-specializers method))
(sett (class-direct-nethods class)
(remove method (class~direct-methods class))))
(finalize-gonoric-function gf)
method)
(@efun find-method (gf qualifier302 Appendix D
optional (errorp t))
Get (method
(find-it #*
fanbda (method)
(and (equal qualifiers
(aethod-qualitiers method))
(equal specializers
yhod-specializere method))))
(generic-function-nethods gf))))
(GE (and (gull ethod) errorp)
(error "Wo such method for “S." (generic-function-nane gt)
nethod)))
tis Render and write methods
id-reader-nethod (class fn-nane slot-nane)
0d
repecializera (List class)
sbody ‘(elot-value object ',slot-nane)
‘environment (top-level-environnent))
(values)?
(@efun add-vriter-method (clase {n-nane slot-nane)
(ensure-neth
‘environnent. (top-Level-environnent))
(values)
111 Generic function invocation
apply-ganeric-function
(defun apply-generic-function (gt args)
(apply (generic-fuaction-discriminating-function gf) args))
js compute-discriminating-function|A Working Closette Implementation 303
(@ofun std-compute-discriminating-function (g#)
spear #'class-of
(required-portion gf args)))
(enfun (gethash classes (classes-to-enf-table gf) nil)))
e onfun
(funcall enfun args)
(elov-nethod-Lookup gf args classes)))))
applicable-methods
(Compute-applicable-nethods-using-classes gf classes))
(class-ot gf) the-clase-standard-gf)
-compute-effective-method-function
# conpute-effective-nethod-function)
(eet (gethash clas jovenf-table gf)) enfun)
(funcall enfun args)))
:i4 compute-applicable-nethods-using-classes
(dofun compute-applicable-methods-using-classes
(gf required-ci
(eort
(copy-List
(renove-if-not #' (lambda (nethod)
(every #’subclassp
required-classes
(nethod-specializers nethod)))
(goneric-function-aethods gf)))
#'(anbda (at a3
(funeal
at
andard-gt)
if gf) the-clas
‘std-nethod-nore-specifii
#zethod-more-specific-p)
gf mi n2 required-classes))))
nothod-nore-specific-P
(dotun std-nothod-nore-epecific-p (gf methodi method? required-clas304 Appendix D
il) ; Bogus top level lexical environment
(defun conpile-in-lexical-environnent (env lasbda-expr)
(@eclare (ignore env))
(compile nil lanbda-expr))
Bootstrap
(progn ; Extends to end-of-file (to avoid printing intermediate results)
to bootstrap Closette...")
sy steps
(nake-offective-slot-definition
name (car slotd)
‘samitarg)))
‘sinitform)
dofclass-etandar:
2. Create the standard-class netaobject by bi
tq the-class-standard-class
(allocate-std-instance
Seba,
(oake-array (length the-slots-of-standard-class)
‘nitial-elenest secret-unbound-value)))
bs 3. Install standard-class's (circular) class-of Link
(ots (std~instance-claes the-class-standard-class)‘A Working Closette Implementation 307
the-class-standard-class)
(It's now okay to use clags-... accessor)
4, Fill in standard-clase's class-slots
wtf (class-slots the-clase-standard-class) the-slots-of-standard-class)
(Skeleton built; it's now okay to call make-instance-standard-class.)
5. Hand build the clase t so that it bas no direct superclasses.
(sett (class-slots class)
class))
(It's now okay to define subc
6. Create the other supercl:
jefclass standard-object (t)
7. Define the full-blown version of standard-class,
jotq the-class-standard-claes (eval the-defclass-standard-class))
8, Replace all (3) existing pointers to the skeleton with real one.
(sett (std-instance-class (find-class 't))
tandard-class)
2 of t.)
andard-class (1.
» standard-object)
class ‘standard-object))
.s-standard-class)
~atandard-cli
function (t) 0)
hash-table (t) 0)Appendix D
-d-generic-function))
Voila! The clase hierarchy is in place
format t "Class hierarchy created.")
; (it's now okay to define generic functions and methods.)
(detgeneric print-object
(defmethod print-objec|
(print-unreadable~
(format strean
instance strean))
instance standard-object) stream)
rnstance strean :identity t)
instance)
bis Slot access
(dotgeneric slot-value-using-class (class instance slot-nane))
(@ofnethod slot-value-using-class
(clase standard-clags) instance slot-nane)
(etd-slot-value instance slot-nane))
Gefgeneric (setf slot-value-using-claes) (nev-value class instance slot-nane))
(defacthod (set! slot-value-using-cl:
ww-value (class standard-class) instance slot-naze)
(eet# (std-slot-value instance slot-nane) nev-value))
N.B. To avoid making a forvard reference to a (setf xxx) generic function:
J:f-slot-value-using-clase (nev-value clase object slot-nane)
ing-clase clase object lot-nane) nev-value))
clase instance slot-nane))
(etd-slot-exists-p instance slot-nane))
(dofgeneric slot-boundp-using-c
(@efnethod slot-boundp-using-cl
((class standard-class) instance slot-name)
(etd-slot-boundp instance slot-nane))
8 (clase instance slot-name))
~sakunbound-uaing-class (class instance slct-naze))
(defmethod slot-nakunbound-using-class‘A Working Closette Implementation. 309
(class standard-cl
(eté-elot-malunbound instance slot-naze))
Instance creation and initialization
(detgeneric allocate-instance (class))
(detmethod allocate-instance ((class standard-class))
instance)
(defmethed make-inetance ((clase symbol) Brest initargs)
(apply #'make-inetance (find-class class) initargs))
(aetgeneric initialize-instance (instance bkey))
(defeethod initialize-instance ((instance stand:
(apply #'shared-initialize instance t initargs
object) brest initargs)
instance Bkey))
dard-object) Arest initargs)
(apply # imitargs))
sric shared-initialize (instance slot-nanes Skey))
jethod shared-initialize ((instance standard-object)
slot-names Brest all-keys)
(doliat (slot (class-slots (class-of instan:
(let ((elot-naze (slot-definition-nane slot
(eultiple-value-bind (init-Key init-value foundp)
(get-properties
all-keys (elot~definition-initargs slot))
(declare ignore init-key))
(GE foundp
£ (elot-valve instance slot-nane) init-value)
2 (and (not (slot-boundp instance slot-nane))
(null (elot~definition-initfunction slot)))
slot-nases t)
(aenber slot-nane slot-nanes)))
(eet (elot-value instance slot-nane)
(funeall (slot~definition-initfunction slot
(or
p00?
instance)
fyi change-cl310 Appendix D
Golist (slot~nane (mapcar #’slot-def inition-n
(class-slets new-class)))
(when (and (siot-existe-p old-instance slot~
(lot-boundp old-instance slot-nane))
(sett (slot-value nev-inetance slot-naze)
‘lot-value old-instance slot-nane))))
d-instance-slots nev-inetance)
nev-instance old-instance initargs)
old-instance))
(defmethod change-class
(Gastence standard-object) (new-clase aysbel) rest initargs)
(apply #'change-clasa instance (find-class nev-class) initarge))
(dofgeneric up
Anstance-for-different-class (old new kkey))
(otnethod ‘nstance-for-different~class
Ld standard-object) (nev standard-object) rest initarga)
Get slots
(reaove-if #'(lanbda (slot~nane)
(Glot-exists-p old slot-nane))
(napcar #'slot~definition-nane
(class-
(apply #'shared-initialize new add
ts (class-of ne
ts initargs
Methods having to do with class metaobjects.
standard-class) str:
stream :identity
class)
(Gofmethod initialize-inatance :after ((class standard-class) skey krest args)‘A Working Closette Implementation au
(apply #'st4-after-initialization-for-classes class args))
Finalize inheritance
(dofgeneric finalize-inheritance (clas
(ofnethod tinalize-inheritance (cl
(std-finalize-inheritance class)
(values
1 Class precedence liste
(detgeneric compute-c
(eo
(std-compute-class-pr
wthod compute-cl
Slot inheritance
(aetgeneric conpute-offective-slot-definition (class direct-slote))
(aefzethod conpute-effective-slot-definition
((class standard-class) direct-slots)
(sta-compute-effective-slot-definition class direct-slots))
Methods having to do vith generic function metacbjecte.
(detuethod print-object ((gf standard-generic-function) strean)
(print-unreadable-object. (gf stream :identity t)
(format stream "7:(°S") “S"
ss-nane (class-of gf))
(generic-function-name gf)))
st)
(ofnethod snitialize-instance :after
‘{inalize-generic-function gf)
ff standard-generic-function) key)
Methods having to do vith method metacbjects
hod print-object
(print-unreadable:
(format strean312 Appendix D
(class-nane (class-of method))
(generic-function-nane
(gethod-generic-function method))
thod-qualifiers method)
(aapcar #'class-nane
(wethod-specializers metho
sthod)
sthod initialize-instance :after ((method standard-nethod) Bkey)
(aethod-function method) (compute-method-function method)))
Methods having to do with generic function invocation.
wte-diacriminating-function (
jefmethod compute-discriminating-function (
(sté-compute-discriminating-function gf
tandard-generic-function))
(otgeneric aethod-nore-specific-p (gf method! method? required-classes))
(detzethod nethod-nore-specific-p
((gf standard-generic-function) methodi method? required-classes)
(sté-nethod-nore-specific-p gf method! method? required-classes))
(defgoneric compute-effective-method-function (gf methods))
(etnethod conpute-effective-nethod-function
(gt standard!
(etd-conpute-ef fective:
je-function) methods)
I-function gf nethods))
(dotgeneric coapute-nethod-function
(defnethod compute-nethod-function (
(etd-compute-nethod-function meth¢
j3 deseribe-object is a handy tool for enquiring minds:
(detgeneric describe-object (object strean))
Abe-object (Cobject standard-object) stream)
format t “A Closette object”
“yPrinted representation: “S*
viclass: “Ss
vistructure *‘A Working Closette Implementation 313
(slot-boundp object en)
(and (slot-boundp object
(slot-value obje
(vatues))
(defaethod describe-object ((object t) strean)
(isp:describe object)
(valuei
(format t "“YClosette is a Knights of the Lambda Calculus production. *)
Mode:LISP; Package:NEWCL; Base:10; Syntax:Common-Lisp -*-
is This is the file newel.1iep
(in-package ‘nevel :use (Lisp):
(shadow '(defun faakunbound fboundp))
(export '(fdefinition defun fmakunbound fboundp print-unreadable-object))
34 Now macros to support function names Like (setf foo)
Gisp:detun setf-function-symbol (function-specifier)
ar function-epecifier)
(print-nane (format nil
Gntern print-nane
(eymbot,
function-specifier
function-specifie
ickage (cadr function-apecifier
Gisp:defun fooundp (function-specitier)
GE (congp function-spectfier)
(isp:fboundp (setf-function-syabol function-specifier))
(isp:fboundp function-specitier)))
(isp:detun faetinition (function-specitier)
(if (consp function-specifier)
yyabol-function (setf-function-symbol function-specifier))
isprsymbol~function function-specifser)))
(isp:defun fakunbound (function-apecifier)
(AE congp function-specit er)
(Lisp:fmakunbound (setf-function-symbol function-epecifier))
(Qiep:fmakunbound function-apecifier)))
Ht {definition (function-specifier) (nev-value)
“fdefinition ,new-value ,function-specitier))a4
Appendix D
dofun set-fdefinition (nov-value function-specifier)
(cadr tunction-specitier)
rest all-arg:
‘new-value)
if-function-symbol function-specifier)
sall-args))))
(sett (symbol-function function-specitier) ney-value)))
(@efaacro defun (nane formals tbody body)
Gaetsett
Gist
#1 Minimal tosts
(eacroexpand '(defun (sett foo) (av x y) ( x y)))
fun (sett baz) (new-value arg)
format t "setting value of “A to “A
\croexpand ‘(sett (baz (+ 2 2)) (+ 33
nev-value))
print-unreadable-object
print-unreadable-object is the standard way in the nev Common Lisp
to generate #& > around objects that can't be read back in. The option
lentity t) causes the inclusion of a representation of the object's
identity, typically some sort of machine-dependent storage address
sfeacro print-unreadable-object
(object stream Bkey type identity) kbody body)
(when type
"(format .strean. "8" (type-of .object.)))
s(uhen (and type (or body identity)?‘A Working Closette Implementation 315
a (format .strean. ‘si:tpointer .object.))
. stream, js:pointer .object.))
Excl strean, (excl: :pointer-to-fixnum .object.))
He:coral ' ‘strean.
(cel: :%ptr-to-int object .))
)
(format stream. ">*)
ail)E Full MOP Cross Reference
This appendix provides an alphabetical cross-reference from the simplified metaobject,
protocol developed in Part I to the full CLOS Metaobject Protocol presented in Part IL
Bach entry describes the counterpart in the full MOP ofa class, generic function, function
or macro introduced in Part I. Note that some of these things are actually in basic CLOS
rather than the MOP (eg., class-of). This appendix clearly identifies these cases
(add-nethod (generic-function) (method))
This generic function is actually specified in full CLOS. Its use in Part T and in the
fall MOP are in accordance with fu
(add-reader-nethod (fn-name)
No generic
be achieved in two steps:
specialized behavior when adding oF i
(add-writer-metihod (fn-name)
ionality exists. But, the same effect can
}) use writer-method-class to control the class of the
use either add-method or ini
ing the method.
ificant difference is that the initialization arguments passed to
is to allocate-instance
(apply-goneric-function (af) (args))
In later parts of Chapter 4, the protocol surrounding this generic function is redesigned,
and a new generic function called coupute-discriminating-function is introduced,
See its entry in this appendix for more information.
(apply-method (method) (args) (next-methods)
In later parts of Chapter 4, the protocol surrounding this generic
and a new generic function ¢
ction is redesigned,
led conpute-method-function is introduced. See its
entry in this appendix for more information.
Capply-methods (gf) (args) (methods)
In later parts of Chapter 4, the protocol surrounding this generic fi
designed, and a new generic function called compute-effective-nethod-function
is introduced. See its entry in this appendix for more information.
(change-class (object) (class) kkey)
‘This generic function is actually specified in full CLOS. Its use in Part I and in the
MOP are in accordance with full CLOS. The keyword arguments do not appear in318 Appendix E
original CLOS Specification, but are expected to appear in the
Lisp standard.
(class-direct-methods (class))
‘The generic function specializer-direct-methods is the direct analog of this func
tion, ‘The difference in the name reflects the fact that the full Metaobject Protocol
supports specializers which are not classes (i.c., eq) spe
(lass-direct-siots
is class accessor is act
anged behavior. The criti
returned are not propert
definition.
(class-direct-subclasses (class))
This class accessor is actually a generic function with the same name and behavior.
(class-direct-superclasses (class))
This class accessor is actually a generic function with the same name and behavior.
(class-name (class))
‘This class accessor is actually specified as a generic function in full CLOS. Its use in
Part I and in the full MOP are in accordance
(elass-of (object)
‘This function is actually specified i
are in accordance with full CLOS.
88))
a generic function wit
can't be called until after the class has been finalized
(class-slots (class))
This class accessor is actually a generic function with the same name and essen-
tially unchanged behavior. ‘The critical difference is thé
metaobjects returned are not prop
effective-slot-definition. In ad&
been finalized.
(conpute-applicable-nethods-using-classes (gf) (required-classes))
This generic function exists with the same name, but in order to support eql and
other non-class specializers, itis required to return a second value. The second value
indicates whether any of the returned methods in fact contain a non-class specializer.
(compute-class-precedence-list (class))
‘This generic function exists with the same name and functionality.
(compute-discriminating-function (of
‘This generic function exists with the sa
ANSI Common
ion with the same name and essentially un-
that the direct slot definition metaobjects
rather are instances of a subclass of direct-slot-
CLOS, Its use in Part I and in the full MOP
the same name and behavior. ItCross Reference to Full MOP 319
(compute-ef fective-method-function (af) (methods))
‘The closest counterpart to this generic function is compute-effective-method. The
difference is that compute-ef fective-method returns a form which is then converted
toa function by the implementation,
(compute-effective-slot-def inition (class) (slots)
‘This generic function exists with the same name and essentially unchanged functional-
ity. The critical difference is that the effective slot definition metaobject returned is not,
a property list, but rather is an instance of a subclass of effective-slot-definition.
In addition, the slot name is interposed between the two other arguments,
(conpute-method-function (method))
The closest counterpart to this generic function in the full MOP
Lanbéa, This allows the same capability of processing the bodies of
order to make it possible to compile method functions during file compilation, it is
called during the expansion of the defmethod form,
(compute-slots
is that the effective slot definition metaobjects returned
rather are instances of a subclass of effective-slot-
name) (supers ) rest (options))
‘This macro is is actually specified in full CLOS. Its use in Part I and in the full MOP
are in accordance wit LOS, except that it also accepts several additional options
not described in Part I. In addition, class definitions can be modified by editing and
re-executing defclass forms.
ts use in Part I and in the full MOP are
Iso accepts several additional options
addition, generic function definitions can be modified by
defgeneric forms
body (body))
full CLOS. Its use in Part I and in the full MOP are
LOS, except that it also accepts several additional options
not described in Part I. In addition, method definitions can be modified by editing and.
reexecuting defaethod forms.
(ensure-class (name) key)
‘A function exists with this name and essentially unchanged functionality. The
difference is that, as ined by ¢
lefclass, a class320 Appendix E
again with the same class name. In addition, the exact format of the arguments is
different.
wric-function (name) key)
ually specific 1LOS. Its use in Part I and
are in accordance with full CLOS. The difference is that, as with defgeneric,
1 generic function can be redefined by calling ensure-generic-function again with
the same function name. In addition, the exact format of the arguments is different.
(ensure-method (name) &key)
‘No function with this name or functionality exists.
(extra-nethod-bindings (method) (args) (nezt-methods))
‘The method body processing protocol is not specified at this level. Instead, a h
level generic function make-method-Lanbda is called to process the entire body of the
method. Specialized methods defined on make-method-Lambda can add bindings by
‘wrapping appropriate forms around the body of the method.
(finalize-inheritance (class))
‘This generic function exists with the same name and essentially unchanged function
ality. To support class redefinition, finalize-inheritance is called again each time
a class is redefined.
(Eind-class (symbol) koptional (errorp))
‘This function is actually specified in full CLOS. Its use in Part I and in the full MOP
are in accordance with full CLOS,
(generic-function-lambda-List (g/})
‘This generic function accessor is actually a generic function with the same name and
behavior.
(generic-function-methods (af))
‘This generic function accessor is actually a generic function with the same name and
behavior.
function-nane (g/))
mneric function accessor is actually a generic function with the same name and
(method-body
No fune this name exists, The source text of a method function is notCross Reference to Full MOP a2
normally preserved, Instead, the generic function method-function can be called
to get a function which, when called, executes the method body in its appropriate
lexical environment.
(method-environment (method)
No function with this name exists. See the entry for compute-method-function.
(mothod-generic-function (method))
‘This method accessor is actually a generic function with the same name and behavior.
(mothod-Lanbda-List (metho
This method accessor is actu
(method-more-specific-p
‘The method lookup protor
ethod-qualifiers (method!
‘This method accessor is actually a goneric function with the same name and behavior.
(wethod-specializers (method))
‘This method accessor is actually a generic function with the same name and behavior.
(print-object (object) (stream))
"This generic function is actually specified in full CLOS. Its use in Part I and in the
full MOP are in accordance with full CLOS.
(reinitialize-instance (object) kkey)
‘This generic function is actually specified in full CLOS. Its use in Part I and in the
full MOP are in accordance with full CLOS.
(ehared-initialize (object) (slot-names) Bkey)
‘This generic function is actually specified in full CLOS. Its use in Part I and in the
full MOP are in accordance with full CLOS, except that in the full MOP and CLOS,
clase-allocated slots are handled
(slot-definition-allocation
‘This slot definition accessor is actually a generic function with the same name and
behavior.
(elot-definition-initargs
‘This slot definition accessor
behavior.
(elot-dofinition-initform (slot)
t definition accessor is actually a generic function with the same name and
a generic function with the same name and behavior.
wod1) (method?) (classes))
specified at this level of detail.
tually a generic function with the same name and
yt-def inition-initfunction
‘This slot definition accessor is actually » generi
behavior.
ction with the same name and322 Appendix E
(elot-detinition-nane
This slot definition accessor is actually « generie function with the same name and
behavior
(lot-definition-readers (slo
This slot definition accessor is actually @ generic function with the same name and
behavior.
(elot-definition-writers (slo
‘This slot definition accessor is actually a generic f
behavior.
(slot-boundp (object) (slot-name})
This function is actually specified in full CLOS. Its use in Part T and in the full MOP
are in accordance with full CLOS
(slot-boundp-using-class (cla:
This generic function exists with the same name and essentially unchanged function-
ality. The critical difference is that the third argument is not a slot name, but rather
the effective slot definition metaobject for that slot.
(olot-exists-p (objec
This function is actual
are in accordance wi
(slot-makunbound (object
This function is actually specified in full CLOS. Its use in Part I and in the full MOP
Los.
(slot~makunbound-using-class (class)
This generic fanction exists with the same name and essential
ality. The critical difference is that the third argument is n
jective slot pject for that slot
(slot-value (object
unchanged function
slot name, but rather
CLOS. Its use in Part I and in the full MOP
are in accordance with
(slot-value-using-class
‘This generic function exists with the same name and essentially unchanged function-
ality. The critical difference hird arg is not a slot name, but rather
lefinition metaobject for that slotCross Reference to 323
((seté slot-value-using-class) (value) (class) (object) (slot-name))
generic function exists with the same name and essentially unchanged function-
‘The critical difference is that the
the effective slot definition metaobject for that slot
standard-class
This class exists with the sam
standard-generic-function
This class exists with the same name and essentially unchanged functionality.
standard-method
‘This class exists with the same name and essentially unchanj
(subclassp (cl) (
‘The Common Lisp function subtypep can be used to achieve the same behavior.
(update-instance-for-different-class (old) (new) key)
‘This generic function is actually specified in full CLOS. Its use in Part I an
full MOP are in accordance with full CLOS.
t a slot name, but rather
unctionality,
tionality.
in theReferences
Fogelong, and Mark S. Miler ‘Definition Groupe: Making
in Brace
1982,
Steele, Gay Common Lisp: The Lenquage, Digital Pres, 1984
Steele, Guy Common Lisp: The Language, Second Digital Press, 1990,
des Riviéres, Jim and Bran C. Smith *The Implementation of Procedurally Reflective
Languages," Proceedings 198 ACM Symposium on LISP and Punctional Programming, Avstin,
‘Tevas, August
Polymorphism.” OOPSLA '85
fed ab 8 special iste of S/G-
David A. *Object-Onented Programming with Flavors" OOPSLA ‘85 Conference Pro
‘peared asa special issue of SIGPLAN Notices 21,
OF [CLELIN, 770-864,Index
option, 20,
accessor-nethod-slot-definition
specification, 220
pendent,
specification, 164
add-direct-method
full-specifcation, 165
add-direct-subclass
fullxpecifieation, 166
add-function-bindings, 44
aa
add-variable-bindings, 43,
add-vriter-nethod, 39
method for dynanic~slot-class, 102
method for standard-class, 101
‘mini-specification, 100
allocate-instance
full-specifcation, 168
allocate-slot-storege, 27
apply-generic-function,
‘method for counting-gf, 109
for standard-generic~
function, 121
method for trusting-gf, 113
specification, 108
apply-zethod, 44, 305
‘before-method for count ing-nethod, 109
method for gf-with-arounds, 123
method for
standard-generic-function, 122
mini-specification, 120,
‘around-method, 122
around-method-p.
avtributes-ch
before-nethod-p, 43, 304
browsers, 48
for classes, 52
for generie functions and methods, 58, 60
puilt-in-class-of, 28, 283
sp
canonicalize-defclass-option, 287
canonical ize-defclass-options, 287
canonicalize-direct-slots, 286
canonical ize-direct-superclasses, 286
canonicalize-apecializers, 208
‘change-class, 310
‘method for (etandard-object
standard-class), 33
method for (standaré-object
clasa-default-initargs
full-epecification, 212
class-direct-default-initargs, 91-92,
o328
clase-prec
I-specification, 214
-protety]
I-specification, 214
class-slots, 18, 285
fall-specification, 214
lasses
object-oriented programming, 243
‘accessor functions, 19
identity conditions, 50
standard vs. specialized, 74
Index
class precedence list, 24
Flavors-style, 81
Loops-style, 81
protocol for computing, 78-82
‘CLOS. See Common Lisp Object System
collect-superclas
color-ixin
class definition, 15,
color-rectangle
class definition, 15,
class precedence list, 24
Common Lisp Object System, 2, 243
‘conpute-applicable-nethods-using-
clases
full-specificatio
conpute-class~default-initarge, 92
compute-class-precedence-list, 24, 311
method for flavore~cli
method for loops-cli
‘method for counting-¢!
method for standard-g
function, 129-130,
conpute-discrininating-function
Tull-specification, 175
coupute-ef fective-method
full- specification, 176
conpute-effective-method-function, 312
method for standard-generic~
function, 126-127Index
conpute-ef fective-slot-definition, 311
‘method for dynami:
method for atandard-class, 87
rmini-specification, 86
‘of fective-slot-def inition
full-specification, 177
compute-nethod-function, 312
‘method for counting-nethod, 128
compute-elots, 25, 311
method for attributes-class, 88
counting-gf, 109
count ing-method, 109
ccurrying, 125
D
dofault-inivarge-class, 91, 94
dofclass, 17-26, 285
rnetaclass option, 76
‘macro definition, 19
regenerating form, 53
dotgeneric, 34-35, 295,
srie-function-class 0}
demon method combination, See method
combination, standard
depth-tiret-proorder-superclassess, 62
describe-object, 312
design of metaobject protocols, 107-132
direct-slot-definition-class
display-defelasse, 55
diaplay-defgeneric, 61
Aisplay-generic-function, 61
ynanic-slot-boundp, 104
dynanic-slot-class, 102
dynazic-slot-nakunbound, 104
aynamic-slot-p, 102
E
offective-slot-definition-class
full-specification, 181
effective method functions, 125
‘memoization, 130,
clctive slot definition metacbject, 25
encapsulation, 89, 114
ted programming, 250
environment
‘of defaethod, 38
used by eval, 43
eql-specializer-object
specification, 188
examples
‘alternative class precedence lists, 80
append method combination, 123,
tilization arguments, 90
inherited information, 55330,
dynamic slots, 99
finding relevant generic functions, 63
finding subclasses, 52
ing all class names, 48, 52
def generic form, 60
Wg defnethed form, 60
sting generic functions, 112
accessor method predicates, 65
adding vanitla-flavor, 83
alternative implementation of
class-direct-subclasses, 51
ualizing an effective method, 64
extra-function-bindings
nd for encapsulated-ne
Index
full-specificat
F
fair use rules, 50
finalization
of classes, 23
final ize-inheritance, 24, 99, 311
‘after-method for
default-initarge-class, 04
‘mini-specifcation, 49,
sett function, 22
Find-goneric-function, 296
Find-method, 301
Find-nethod-combination
full-specification, 191
Find-programmatic-class, 68
Flavors-claes, 81
gonerate-inherited-slot~
specification, 55
generate-slot-specification, 54
generate-specialized-arglist, 61
generic-function-argunent~
c~function-diseriminating-
function, 204Index
e-function-method-class, 108, 294
e-function-aethod-class
Specification, 217
-function-zethod-combination
I-specification, 217
generic-function-methods, 34, 204
smini-specification, 60
generic-function-nethods
full-specification, 217
generic-function-nane, 34, 204
mini-specification, 59
standard vs. specialized, 74
gf-with-append, 123
gf-with-arounds, 123
glue layer, 17
331
initfunction
slot property, 20
initialize-instance, 309
after-method for etandard-class, 23,
method for standard-object, 31
instance
accessing slot bindings, 28
protocols for, 96
allocation, 27|
protocol for, 99
changing class of, 32
object-oriented programming, 243
ject protocols, 71
K
Knights of the Lambda Calculus, 313
L
layering. See metaobject protocol, See also332
M
make-direct-elot-definition, 21, 25, 289
rethod for standard-class, 90, 73
method for syzbot, 30
minispecifcation re: metaobject
ake-progranmatic-class, 69
ake-progranmatic-inatance, 67
and functional protocols,
and procedural protocol
uring generic function invocation, 125
discriminating functions, 129
‘accessor functions
fair use rules, 50
accessor functions for
‘lass, 19
‘generic function, 34
method, 37
slot, 21
lass, 18
definition, 17
direct slot definition, 21
metaobjects
standard vs. specialized, 75
metastability, 270
method
accessor, 39
applicable, 41
next methods, 44
ordering, 41
sequencing, 42
combination, 43
mini-specification,
nethod-function, 295
nethed-function
full-specification, 219
ethod-generic-function, 37, 295
‘mini-specifcation, 60
nethod-generic-functionIndex
nethod-Lanbda-List
full-specifcation, 219
nethod-nore-specific-p, 42, 312
method for atandard-generic~
nethod-quali
rmini-specification, 60
nethod-qualifiers
fullspecification, 219
method dispatch. See generic function,
invocation
‘method functions, 127
memoization, 130
method lookup. See genetic function,
ct aT
specialization, 107
standard vs. specialized, 74
note-operation, 98
°
con-backstage, 70,
on-stage, 13.
summary, 47
optimization. See performance
overriding of methods, 112
P
paint
generic function definition, 15
method definition, 15,
parse-defmethod, 298
performance, 45
‘and memoization,
of generic functi
prinary-nethod~p,
primary method. See
print-object, 26, 308
method for standard-clase, 50, 77
procedural protocols, 111
procedural reflection, 270
producers
program analysis tools, 48
protocol. See metaobject protocol
R
read-dynanic-slot-value, 103
reeader
slot option, 20
ynod-class
specification, 224
jothod-p, 65
slot property, 20,
rectangle
class definition, 15,
reflection, 7, 270334
reinitialize-instance, 309
method for standard-objac
relevant-generic-functions, 63, 65
rezove-dependent,
specification, 225
renove-direct-nethod
renove-nethod, 301
renove-method
full-specification, 229,
required-portion, 298
reset-slot-accest-bistory, 98
8
set-funcallable-instance-function
full-specification, 230
(eetf class-nane
full specifcat
(sett find-clase
(set! generic-function-naze)
full-specification, 231
slot-attribute), 89
slot-value), 29, 97
slot-value-using-class)
we-method for nonitored-class, 97
specification, 231
moth standard-clase, 97
shared-initialize, 309
method for standard-object, 31
slot-accees-history, 98
slot-attribute, 88
‘et function, 89
slot-attribute-bucket, 89
slot-boundp, 29, 97, 289
slot-boundp-ueing-claes, 308,
‘before-method for nonitored-class, 07
‘method for standard-clase, 97
slot-boundp-using-class
full-specification, 233
slot-contents, 27
slot-definition-allocation, 200
mint-specification, 101
Index
slot-definition-allocation
full-specifiation, 221
slot-definition-initarge, 21, 290
rmini-specification, 53
slot-definition-initargs
full-specification, 221
slot-definition-initform, 21, 200
‘mini-specifcation, 53
slot-definition-initforn
specification, 221
slot-definition-initfunction, 21, 289
‘mini-specifcation, 53
slot~def inition-initfunction
specification, 222
slot~dofinition-location
full-specifieation, 224
specification, 53
slot-definition-nane
fulhspecifcation, 222
slot-definition-readers, 21, 200
specification, 54
slot-definition-readers
specifica
slot-definition-type
full-specifcation, 222
slot-definition-eriters, 21, 200
rmini-specification, 54
slot-definition-uriters
full-specification, 223
slot-exists-p, 30, 283,
slot-exists-p-using-class, 308
slot-location, 29, 101, 282
slot-makunbound, 30, 97, 283
slot-makunbound-using-class, 308
before-method for nonitored-class, 97
‘method for standard~class, 97
slot~nakunbound-using-clase
specification, 234
slot-value, 29, 96, 282
sett function, 29
slot-value-using-class, 308
before-method for nonitored-class, 97
method for dynanic~slot-class, 103
method for standard-clase, 97Index
specification, 96
slot inheritance rules, 25
slot options, 20
sot properties, 20,
slots
riented programming, 243
initialization, 30
standard metaobject classes, 74
standard metaobjects, 74
std-snstance-class, 27
std-tie-br 292
sub-specializer. See sub-specializer-p
sub-spacializer-p, 42
subclasses, 52
subclagep, 41
subprotocols. See metaobject protocol,
layering
theatre metaphor, 13
topological-sort, 291
‘erusting-counting-gf,
‘trusting-gt, 112
u
update-dependent,
full-specification, 289
update-instance-for-different~
clase, 34, 310
v
validate-superclass
slot property, 20