0% found this document useful (0 votes)
299 views330 pages

MIT Press - The Art of The Metaobject Protocol - Gregor Kiczales, Jim Des Rivières, Daniel G. Bobrow (ISBN0262111586) (1991) (LISP) (Rotated, OCRed)

Lisp book

Uploaded by

GrandeBoi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
0% found this document useful (0 votes)
299 views330 pages

MIT Press - The Art of The Metaobject Protocol - Gregor Kiczales, Jim Des Rivières, Daniel G. Bobrow (ISBN0262111586) (1991) (LISP) (Rotated, OCRed)

Lisp book

Uploaded by

GrandeBoi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 330
The Art of the Metaobject Protocol Gregor Kiczales, Jim des Riviéres, and Daniel G. Bobrow The MIT Press Cambridge, Massachusetts London, England Contents 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 69 i % 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 Contents Acknowledgments 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, Mark Introduction 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 implementat sone 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 programming 4 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 this Introduction 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 implementation 6 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 detail del 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 revis 10 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 PROTOCOLS 1 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 ac 4 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 classes How 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 are How 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 ine 18 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 consi How 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 to 2 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 provided 26 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 of How 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 ac 32 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 armen Chapter 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 aymbsis 36 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 accessor Introspection 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 ject 54 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 to 58 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 and Introspection. 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 metaobject 60 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 the Introspection 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 function ntrospection 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 been on 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- programming 3 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 in Chapter 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 for 76 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 the Extending 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-class Chapter 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 ways oy 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 the 86 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 take Bxtending 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 ar 4 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 alternate 96 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 3 99 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. Pe 100 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 pointers Chapter 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 provided Extending 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 of Protocol 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 to a 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 Caos 16 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 function 120 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, this Protocol 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 usual 123 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 the 126 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 tfc Protocol 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. 1 Protocol 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 the 130 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 CLOS 135, 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. This Concepts 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 sp we 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 if Concepts 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 generic a 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 which 146 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 value 8 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 the Concepts 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 initialization 150 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 called 152 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 exact Concepts 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 examples 154 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 processed Concepts 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 initialization Chapter 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 slots Chapter 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 class Concepts 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 1 164 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. objects 170 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 of Generic 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 of 176 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 of Generic 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 keyword Generic 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 the Generic 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 specialize 196 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 value 198 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 violated ization 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 from 202 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 to 208 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 of Generic 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 the Readers 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 Class Generie 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 the 220 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 wi 224 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 of 226 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 faci Generic 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 object 232 (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 class Goneric 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 generic Generic 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 two generic 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 virtual Introduction 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., shap 248 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 option Introduetion 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 recone 252 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 are A.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 combination B 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 subclass Solutions 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) y Solutions 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 value 260 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 nique jons 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 mide 262 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 list Solutions 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 the 70 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 (by us, 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 regular Living 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 metastability D 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-definition 280 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 slot 282 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 x 284 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 functions A 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 D logical-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 291 292 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-duplicates A 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-table 294 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-function A 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-class 296 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 cannot A 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 qualifier 302 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-clas 304 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-cl 310 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 strean 312 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 in 318 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. It Cross 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 class 320 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 not Cross 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 and 322 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 slot Cross 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 the References 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, o 328 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-127 Index 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, 55 330, 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, 204 Index 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 also 332 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-function Index 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, 270 334 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, 97 Index 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

You might also like