CLOS To start off with CLOS pronounced kloss

  • Slides: 36
Download presentation
CLOS • To start off with, CLOS (pronounced “kloss” or “seeloss”) is very different

CLOS • To start off with, CLOS (pronounced “kloss” or “seeloss”) is very different from other object oriented programming languages – Multiple inheritance is permitted – Class members are encapsulated, but methods are defined outside of classes, so encapsulation is only partial – Information hiding is not enforced • you can define accessor functions, but there is an overriding function, slot-value, that permits access to any object’s members – There is no “message passing” but instead method evocation through generic functions • These differences are all made available because of the desire to make inheritance more powerful than what is available in most other OOPLs – Many people feel that CLOS is not a true object system whereas others love it

CLOS as an OOPL

CLOS as an OOPL

Recall the Structure • The original form of object-oriented programming was provided by the

Recall the Structure • The original form of object-oriented programming was provided by the structure – You define the non-homogenous nature by listing slots – Accessor and constructor functions are generated as part of the structure definition, and you can define others – Single inheritance is available by extending a definition • Inheritance in this case means to inherit everything, and you are not able to override previous members (slots) although you can create more specialized accessor and constructor functions • As OOPLs became more popular in the mid 1980 s, it was decided that CL needed something more powerful than structures and so CLOS was created and then added to the language

What Does CLOS Give You Then? • CLOS gives you the ability to utilize

What Does CLOS Give You Then? • CLOS gives you the ability to utilize multiple inheritance – This might be considered a good thing by some, but overly complex by others – There is also an ability to control what gets inherited • Aside from these two things, CLOS is unlike other OOPLs for the following reasons: – You can dynamically change an object’s class – You can dynamically combine methods without access to source code – You can dispatch (invoke) based on either class or object identity – You can dispatch on multiple objects – You can provide user-defined code to determine method combination – Additionally, slots can contain data or code so that invoking a slot of an object carries out an action

All Types are Objects in CL • I’ve been purposefully misleading to this point

All Types are Objects in CL • I’ve been purposefully misleading to this point of the course – It turns out that all of the types that we have used to date (numbers, characters, strings, lists, etc) are objects – All objects inherit from T – One type of object is a standard class • Classes that you define will inherit from that class • The class (type) hierarchy is given below (Standard-Object and Structure. Object have numerous subclasses, omitted here)

CLOS: Some Basics • You define classes using defclass – This is much like

CLOS: Some Basics • You define classes using defclass – This is much like defstruct in that you supply the slots and various specifications for slots • e. g. , default values, accessor functions – Classes may have 0 parents (in which case they default to inheriting from standard-object), 1 parent, or multiple parents • You define methods using defmethod – Methods are not necessarily associated with any give class – Instead, methods are grouped together by common name where each method is unique in terms of the type(s) of parameter it expects • you either define a generic function for each group of methods, or it is generated automatically – Instead of message passing, a method is invoked by calling the generic function • the generic function selects which method(s) to then call • several methods may be called with one invocation – this is where things get complicated!

Simple Example (defclass ball ( ) (color type size)) (defmethod get. Volume ((b ball))

Simple Example (defclass ball ( ) (color type size)) (defmethod get. Volume ((b ball)) (* (/ 4 3) pi (cube (slot-value b 'size)))) (defmethod get. Surface. Area ((b ball)) (* 4 pi (square (slot-value b 'size)))) (setf b 1 (make-instance 'ball)) (setf (slot-value b 1 'color) 'red) (setf (slot-value b 1 'type) 'medicine-ball) (setf (slot-value b 1 'size) 10) (setf b 2 (make-instance 'ball)) (setf (slot-value b 2 'color) 'orange) (setf (slot-value b 2 'type) 'basketball) (setf (slot-value b 2 'size) 15) (defmethod get. Weight ((b ball)) (let (weight-external weight-internal) (if (or (equal (slot-value b 'type) 'medicine-ball) (equal (slot-value b 'type) 'filled)) (setf weight-internal 1) (setf weight-internal 0)) (setf weight-external (* (get. Surface. Area b))) (setf weight-internal (* (get. Volume b) weight-internal)) (+ weight-external weight-internal)))

Defclass Explored • Defclass expects three things – The name of the class –

Defclass Explored • Defclass expects three things – The name of the class – The parent class(es) in parens – empty parens if you want this class to have no parent • if no parent is specified, then the class has a default parent of Standard. Object (similar to inheriting from Object in java) • all slots are inherited from the parent(s) – methods defined on the parent(s) are also inherited, we’ll talk about this later • multiple parents are allowed – the order that you place the parents will be used to break ties in cases where items (slots, methods) share the same name between parent classes – A list of slot names • as with structures, slot names can have a number of additional arguments such as default values, in which case you place the slot name and its arguments in an extra layer of ( )

Slot Arguments • Slots are not protected through information hiding • To enforce some

Slot Arguments • Slots are not protected through information hiding • To enforce some information hiding, define accessors – Three types of accessor functions are reader, writer, and accessor (read or write) • to define accessor functions for a slot, you can state any or all of these: – : accessor name, : reader name, : writer name – Examples: • (defclass ball ( ) (color (type : reader ball-type) (size : accessor ballsize))) – we can get b 1’s type by doing (ball-type b 1) – or set b 2’s size by doing (setf (ball-size b 2) 12) – but we can only access color by (slot-value b 1 ’color) – Notice the accessor function does not preclude someone from using slot-value as in (setf (slot-value b 2 ’ball-size) 0) • Therefore, while accessors give us a way to define an interface, no object is truly ever protected since we can always use slot-value!

More Slot Arguments • You can also define default values and default constructors for

More Slot Arguments • You can also define default values and default constructors for any slot – Default Initialization: : initarg symbol • this allows the caller to place : symbol value in the make-instance instruction so that the slot will be provided an initial value • usually you will use the slot-name as the symbol with the name preceded by a : as in : color or : size – Construction Initialization: : initform expression • this provides a default value for the slot, expression will be evaluated at the time the object is instantiated through make-instance – if expression is a function call, the function gets invoked and its return value is used to initialize the slot – expression will be evaluated only if an : initarg does not exist, or was not supplied in the make-instance statement • note that the constructor is only for the slot, not for the entire object – Example: • (defclass ball ( ) ((color : initarg : color) (type : reader ball-type : initform (input-ball-type)) (size : accessor ball-size)))

Methods: An Introduction • The complexity of CLOS lies in the ability to create

Methods: An Introduction • The complexity of CLOS lies in the ability to create a variety of same-named methods – we will start however with simple methods • Use defmethod to define a method – (defmethod name (params) body) • The main difference between a method and a function is that at least one parameter must include the type of object that the method is defined for – assume our ball includes slots to store the <x, y, z> coordinates – the following method might be used to define how high a ball can be bounced Notice how one parameter includes a type (ball) but not all parameters must have them (defmethod bounce ((b ball) hardness) (let ((btype (ball-type b))) (when (or (equal btype ’basketball) (equal btype ’tennisball)) (setf (slot-value b ’z) (* hardness (slot-value b ’z))))))

A Constructor Function • Let’s create a method to construct a ball object for

A Constructor Function • Let’s create a method to construct a ball object for us – What should the constructor do? • initialize the slots – Which slots? • at least the type and size, the initial x, y, z coordinates may be unknown, so we can use optional parameters (defmethod construct-ball ((b ball) type size &optional x y z) (setf (slot-value b 'type) Notice that if z is unknown we change (setf (slot-value b 'size) all coordinates to be unknown (if z (progn (setf (slot-value b 'x) x) If the ball class would normally default (setf (slot-value b 'y) y) x, y and z to 0, as we might hope, then this constructor method changes those (setf (slot-value b 'z) z)) default values, probably inappropriately (progn (setf (slot-value b 'x) 'unknown) (setf (slot-value b 'y) 'unknown) (setf (slot-value b 'z) 'unknown))))

An Alternative Constructor • CLOS does not automatically call a constructor like in Java

An Alternative Constructor • CLOS does not automatically call a constructor like in Java when you use new – so if you want a constructor to execute, you must invoke it explicitly • In the previous version, we already had a ball, and then we would invoke its constructor by calling (construct-ball myball …) – here, we have a constructor function (not a method) that creates a ball and sets up its slot values (defun construct-ball (type size &optional x y z) (let ((temp (make-instance 'ball))) (setf (slot-value temp 'type) Of the two approaches, (setf (slot-value temp 'size) neither is really similar (if z (progn to Java’s constructor (setf (slot-value temp 'x) x) (setf (slot-value temp 'y) y) (setf (slot-value temp 'z) z)) (progn (setf (slot-value temp 'x) 'unknown) (setf (slot-value temp 'y) 'unknown) (setf (slot-value temp 'z) 'unknown))) temp))

More on Method Parameters • When writing a method, at least one of the

More on Method Parameters • When writing a method, at least one of the parameters must be specialized – that is, listed with its type – The type specialization is what makes a method unique – Other parameters may or may not be specialized • in the previous example, hardness is not specialized and so could take on any type – Consider a situation where you have two sets of classes • an item to bounce, and a person • you may want a method based on a type of ball, or a type of person, or both • below, we have such a case based on whether the item being bounced is a ball or brick and whether the person is an athlete, a normal person, or other (defmethod bounce ((b ball) (p athlete) hardness) …) (defmethod bounce ((b ball) (p person) hardness) …) (defmethod bounce ((b brick) p hardness) …) Notice that with two specialized parameters, the method is no longer defined for a single class

Example (defclass ball ( ) ((type : accessor ball-type : initarg : type) (size

Example (defclass ball ( ) ((type : accessor ball-type : initarg : type) (size : accessor ball-size : initarg : size) (x : accessor ball-x : initarg : x : initform 0) (y : accessor ball-y : initarg : y : initform 0) (z : accessor ball-z : initarg : z : initform 0))) (defclass brick ( ) ((x : accessor brick-x : initarg : x : initform 0) (y : accessor brick-y : initarg : y : initform 0) (z : accessor brick-z : initarg : z : initform 0))) (defclass person ( ) ((type : accessor person-type : initarg : type) (size : accessor person-size : initarg : size))) (defclass athlete (person) ((sport : accessor athlete-sport : initarg : sport)))

Example Continued (defmethod bounce ((b ball) (p athlete) hardness) (cond ((equal (ball-type b) (athlete-sport

Example Continued (defmethod bounce ((b ball) (p athlete) hardness) (cond ((equal (ball-type b) (athlete-sport p)) Here, how high (setf (ball-z b) (* (ball-z b) hardness))) (t (setf (ball-z b) (* (ball-z b). 5 hardness))))) something bounces is based not only on the type of thing (ball (defmethod bounce ((b ball) (p person) hardness) vs. brick) but also (setf (ball-z b) (* (ball-z b). 3 hardness))) based on whether the person bouncing it is (defmethod bounce ((b brick) (p athlete) hardness) (setf (brick-z b) (* (brick-z b). 1 hardness))) an average person or an athlete (defmethod bounce ((b brick) (p person) hardness) (setf (brick-z b) 0))

With-Slots • Using the accessor function simplifies how to access slot values, but can

With-Slots • Using the accessor function simplifies how to access slot values, but can still be cumbersome • A short-cut function is with-slots – Form: • (with-slots (slots) object body) – Examples: (defmethod increase-size ((b ball) new-size) (with-slots (size x y z) b (setf x (+ x new-size)) (setf y (+ y new-size)) (setf z (+ z new-size)) (setf size new-size))) (defmethod foobar ((a aclass) (b aclass)) (with-slots (x y) a (with-slots (z) b (print (list x y (slot-value a ’z)) (print (list (slot-value b ’x) (slot-value b ’y) z)))))

Two Utility Functions • type-of – provide it any datum and it returns the

Two Utility Functions • type-of – provide it any datum and it returns the most specific type – alternatively, you can use class-of – If you do (class-of obj)) you get obj’s class’ parent class • find-class – when passed the class name (as a symbol), returns the actual class – this is a pointer to the class definition, which itself is an object > (find-class 'ball) #<STANDARD-CLASS BALL 2168 C 454> – you could combine this with describe, for instance, to see a description of the class as in (describe (find-class ’ball)) • notice this is different from using describe on an instance, this enumerates the elements of the class itself (which contains things that you may or may not care to see)

Some Notes on Objects • Recall that in CL, all types are actually objects,

Some Notes on Objects • Recall that in CL, all types are actually objects, including structs • Here are some restrictions on types of objects – For built-in-classes (string, number, character, etc) • • you may not use make-instance you may not use slot-value you may not use defclass to modify you may not create subclasses – For structure-classes (that is, classes created through defstruct) • • you may not use make-instance it might work with slot-value (implementation-dependent) use defstruct to subclass application structure types consequences of modifying an existing structure-class are undefined: full recompilation may be necessary – For standard-classes (defined through defclass) • none of the above restrictions apply

Example: Shapes (defclass shape ( ) ((x : accessor shape-x : initarg : x)

Example: Shapes (defclass shape ( ) ((x : accessor shape-x : initarg : x) (y : accessor shape-y : initarg : y))) (defmethod move-to ((figure shape) new-x new-y) (setf (shape-x figure) new-x) (setf (shape-y figure) new-y)) (defmethod r-move-to ((figure shape) delta-x delta-y) (setf (shape-x figure) (+ delta-x (shape-x figure))) (setf (shape-y figure) (+ delta-y (shape-y figure)))) (defmethod draw ((figure shape))) (defclass circle (shape) ((radius : accessor circle-radius : initarg : radius))) (defmethod draw ((figure circle)) (format t "~&Drawing a Circle at: (~a, ~a), radius ~a~%" (shape-x figure) (shape-y figure) (circle-radius figure))) (defmethod set-radius ((figure circle) new-radius) (setf (circle-radius figure) new-radius))

Example Continued (defclass rectangle (shape) ((width : accessor rectangle-width : initarg : width) (height

Example Continued (defclass rectangle (shape) ((width : accessor rectangle-width : initarg : width) (height : accessor rectangle-height : initarg : height))) (defmethod draw ((figure rectangle)) (format t "~&Drawing a Rectangle at: (~a, ~a), width ~a, height ~a~%" (shape-x figure) (shape-y figure) (rectangle-width figure) (rectangle-height figure))) (defmethod set-width ((figure rectangle) new-width) (setf (rectangle-width figure) new-width)) (defmethod set-height ((figure rectangle) new-height) (setf (rectangle-height figure) new-height)) (defun polymorph( ) (let ((scribble) (a-rectangle))) (setf scribble (list (make-instance 'rectangle : x 10 : y 20 : width 5 : height 6) (make-instance 'circle : x 15 : y 25 : radius 8))) (dolist (a-shape scribble) (draw a-shape) (r-move-to a-shape 100) (draw a-shape)) (setf a-rectangle (make-instance 'rectangle : x 0 : y 0 : width 15 : height 15)) (set-width a-rectangle 30) (draw a-rectangle))

Generic Functions vs. Methods • When you define a method whose name had not

Generic Functions vs. Methods • When you define a method whose name had not previously been defined, CLOS automatically generates for a you a corresponding generic function – The generic function is used for bookkeeping in CLOS • it determines what method(s) to invoke when there is a method call – You may also write your own generic functions • (defgeneric name (params) : documentation “…”) • the generic function requires a name and list of unspecialized parameters – unlike the defmethod in which at least one parameter must be specialized • there is no body to the generic function – CLOS sets up a table for the generic function once it has been defined • as new methods that use the same name are defined, they are added to the appropriate generic function’s table by listing, for a specialized parameter list, what method(s) are invoked – The generic function is necessary for any methods but you do not have to bother with defining one yourself

Example • From our shape example, when we first define the draw method, the

Example • From our shape example, when we first define the draw method, the following generic function was generated: – (defgeneric draw (figure) : documentation “”) • the generic function draw, had one entry at this point, ((figure shape)) – would invoke that first defined defmethod • As other draw defmethods were defined, the generic function was added to: – draw ((figure circle)) – call the second definition – draw ((figure rectangle)) – call the third definition • The generic function is consulted every time the method is called to determine which specific definition should be invoked – this is how polymorphism is implemented – what if there is no specific defmethod defined for this class? • then the generic function finds the closest matching definition by moving up the class hierarchy until a class is found that has a definition • this gets more complicated when we take into account multiple inheritance

Inheritance • In CLOS, inheritance is all-inclusive – the child class inherits all slots

Inheritance • In CLOS, inheritance is all-inclusive – the child class inherits all slots from the parent class – further, all methods defined on the parent class can be invoked by instances of the child class – the generic function selects the most specific definition, if none are defined for this class, then the parent class’ definitions are consulted • In order to override inheritance – you must redefine things • you can redefine a slot – although it will have the same name you can change its initform, initarg or accessor function – accessors are combined (new and old are unioned) – initargs are combined (new and old are unioned) – initforms are overridden • you can redefine a method by including this class’ name as the parameter specifier rather than the parent class

Multiple Inheritance • In order to create a class that inherits from multiple classes,

Multiple Inheritance • In order to create a class that inherits from multiple classes, you just list all of the parents – (defclass multiplechild (parent 1 parent 2 parent 3) ((; ; more specific slots go here if desired))) • When it comes to inheriting from multiple parents – slots are inherited from the left-most class first, and then each successive class to the right as long as slot names do not conflict – when there is conflict, then only the first slot of the same name is inherited – the same will be true of inheriting methods – the generic function will select a method based on the left-to-right listing of each parent class • if none is found, then polymorphism kicks in and each grandparent is checked from left-to-right

Example (defclass creature ( ) ((type : accessor creature-type : initform 'unknown) (height :

Example (defclass creature ( ) ((type : accessor creature-type : initform 'unknown) (height : accessor creature-height : initform 'unknown) (weight : accessor creature-weight : initform 'unknown) (habitat : accessor creature-habitat : initform 'unknown))) (defclass bird (creature) ((type : initform 'bird) (habitat : initform 'air))) (defclass mammal (creature) ((type : initform 'mammal) (habitat : initform 'ground))) (defclass ostrich (mammal bird) ((type : initform 'ostrich))) (defmethod lives ((c creature)) (format t "~A" (creature-habitat c))) (defmethod lives ((b bird)) (format t "I live in the air")) (defmethod lives ((o ostrich)) (format t "I am an ostrich")) (lives (make-instance ’ostrich)) “I am an ostrich” (lives (make-instance ’bird)) “I live in the air”

What About Multiple Specificiers? • Imagine that a method had two parameters, both of

What About Multiple Specificiers? • Imagine that a method had two parameters, both of which were specialized? – Consider the following partial definitions for some method op 2 • • (defmethod op 2 ((x number) (y number)). . . ) ; method 1 (defmethod op 2 ((x integer) (y integer)). . . ) ; method 2 (defmethod op 2 ((x float) (y number)). . . ) ; method 3 (defmethod op 2 ((x number) (y float)). . . ) ; method 4 – Which version is called for each of the following? • • (OP 2 11 23) – method 2 (OP 2 13 2. 9) – method 4 (OP 2 8. 3 4/5) – method 3 (OP 2 5/8 11/3) – method 1 – In a left-to-right manner, each parameter is determined by following the hierarchy, so (OP 2 13 2. 9) identifies methods with an integer first and then a float second (method 3 is the only one that fits integer first, but it does not have float second) so we try the parent class of integer (number)

Class Precedence List • In order for the generic function to know which method

Class Precedence List • In order for the generic function to know which method to invoke, every time a new defmethod statement is defined, the associated generic function consults its class precedence list for the specialized parameter(s) • Consider a method that accepts an instance of one of these classes, and a number – the CPL is in essence the concatenation of all classes and their parents of the classes in the parameter list – consider the figure on the right showing the class hierarchical Imagine that we have a method structure defined with these specialized params: – C 3 is a subclass of C 1, C 5 is a • (C 2 Integer) subclass of both C 3 and C 2, etc • (C 2 Number) • The CPL is consulted for each • (C 3 Integer) parameter from left to right until a • (C 3 Number) match is found for all parameters Which method is invoked if we call it with a C 6 and an integer? A C 6 and a float?

Calling Multiple Methods • The strength of the generic function in CLOS is that

Calling Multiple Methods • The strength of the generic function in CLOS is that multiple methods can be invoked when you call one – the generic function keeps track of the order by which to call functions • (call-next-method) is a function that allows a method to invoke the next one – if we had added (call-next-method) as the last thing that lives did for the ostrich class, then • lives of ostrich would print “I am an ostrich” and then • call lives of the mammal class since there is none, it would then • call lives of the bird class and we would get “I live in the air” – if we had also added (call-next-method) to the bird class, it would then call lives for the creature class and we would also get “Ground” output • Thus, call-next-method can, in a way, take the place of super( ); as used in Java to invoke the parent class’ method of the same name

Using Multiple Methods • Aside from using call-next-method, you can also create combinations of

Using Multiple Methods • Aside from using call-next-method, you can also create combinations of methods so that, when called, a group of methods, assembled by the generic function, will all be invoked, one at a time – to accomplish this, there are 3 specifiers that can be placed in a defmethod to denote when the method should be called • : before, : after, : around – these dictate when this method should be called • if a method is available, it is called • if there is an additional method whose parameters match, and has : before, then it is called first • if there is an additional method whose parameters match, and has : after, it is called last – with these, if there are multiple methods whose parameters match, then the methods are called using the CPL like the previous example • if there is an additional method whose parameters match and has : around, then this method, and only this method, is invoked • You can add call-next-method if you want an around method to invoke the next around method

(defclass food ( )) Example (defmethod cook : before ((f food)) (print "A food

(defclass food ( )) Example (defmethod cook : before ((f food)) (print "A food is about to be cooked. ")) (defmethod cook : after ((f food)) (print "A food has been cooked. ")) (defclass pie (food) ((filling : accessor pie-filling : initarg : filling : initform 'apple))) (defmethod cook ((p pie)) (print "Cooking a pie. ") (setf (pie-filling p) (list 'cooked (pie-filling p)))) (defmethod cook : before ((p pie)) (print "A pie is about to be cooked. ")) (defmethod cook : after ((p pie)) (print "A pie has been cooked. ")) (setq pie-1 (make-instance 'pie : filling 'apple)) (cook pie-1) "A pie is about to be cooked. " "A food is about to be cooked. " "Cooking a pie. " "A food has been cooked. " "A pie has been cooked. " (cooked apple) The return value is from the main method (in this case, pie’s non-before/after version of cook)

Example Continued – Using : around (defmethod cook : around ((f food)) (print "Begin

Example Continued – Using : around (defmethod cook : around ((f food)) (print "Begin around food. ") (let ((result (call-next-method))) (print "End around food. ") result)) (cook pie-1) "Begin around food. " "A pie is about to be cooked. " "A food is about to be cooked. " "Cooking a pie. " "A food has been cooked. " "A pie has been cooked. " "End around food. " (cooked apple)) Why should (cooked …) appear twice? Here, the around method uses call-next-method so that the before, main and after methods are also invoked Without this however, notice what happens (defmethod cook : around ((f food)) (print "Begin around food. ") (print "End around food. ")) (cook pie-1) "Begin around food" "End around food"

Example • A (slightly) better example is presented here – We have an adventure

Example • A (slightly) better example is presented here – We have an adventure game where the base class is a creature • creatures will have some way to attack and some way to defend, for now we will use three slots: attack-points, life-points and defense-points – we define an attack method for creature which is passed another creature – if a random number generated by our creature > the other creatures defensepoints, then this creature generates a random number from 1 to attack-points and this is deducted from the other creature’s life-points – We extend creature to a human class and a dragon class, which are extended into magician and magic-negating-dragon respectively • rather than redefining attack for these four subclasses, we can use some combination of before, after and around – for instance, if a human is able to perform two attacks, then a : before method might invoke the parent method twice – the magic-negating-dragon might have an : around method that checks to see if the attacking creature is using magic, if so, nothing happens, otherwise this method calls the next method to permit the attack

Specializing on an Instance • Just as parameters in a method specialize on classes,

Specializing on an Instance • Just as parameters in a method specialize on classes, you can specialize parameters on specific instances as well – Form: • (defmethod name ((var instance-name) params) body) – Example • (defmethod cook ((p *my-pie*)) (print "My pie is in the oven. ")) – This is of limited use since a programmer may not know what instances are going to be generated – Consider though an example where the system has a predefined object • if a method is called with any instance, one behavior might be desired, but if called with this system object, then another behavior is desirable • one of the texts gives an example of what happens when a customer overdraws on their bank account versus the bank president!

Code in Slots • Slots (whether from an object or a struct) just store

Code in Slots • Slots (whether from an object or a struct) just store what any other CL variable stores, a pointer – this can point to a number or symbol or list, but can also point to code – consider a macro that generates code and places that code into the slots of an object – now, an object-user can invoke the necessary code by doing (eval (slot-value obj ’slot-name)) • or alternatively, if the function name itself has been placed into the slot rather than the function, you can do (funcall (slot-value obj ’slot-name) params) • In this way, you can use macros to generate code • And you can place that code inside of objects for encapsulation purposes – encapsulation here is not in the sense of ADTs but instead in the sense that an object, which is a problem solving agent, has code available internally

One Last Comment • Because of Common Lisp’s dynamic nature, CLOS has the ability

One Last Comment • Because of Common Lisp’s dynamic nature, CLOS has the ability to dynamically redefine classes and instances – if you redefine (or define for the first time) accessors (including readers and writers) then you can use them on an instance that was already created previously – if you redefine or add an initform or initarg, it has no affect on the previously defined instances – newly added slots are added to the instance – deleted slots are removed from the instance (defclass foo ( ) ((a : initform '1) (b : initarg : b))) (defclass foo ( ) ((a : initform '3 : accessor get-a) b (c : initform '4))) (setf f 1 (make-instance 'foo : b 2)) (describe f 1) #<FOO 2069 C 6 B 4> is a FOO A 1 B 2 C 4 (get-a f 1) returns 1