clos to start off with, clos (pronounced “kloss” or “see- loss”) is very different from...

36
CLOS To start off with, CLOS (pronounced “kloss” or “see-loss”) 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

Upload: theodore-parker

Post on 18-Dec-2015

213 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

CLOS• To start off with, CLOS (pronounced “kloss” or “see-

loss”) 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

Page 2: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

CLOS as an OOPL

Page 3: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 1980s, it was decided that CL needed something more powerful than structures and so CLOS was created and then added to the language

Page 4: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 5: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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)

Page 6: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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!

Page 7: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

Simple Example(defclass ball ( ) (color type size))

(defmethod getVolume ((b ball)) (* (/ 4 3) pi (cube (slot-value b 'size))))

(defmethod getSurfaceArea ((b ball)) (* 4 pi (square (slot-value b 'size))))

(defmethod getWeight ((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 (* (getSurfaceArea b))) (setf weight-internal (* (getVolume b) weight-internal)) (+ weight-external weight-internal)))

(setf b1 (make-instance 'ball))(setf (slot-value b1 'color) 'red)(setf (slot-value b1 'type) 'medicine-ball)(setf (slot-value b1 'size) 10)

(setf b2 (make-instance 'ball))(setf (slot-value b2 'color) 'orange)(setf (slot-value b2 'type) 'basketball)(setf (slot-value b2 'size) 15)

Page 8: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 ( )

Page 9: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 ball-

size)))– we can get b1’s type by doing (ball-type b1) – or set b2’s size by doing (setf (ball-size b2) 12)– but we can only access color by (slot-value b1 ’color)

– Notice the accessor function does not preclude someone from using slot-value as in (setf (slot-value b2 ’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!

Page 10: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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)))

Page 11: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

(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))))))

Notice how one parameter includes a type (ball) but not all parameters must have them

Page 12: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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) type)(setf (slot-value b 'size) size)(if z (progn

(setf (slot-value b 'x) x)(setf (slot-value b 'y) y)(setf (slot-value b 'z) z))

(progn(setf (slot-value b 'x) 'unknown)(setf (slot-value b 'y) 'unknown)(setf (slot-value b 'z) 'unknown))))

Notice that if z is unknown we changeall coordinates to be unknown

If the ball class would normally defaultx, y and z to 0, as we might hope, thenthis constructor method changes thosedefault values, probably inappropriately

Page 13: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

An Alternative Constructor

(defun construct-ball (type size &optional x y z) (let ((temp (make-instance 'ball)))

(setf (slot-value temp 'type) type)(setf (slot-value temp 'size) size)(if z (progn

(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))

• 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

Of the two approaches,neither is really similarto Java’s constructor

Page 14: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 twospecialized parameters,the method is no longerdefined for a singleclass

Page 15: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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)))

Page 16: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

Example Continued(defmethod bounce ((b ball) (p athlete) hardness)

(cond ((equal (ball-type b) (athlete-sport p)) (setf (ball-z b) (* (ball-z b) hardness)))(t (setf (ball-z b) (* (ball-z b) .5 hardness)))))

(defmethod bounce ((b ball) (p person) hardness)(setf (ball-z b) (* (ball-z b) .3 hardness)))

(defmethod bounce ((b brick) (p athlete) hardness)(setf (brick-z b) (* (brick-z b) .1 hardness)))

(defmethod bounce ((b brick) (p person) hardness)(setf (brick-z b) 0))

Here, how high something bounces is based not only on the type of thing (ballvs. brick) but also based on whether the person bouncing it is an average person or an athlete

Page 17: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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)))))

Page 18: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 (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

– 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)

> (find-class 'ball)#<STANDARD-CLASS BALL 2168C454>

Page 19: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 20: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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))

Page 21: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 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))

Page 22: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 23: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 24: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 25: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

classes, you just list all of the parents– (defclass multiplechild (parent1 parent2 parent3) ((;; 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

Page 26: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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”

Page 27: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

which were specialized?– Consider the following partial definitions for some method op2

• (defmethod op2 ((x number) (y number)) ...) ; method 1 • (defmethod op2 ((x integer) (y integer)) ...) ; method 2• (defmethod op2 ((x float) (y number)) ...) ; method 3• (defmethod op2 ((x number) (y float)) ...) ; method 4

– Which version is called for each of the following?• (OP2 11 23) – method 2 • (OP2 13 2.9) – method 4 • (OP2 8.3 4/5) – method 3 • (OP2 5/8 11/3) – method 1

– In a left-to-right manner, each parameter is determined by following the hierarchy, so (OP2 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)

Page 28: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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)– 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 structure

– C3 is a subclass of C1, C5 is a subclass of both C3 and C2, etc

• The CPL is consulted for each parameter from left to right until a match is found for all parameters

• Consider a method that accepts an instance of one of these classes, and a number

Imagine that we have a method defined with these specialized params:

•(C2 Integer)•(C2 Number)•(C3 Integer)•(C3 Number)Which method is invoked if we call it with a C6 and an integer? A C6 and a float?

Page 29: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 30: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 31: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

Example(defclass food ( ) ( ))

(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)

Page 32: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 (cooked apple))

(defmethod cook :around ((f food)) (print "Begin around food.")

(print "End around food."))

(cook pie-1)

"Begin around food" "End around food"

Here, the around method usescall-next-method so that thebefore, main and after methodsare also invoked

Without this however, noticewhat happens

Why should (cooked …)appear twice?

Page 33: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 defense-points, 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

Page 34: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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 pre-defined 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!

Page 35: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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

Page 36: CLOS To start off with, CLOS (pronounced “kloss” or “see- loss”) is very different from other object oriented programming languages –Multiple inheritance

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)))

(setf f1 (make-instance 'foo :b 2))

(describe f1)

#<FOO 2069C6B4> is a FOOA 1B 2

(defclass foo ( ) ((a :initform '3 :accessor get-a) b (c :initform '4)))

(describe f1)

#<FOO 2069C6B4> is a FOOA 1B 2C 4

(get-a f1) returns 1