;; -*- gauche -*- ;; Experiment with and overview of Gauche's CLOS/MOP features ;; references: ;; - $gauche/{test/object.scm,lib/gauche/{object.scm,mop}} ;; - CL CLOS docs ;; - guile GOOPS docs (define-class () ((aap :init-keyword :aap ; constructor keyword :init-value 0 ; simple value :init-form (lambda () #t) ; form :getter get-aap ; read :setter set-aap ; write :accessor aap-accessor ; read/write :allocation :instance))) ; instance, class, ; each-subclass, virtual (let ((sf (make :aap 'aap))) (print (get-aap sf))) ;; books (define-class () ((title :accessor title) (author :accessor author))) (define-method initialize ((self ) init-args) ;; explicitly call base class (next-method) (print "literature.initialize")) (let ((book (make ))) ;; accessor and slot-ref/set! access to slots (set! (title book) "The Hitchhiker's Guide to the Galaxy") (set! (author book) "Douglas N. Adams") (print (list (author book) (slot-ref book 'author))) (print book)) ;; shapes (define-class () ((x :accessor x) (y :accessor y))) (define-class () ((side :init-keyword :side) (area :getter area))) ;; convenience constructor (define (make-square side) (make :side side)) ;; generic method (define-method area ((self )) (* (slot-ref self 'side) (slot-ref self 'side))) ;; call-back used by display (print, format, &c) (define-method write-object ((self ) port) (format port "#" (slot-ref self 'side))) ;; unspecialized method, applicable to any type (define-method unspec (self) (print (class-of self))) (let ((sq (make :side 3))) (print sq) (unspec sq) (unspec (make )) (print (area sq))) ;; GoF pattern ;; observer (define-class () ((id :init-keyword :id :getter id-of))) (define-method update ((self ) data) (print "observer " (id-of self) ": new data " data)) (define-method write-object ((self ) port) (format port "#" (id-of self))) ;; subject (define-class () ((observers :init-value '() :accessor observers-of))) (define-method attach ((self ) (obj )) (set! (observers-of self) (cons obj (observers-of self)))) (define-method notify ((self ) data) (for-each (cut update <> data) (observers-of self))) (let ((sub (make )) (ob1 (make :id 'ob1)) (ob2 (make :id 'ob2))) (attach sub ob1) (notify sub 'aap) (attach sub ob2) (print (observers-of sub)) (notify sub 'noot)) ;; calling order: Class Precedence List ;; (see successful-lisp, chp 14) (define-class c1 () ()) (define-class c2 () ()) (define-class c3 (c1) ()) (define-class c4 (c2) ()) (define-class c5 (c3 c2) ()) (define-class c6 (c5 c1) ()) (define-class c7 (c4 c3) ()) (print (map class-name (class-precedence-list c5))) ; c5 c3 c1 c2 (print (map class-name (class-precedence-list c6))) ; c6 c5 c3 c1 c2 (print (map class-name (class-precedence-list c7))) ; c7 c4 c2 c3 c1 ;; mix-ins (define-class () ()) (define-method <=> ((self ) other) (error "abstract <=> in ")) (define-class () ((value :init-keyword :value))) (define-method <=> ((self ) other) (cond ((< (slot-ref self 'value) (slot-ref other 'value)) -1) ((> (slot-ref self 'value) (slot-ref other 'value)) +1) (else 0))) (let ((n1 (make :value 1)) (n2 (make :value 2)) (n3 (make :value 3))) (print (<=> n1 n2)) (print (<=> n3 n2)) (print (<=> n1 n1))) ;; catching unbound-slot (define-class () ((name :accessor name :init-keyword :name) (age :accessor age :init-keyword :age))) (define-method slot-unbound ((class ) obj slot) (format #t "slot '~s:~s' unbound, enter a value:\n" obj slot) (set! (age obj) (read)) (age obj)) (let ((p (make :name "Pascal"))) (print "age: " (age p))) ;; comparable using a metaclass (other methods are unchanged) (define-class () ()) (define-class () ((value :init-keyword :value)) :metaclass ) ;; differences between class+subclass and metaclass ;; subclass: ;; => #> ;; (class-precedence-list ) => ;; (#> #> #> #>) ;; metaclass: ;; => #< 0x8111240> instance of comparable! ;; (class-precedence-list ) => ;; (#< 0x8111240> #> #>) ;; metaclass = class-of-classes. Gauche uses this to implement ;; singletons, validators, propagators, &c. See gauche.mop.* module. (define-class () ((doc :init-keyword :doc :initform #f))) (define-class () (a b c) :metaclass :doc "new :doc keyword for class definitions") (print (slot-ref 'doc))