CLOS


1. Running Example (Also Gates.lisp handout)

2. Defining Classes

           (defclass Foo ()
             ((Slot-1 :initform 3)
              (Slot-2 :initform '(A B)))

3. Defining Methods

           (defmethod Square ((X number)) (* X X))
           (defmethod Square ((S string)) (format nil "~A~A" S S))
Note common error of omitting () above.

           (defmethod Area ((C Circle)) (* pi (Square (Radius C)))
           (defmethod Area ((R Rectangle)) (* (Width R) (Height R)))
           (defmethod Area ((Sq Square)) (Square (Width Sq)))
           OR (defmethod Height ((Sq Square)) (Width Sq))
           
Point: several can apply, but only most specific wins. Which one gets invoked ("dispatch") is decided at run-time, in contrast to many other languages with "overloading".

Idea of error-catching method: Arg = (Arg t)

           (defmethod Area (Arg) 
             (error "Area only defined for Circles, Rectangles, and Squares"))
           

4. Define-Class Macro. (Defined in ~hall/lisp/CLOS-Utilities.lisp or ...wfasl)

           (defmacro Define-Class (Class-Name Superclass-List &rest Slot-Descriptions)
             '(defclass ,Class-Name ,Superclass-List
               ,(mapcar #'Expand-Slot-Entry Slot-Descriptions)))
           
           (defun Expand-Slot-Description (Slot-Description)
             (let ((Name (first Slot-Description))
                   (Value (second Slot-Description)) )
               (list
                 Name
                 :accessor Name
                 :initarg (Add-Colon Name)
                 :initform value)))
           
           ;;; Like (read-from-string (format nil ":~A" Symbol))
           (defun Add-Colon (Symbol) (intern (symbol-name Symbol) :keyword))

5. IC/Gates Example (see ~hall/lisp/Gates.lisp)

6. Multi-Methods and Message Passing

           (Area Object-1) = (funcall (method-for 'Area Object-1) Object-1) 
           = (send Object-1 :Area) ; MRH -Flavors
           ;;; Two strings: just call CONCATENATE
           (defmethod Concat ((S1 string) (S2 string))(concatenate 'string S1 S2))
           
           ;;; Two lists: just call APPEND
           (defmethod Concat ((L1 list) (L2 list))(append L1 L2))
           
           ;;; A list and a string: convert the list into one big string, then call Concat,
           ;;; to let the string/string version handle it. Note that we use PRINC-TO-STRING
           ;;; instead of just STRING, since STRING fails on numbers.
           (defmethod Concat ((L1 list) (S1 string))
             (Concat (apply #'concatenate 'string (mapcar #'princ-to-string L1))
                     S1))
           
           ;;; A string and a list: just call Concat with the args in the opposite order, to
           ;;; let the above version handle it.
           (defmethod Concat ((S1 string) (L1 list))
             (Concat L1 S1))
           
           ;;;Error-catching method
           (defmethod Concat (X Y) (error "..."))
           
You cannot specialize on &optional , &rest or &key variables. I.e. same number of required args. Steele calls this "lambda list congruence".

7. Setf Methods

           (defmethod Color ((Obj Graphical-Object)) (slot-value Obj 'Color))
but :accessor Color also lets us do (setf (Color Obj) 'Red)

           (defmethod (setf Color) (New-Color (Obj Graphical-Object))
             (setf (slot-value Obj 'Color) New-Color))
           (defun Add-SOme (X) (+ X *Global-Var*))
           (defmethod (setf Add-Some) ((Result number) (X number))
             (setq *Global-Var* (- Result X)))
           (defmethod (setf Area) ((New-Area number) (C Circle))
             (setf (Radius C) (sqrt (/ New-Area pi))))

8. Before/After Methods

           (defmethod Attach :before ((G1 Gate) (G2 Gate)) (Draw-New-Picture G1 G2))
           
           (defmethod initialize-instance :after ((Obj Gate) &rest Extra-Args)
             (declare (ignore Extra-Args))
             (push Obj *Active-Gates*))
           
           ;;; Updating area each time instead of calculating it as before
           (defmethod (setf Radius) :after ((New-Radius number) (C Circle))
             (setf (Area C) (* pi (Square New-Radius))))
           
           (defmethod Area :before ((R Rectangle))
             (unless
               (slot-boundp R 'Height)
               (setf (Height R) (Width R))))

9. Additional optional topics

           (defmethod Fact ((N (eql 0))) 1)
           (defmethod Fact ((N integer)) (* N (Fact (- N 1))))
           
Methods are slower, but perhaps it is doing the same tests you are anyhow.

Around methods, user-definable method combination, slot-names, :allocation :class, etc.

Table of contents

1. - Running Example (Also Gates.lisp handout)
2. - Defining Classes
3. - Defining Methods
4. - Define-Class Macro. (Defined in ~hall/lisp/CLOS-Utilities.lisp or ...wfasl)
5. - IC/Gates Example (see ~hall/lisp/Gates.lisp)
6. - Multi-Methods and Message Passing
7. - Setf Methods
8. - Before/After Methods
9. - Additional optional topics