;; This file contains excerpts from the textbook Concrete ;; Abstractions: An Introduction to Computer Science Using Scheme, by ;; Max Hailperin, Barbara Kaiser, and Karl Knight, Copyright (c) 1998 ;; by Brooks/Cole Publishing Company. This file may not be reproduced ;; or redistributed other than for use with that textbook. ;; Chapter 14: Object-Oriented Programming ;; 14.4 Implementing an Object-oriented Programming System (define-class 'widget object-class '(size) '(activate)) (define widget-class (make-class 'widget object-class '(size) '(activate))) (define widget? (class/predicate widget-class)) (define make-widget (class/instantiator widget-class)) (define widget/get-size (class/getter widget-class 'size)) (define widget/set-size! (class/setter widget-class 'size)) (define widget/get-class (class/getter widget-class 'class)) (define widget/set-class! (class/setter widget-class 'class)) (define widget/activate (class/method widget-class 'activate)) (define widget^activate (class/non-overridable-method widget-class 'activate)) (define widget/init (class/method widget-class 'init)) (define widget^init (class/non-overridable-method widget-class 'init)) (define widget/describe (class/method widget-class 'describe)) (define widget^describe (class/non-overridable-method widget-class 'describe)) (define widget/get-size (lambda (object) (vector-ref object 1))) (define widget/set-size! (lambda (object value) (vector-set! object 1 value))) (define widget/get-size (class/getter widget-class 'size)) (class/set-method! class-class 'getter (lambda (this instvar-name) (let ((index (class/ivar-position this instvar-name))) (lambda (object) (vector-ref object index))))) (define widget/get-size (lambda (object) (if (widget? object) (vector-ref object 1) (error "Getter applied to object not of correct class:" 'size 'widget)))) (class/set-method! class-class 'getter (lambda (this instvar-name) (let ((index (class/ivar-position this instvar-name)) (ok? (class/predicate this))) (lambda (object) (if (ok? object) (vector-ref object index) (error "Getter applied to object not of correct class:" instvar-name (class/get-name this))))))) (class/set-method! class-class 'setter (lambda (this instvar-name) (let ((index (class/ivar-position this instvar-name)) (ok? (class/predicate this))) (lambda (object value) (if (ok? object) (begin (vector-set! object index value) 'set-done) (error "Setter applied to object not of correct class:" instvar-name (class/get-name this))))))) (define make-widget (lambda () (make-vector 2))) (define make-widget (lambda () (let ((instance (make-vector 2))) (object/set-class! instance widget-class) (object/init instance) instance))) (define make-widget (lambda () (let ((instance (make-vector 2))) (unchecked-object/set-class! instance widget-class) (object/init instance) instance))) (define make-widget (lambda init-args (let ((instance (make-vector 2))) (unchecked-object/set-class! instance widget-class) (apply object/init (cons instance init-args)) instance))) (class/set-method! class-class 'instantiator (lambda (this) (let ((num-ivars (class/get-num-ivars this))) (lambda init-args (let ((instance (make-vector num-ivars))) (unchecked-object/set-class! instance this) (apply object/init (cons instance init-args)) instance))))) (define widget/activate (lambda (object) (let ((method (vector-ref (class/get-method-vector (object/get-class object)) 2))) (method object)))) (define widget^activate (let ((method-vector (class/get-method-vector widget-class))) (lambda (object) (let ((method (vector-ref method-vector 2))) (method object))))) (define widget/activate (lambda (object . args) (let ((method (vector-ref (class/get-method-vector (object/get-class object)) 2))) (apply method (cons object args))))) (define widget/activate (let ((index (class/method-position widget-class 'activate))) (lambda (object . args) (let ((method (vector-ref (class/get-method-vector (object/get-class object)) index))) (apply method (cons object args)))))) (class/set-method! class-class 'set-method! (lambda (this method-name method) (vector-set! (class/get-method-vector this) (class/method-position this method-name) method))) (class/set-method! class-class 'set-method! (lambda (this method-name method) (let ((index (class/method-position this method-name))) (vector-set! (class/get-method-vector this) index method) (vector-set! (class/get-method-set?-vector this) index #t) (apply-below this (lambda (class) (vector-set! (class/get-method-vector class) index method)) (lambda (class) (not (vector-ref (class/get-method-set?-vector class) index))))) method-name)) (define apply-below (lambda (class proc apply-to?) (for-each (lambda (subclass) (if (apply-to? subclass) (begin (proc subclass) (apply-below subclass proc apply-to?)))) (class/get-subclasses class)))) (define widget? (lambda (object) (let ((ancestry (class/get-ancestry (object/get-class object)))) (and (> (vector-length ancestry) 1) (eq? (vector-ref ancestry 1) widget-class))))) (define widget? (let ((level (- (vector-length (class/get-ancestry widget-class)) 1)) (min-length (class/get-num-ivars widget-class)) (min-class-length (class/get-num-ivars class-class))) (lambda (object) (and (vector? object) (>= (vector-length object) min-length) (let ((class (object/get-class object))) (and (vector? class) (>= (vector-length class) min-class-length) (let ((a (class/get-ancestry class)) (size (class/get-num-ivars class))) (and (number? size) (= size (vector-length object)) (vector? a) (eq? (vector-ref a (- (vector-length a) 1)) class) (> (vector-length a) level) (eq? (vector-ref a level) widget-class))))))))) (define class-class (make-class 'class ; name object-class ; superclass '(name ; instance variables subclasses num-ivars ivar-alist num-methods method-alist method-vector method-set?-vector ancestry) '(instantiator ; methods predicate getter setter method non-overridable-method set-method! ivar-position method-position))) (class/set-method! class-class 'init (lambda (this class-name superclass instvar-names method-names) (object^init this) ;; some code should go here to check that none of the new ;; instvar-names or method-names are already in use in the ;; superclass -- and if any are, to signal an error (class/set-name! this class-name) (class/set-subclasses! this '()) (class/set-subclasses! superclass (cons this (class/get-subclasses superclass))) (class/set-num-ivars! this (+ (class/get-num-ivars superclass) (length instvar-names))) (class/set-ivar-alist! this ;; some code needs to go here to ;; assign the positions for the instance ;; variables ) (class/set-method-alist! this ;; some code needs to go here to ;; assign the positions for the methods ) (let ((num-methods (+ (class/get-num-methods superclass) (length method-names)))) (class/set-num-methods! this num-methods) (let ((method-vector (make-vector num-methods))) (class/set-method-vector! this method-vector) (vector-copy! (class/get-method-vector superclass) method-vector) (for-each (lambda (method-name) (vector-set! method-vector (class/method-position this method-name) (lambda (object . args) (error "Unimplemented method" method-name)))) method-names)) (let ((method-set?-vector (make-vector num-methods))) (class/set-method-set?-vector! this method-set?-vector) (vector-fill! method-set?-vector #f))) (let ((ancestry (make-vector (+ (vector-length (class/get-ancestry superclass)) 1)))) (class/set-ancestry! this ancestry) (vector-copy! (class/get-ancestry superclass) ancestry) (vector-set! ancestry (- (vector-length ancestry) 1) this)))) (define alist-from-onto (lambda (names num alist) (if (null? names) alist (alist-from-onto (cdr names) (+ num 1) (cons (list (car names) num) alist))))) (alist-from-onto instvar-names (class/get-num-ivars superclass) (class/get-ivar-alist superclass)) (alist-from-onto method-names (class/get-num-methods superclass) (class/get-method-alist superclass)) (assq 'size '((class 0) (size 1))) ;Value: (size 1) (assq 'color '((class 0) (size 1))) ;Value: #f (class/set-method! class-class 'ivar-position (lambda (this ivar-name) (let ((lookup (assq ivar-name (class/get-ivar-alist this)))) (if lookup (cadr lookup) (error "instance variable name not present in class" ivar-name (class/get-name this)))))) (class/set-method! class-class 'method-position (lambda (this method-name) (let ((lookup (assq method-name (class/get-method-alist this)))) (if lookup (cadr lookup) (error "method name not present in class" method-name (class/get-name this)))))) (class/set-method! object-class 'init (lambda (this) 'done)) (define class-class (vector 'class-class-goes-here 'class ; name '() ; subclasses 10 ; num-ivars '((class 0) ; ivar-alist (These position numbers must (name 1) ; be matched by the actual positioning (subclasses 2) ; of the items in this class-class vector (num-ivars 3) ; as well as the ones in the object-class (ivar-alist 4) ; vector below.) (num-methods 5) (method-alist 6) (method-vector 7) (method-set?-vector 8) (ancestry 9)) 11 ; num-methods '((init 0) ; method-alist (describe 1) (instantiator 2) (predicate 3) (getter 4) (setter 5) (method 6) (non-overridable-method 7) (set-method! 8) (ivar-position 9) (method-position 10)) (make-vector 11) ; method-vector (make-vector 11) ; method-set?-vector (make-vector 2))) ; ancestry (unchecked-object/set-class! class-class class-class) (define object-class (vector class-class ; class 'object ; name (list class-class) ; subclasses 1 ; num-ivars '((class 0)) ; ivar-alist 2 ; num-methods '((init 0) ; method-alist (describe 1)) (make-vector 2) ; method-vector (make-vector 2) ; method-set?-vector (make-vector 1))) ; ancestry (define class/get-ancestry (lambda (obj) (vector-ref obj 9))) (vector-fill! (class/get-method-set?-vector object-class) #f) (vector-fill! (class/get-method-set?-vector class-class) #f) (vector-set! (class/get-ancestry object-class) 0 object-class) (let ((a (class/get-ancestry class-class))) (vector-set! a 0 object-class) (vector-set! a 1 class-class)) (define class/method-position ; temporary real, later replaced (lambda (this method-name) ; with virtual (let ((lookup (assq method-name (class/get-method-alist this)))) (if lookup (cadr lookup) (error "method name not present in class" method-name (class/get-name this)))))) (define class/set-method! ; temporary real, later replaced (lambda (this method-name method) ; with virtual (let ((index (class/method-position this method-name))) (vector-set! (class/get-method-vector this) index method) (vector-set! (class/get-method-set?-vector this) index #t) (apply-below this (lambda (class) (vector-set! (class/get-method-vector class) index method)) (lambda (class) (not (vector-ref (class/get-method-set?-vector class) index))))) method-name)) (class/set-method! class-class 'method-position class/method-position) (class/set-method! class-class 'set-method! class/set-method!) ; similarly for other methods, including class/method (define class/method-position (class/method class-class 'method-position)) (define class/set-method! (class/method class-class 'set-method!)) ; and so forth (define define-class (lambda (class-name superclass instvar-names method-names) (eval-globally (class-definitions class-name superclass instvar-names method-names)))) (symbol-append 'make- 'widget) ;Value: make-widget (symbol-append 'widget '/ 'activate) ;Value: widget/activate (define symbol-append (lambda symbols (string->symbol (apply string-append (map symbol->string symbols))))) (define class-predicate-definition (lambda (class-name) (list 'define (symbol-append class-name '?) (list 'class/predicate (symbol-append class-name '-class))))) (class-predicate-definition 'widget) ;Value: (define widget? (class/predicate widget-class)) ;Value: (begin ; (define widget-class ...) ; (define make-widget ...) ; (define widget? ...) ; ...)