362 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Scheme
		
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Scheme
		
	
	
	
; Mode: Scheme
 | 
						|
;
 | 
						|
;
 | 
						|
; **********************************************************************
 | 
						|
; Copyright (c) 1992 Xerox Corporation.  
 | 
						|
; All Rights Reserved.  
 | 
						|
;
 | 
						|
; Use, reproduction, and preparation of derivative works are permitted.
 | 
						|
; Any copy of this software or of any derivative work must include the
 | 
						|
; above copyright notice of Xerox Corporation, this paragraph and the
 | 
						|
; one after it.  Any distribution of this software or derivative works
 | 
						|
; must comply with all applicable United States export control laws.
 | 
						|
;
 | 
						|
; This software is made available AS IS, and XEROX CORPORATION DISCLAIMS
 | 
						|
; ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 | 
						|
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
						|
; PURPOSE, AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY
 | 
						|
; LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS
 | 
						|
; EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING
 | 
						|
; NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED
 | 
						|
; OF THE POSSIBILITY OF SUCH DAMAGES.
 | 
						|
; **********************************************************************
 | 
						|
;
 | 
						|
; Some simple examples of using Tiny CLOS and its MOP.
 | 
						|
; 
 | 
						|
; Much of this stuff corresponds to stuff in AMOP (The Art of the
 | 
						|
; Metaobject Protocol).
 | 
						|
;
 | 
						|
 | 
						|
;***
 | 
						|
;
 | 
						|
; This is a useful sort of helper function.  Note how it uses the
 | 
						|
; introspective part of the MOP.  The first few pages of chapter
 | 
						|
; two of the AMOP discuss this.
 | 
						|
;
 | 
						|
; Note that this introspective MOP doesn't support back-links from
 | 
						|
; the classes to methods and generic functions.  Is that worth adding?
 | 
						|
;
 | 
						|
;
 | 
						|
(define initialize-slots
 | 
						|
    (lambda (object initargs)
 | 
						|
      (let ((not-there (list 'shes-not-there)))
 | 
						|
	(for-each (lambda (slot)
 | 
						|
		    (let ((name (car slot)))
 | 
						|
		      (let ((value  (getl initargs name not-there)))
 | 
						|
			(if (eq? value not-there)
 | 
						|
			    'do-nothing
 | 
						|
			    (slot-set! object name value)))))
 | 
						|
		  (class-slots (class-of object))))))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;***
 | 
						|
;
 | 
						|
; A simple class, just an instance of <class>.  Note that we are using
 | 
						|
; make and <class> rather than make-class to make it.  See Section 2.4
 | 
						|
; of AMOP for more on this.
 | 
						|
;
 | 
						|
;
 | 
						|
 | 
						|
(define <pos> (make <class>                          ;[make-class 
 | 
						|
		    'direct-supers (list <object>)   ;  (list <object>) 
 | 
						|
		    'direct-slots  (list 'x 'y)))    ;  (list 'x 'y)]
 | 
						|
 | 
						|
(add-method initialize
 | 
						|
    (make-method (list <pos>)
 | 
						|
      (lambda (call-next-method pos initargs)
 | 
						|
	(call-next-method)
 | 
						|
	(initialize-slots pos initargs))))
 | 
						|
 | 
						|
(define p1 (make <pos> 'x 1 'y 2))
 | 
						|
(define p2 (make <pos> 'x 3 'y 5))
 | 
						|
 | 
						|
 | 
						|
;***
 | 
						|
;
 | 
						|
; Another way of writing that class definition, that achives better
 | 
						|
; `encapsulation' by using slot names that are unique keys, rather
 | 
						|
; than symbols.
 | 
						|
;
 | 
						|
;
 | 
						|
 | 
						|
(define <pos>)
 | 
						|
(define pos-x (make-generic))
 | 
						|
(define pos-y (make-generic))
 | 
						|
(define move  (make-generic))
 | 
						|
 | 
						|
(let ((x (vector 'x))
 | 
						|
      (y (vector 'y)))
 | 
						|
  
 | 
						|
  (set! <pos> (make <class>
 | 
						|
		    'direct-supers (list <object>)
 | 
						|
		    'direct-slots  (list x y)))
 | 
						|
 | 
						|
  (add-method pos-x
 | 
						|
      (make-method (list <pos>)
 | 
						|
	(lambda (call-next-method pos) (slot-ref pos x))))
 | 
						|
  (add-method pos-y
 | 
						|
      (make-method (list <pos>)
 | 
						|
	(lambda (call-next-method pos) (slot-ref pos y))))
 | 
						|
 | 
						|
  (add-method move
 | 
						|
      (make-method (list <pos>)
 | 
						|
        (lambda (call-next-method pos new-x new-y)
 | 
						|
	  (slot-set! pos x new-x)
 | 
						|
	  (slot-set! pos y new-y))))
 | 
						|
 | 
						|
  (add-method initialize
 | 
						|
      (make-method (list <pos>)
 | 
						|
	(lambda (call-next-method pos initargs)
 | 
						|
	  (move pos (getl initargs 'x 0) (getl initargs 'y 0)))))
 | 
						|
  )
 | 
						|
 | 
						|
 | 
						|
(define p3 (make <pos> 'x 1 'y 2))
 | 
						|
(define p4 (make <pos> 'x 3 'y 5))
 | 
						|
 | 
						|
 | 
						|
;***
 | 
						|
;
 | 
						|
; Class allocated slots.
 | 
						|
;
 | 
						|
; In Scheme, this extension isn't worth a whole lot, but what the hell.
 | 
						|
;
 | 
						|
;
 | 
						|
 | 
						|
(define <class-slots-class>
 | 
						|
    (make-class (list <class>)
 | 
						|
		(list)))
 | 
						|
 | 
						|
(add-method compute-getter-and-setter
 | 
						|
    (make-method (list <class-slots-class>)
 | 
						|
      (lambda (call-next-method class slot allocator)
 | 
						|
	(if (null? (memq ':class-allocation slot))
 | 
						|
	    (call-next-method)
 | 
						|
	    (let ((cell '()))
 | 
						|
	      (list (lambda (o) cell)
 | 
						|
		    (lambda (o new) (set! cell new) new)))))))
 | 
						|
 | 
						|
 | 
						|
;
 | 
						|
; Here's a silly program that uses class allocated slots.
 | 
						|
;
 | 
						|
;
 | 
						|
(define <ship>
 | 
						|
    (make <class-slots-class>
 | 
						|
	  'direct-supers (list <object>)
 | 
						|
	  'direct-slots  (list 'name
 | 
						|
			       '(all-ships :class-allocation))))
 | 
						|
 | 
						|
(add-method initialize
 | 
						|
    (make-method (list <ship>)
 | 
						|
      (lambda (call-next-method ship initargs)
 | 
						|
	(call-next-method)
 | 
						|
	(initialize-slots ship initargs)
 | 
						|
	(slot-set! ship
 | 
						|
		   'all-ships
 | 
						|
		   (cons ship (slot-ref ship 'all-ships))))))
 | 
						|
 | 
						|
(define siblings (make-generic))
 | 
						|
(add-method siblings
 | 
						|
    (make-method (list <ship>)
 | 
						|
      (lambda (call-next-method ship)
 | 
						|
	(remove ship (slot-ref ship 'all-ships)))))
 | 
						|
 | 
						|
(define s1 (make <ship> 'name 's1))
 | 
						|
(define s2 (make <ship> 'name 's2))
 | 
						|
(define s3 (make <ship> 'name 's3))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;***
 | 
						|
;
 | 
						|
; Here's a class of class that allocates some slots dynamically.
 | 
						|
;
 | 
						|
; It has a layered protocol (dynamic-slot?) that decides whether a given
 | 
						|
; slot should be dynamically allocated.  This makes it easy to define a
 | 
						|
; subclass that allocates all its slots dynamically.
 | 
						|
;
 | 
						|
;
 | 
						|
(define <dynamic-class>
 | 
						|
    (make-class (list <class>)
 | 
						|
		(list 'alist-g-n-s)))
 | 
						|
 | 
						|
 | 
						|
(define dynamic-slot? (make-generic))
 | 
						|
 | 
						|
(add-method dynamic-slot?
 | 
						|
    (make-method (list <dynamic-class>)
 | 
						|
      (lambda (call-next-method class slot)
 | 
						|
	(memq :dynamic-allocation (cdr slot)))))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
(define alist-getter-and-setter
 | 
						|
    (lambda (dynamic-class allocator)
 | 
						|
      (let ((old (slot-ref dynamic-class 'alist-g-n-s)))
 | 
						|
	(if (null? old)
 | 
						|
	    (let ((new (allocator (lambda () '()))))
 | 
						|
	      (slot-set! dynamic-class 'alist-g-n-s new)
 | 
						|
	      new)
 | 
						|
	    old))))
 | 
						|
 | 
						|
 | 
						|
(add-method compute-getter-and-setter
 | 
						|
    (make-method (list <dynamic-class>)
 | 
						|
      (lambda (call-next-method class slot allocator)
 | 
						|
	(if (null? (dynamic-slot? class slot))
 | 
						|
	    (call-next-method)
 | 
						|
	    (let* ((name (car slot))
 | 
						|
		   (g-n-s (alist-getter-and-setter class allocator))
 | 
						|
		   (alist-getter (car g-n-s))
 | 
						|
		   (alist-setter (cadr g-n-s)))
 | 
						|
	      (list (lambda (o)
 | 
						|
		      (let ((entry (assq name  (alist-getter o))))
 | 
						|
			(if (null? entry)
 | 
						|
			    '()
 | 
						|
			    (cdr entry))))
 | 
						|
		    (lambda (o new)
 | 
						|
		      (let* ((alist (alist-getter o))
 | 
						|
			     (entry (assq name alist)))
 | 
						|
			(if (null? entry)
 | 
						|
			    (alist-setter o
 | 
						|
			                  (cons (cons name new) alist))
 | 
						|
			    (set-cdr! entry new))
 | 
						|
			new))))))))
 | 
						|
 | 
						|
 | 
						|
(define <all-dynamic-class>
 | 
						|
    (make-class (list <dynamic-class>)
 | 
						|
		(list)))
 | 
						|
 | 
						|
(add-method dynamic-slot?
 | 
						|
    (make-method (list <all-dynamic-class>)
 | 
						|
      (lambda (call-next-method class slot) #t)))
 | 
						|
	    
 | 
						|
 | 
						|
 | 
						|
;
 | 
						|
; A silly program that uses this.
 | 
						|
;
 | 
						|
;
 | 
						|
(define <person> (make <all-dynamic-class>
 | 
						|
		       'direct-supers (list <object>)
 | 
						|
		       'direct-slots  (list 'name 'age 'address)))
 | 
						|
 | 
						|
(add-method initialize
 | 
						|
    (make-method (list <person>)
 | 
						|
      (lambda (call-next-method person initargs)
 | 
						|
	(initialize-slots person initargs))))
 | 
						|
 | 
						|
 | 
						|
(define person1 (make <person> 'name 'sally))
 | 
						|
(define person2 (make <person> 'name 'betty))
 | 
						|
(define person3 (make <person> 'name 'sue))
 | 
						|
 | 
						|
 | 
						|
;***
 | 
						|
;
 | 
						|
; A ``database'' class that stores slots externally.
 | 
						|
;
 | 
						|
;
 | 
						|
 | 
						|
(define <db-class>
 | 
						|
  (make-class (list <class>)
 | 
						|
	      (list 'id-g-n-s)))
 | 
						|
 | 
						|
(define id-getter-and-setter
 | 
						|
    (lambda (db-class allocator)
 | 
						|
      (let ((old (slot-ref db-class 'id-g-n-s)))
 | 
						|
	(if (null? old)
 | 
						|
	    (let ((new (allocator db-allocate-id)))
 | 
						|
	      (slot-set! class 'id-g-n-s new)
 | 
						|
	      new)
 | 
						|
	    old))))
 | 
						|
 | 
						|
(add-method compute-getter-and-setter
 | 
						|
    (make-method (list <db-class>)
 | 
						|
      (lambda (call-next-method class slot allocator)
 | 
						|
	(let* ((id-g-n-s (id-getter-and-setter class allocator))
 | 
						|
	       (id-getter (car id-g-n-s))
 | 
						|
	       (id-setter (cadr id-g-n-s))
 | 
						|
	       (slot-name (car slot)))
 | 
						|
	  (list (lambda (o)
 | 
						|
		  (db-lookup (id-getter o) slot-name)) 
 | 
						|
		(lambda (o new)
 | 
						|
		  (db-store  (id-getter o) slot-name new)))))))
 | 
						|
 | 
						|
 | 
						|
;***
 | 
						|
;
 | 
						|
; A kind of generic that supports around methods.
 | 
						|
;
 | 
						|
;
 | 
						|
(define make-around-generic
 | 
						|
    (lambda () (make <around-generic>)))
 | 
						|
 | 
						|
(define make-around-method
 | 
						|
    (lambda (specializers procedure)
 | 
						|
      (make <around-method>
 | 
						|
	    'specializers specializers
 | 
						|
	    'procedure procedure)))
 | 
						|
 | 
						|
 | 
						|
(define <around-generic> (make <entity-class>
 | 
						|
			       'direct-supers (list <generic>)))
 | 
						|
(define <around-method>  (make <class>
 | 
						|
			       'direct-supers (list <method>)))
 | 
						|
 | 
						|
 | 
						|
(define around-method?   (make-generic))
 | 
						|
 | 
						|
(add-method around-method?
 | 
						|
    (make-method (list <method>)
 | 
						|
      (lambda (call-next-method x) #f)))
 | 
						|
(add-method around-method?
 | 
						|
    (make-method (list <around-method>)
 | 
						|
      (lambda (call-next-method x) #t)))
 | 
						|
 | 
						|
 | 
						|
(add-method compute-methods
 | 
						|
    (make-method (list <around-generic>)
 | 
						|
      (lambda (call-next-method generic)
 | 
						|
	(let ((normal-compute-methods (call-next-method)))
 | 
						|
	  (lambda (args)
 | 
						|
	    (let ((normal-methods (normal-compute-methods args)))
 | 
						|
	      (append
 | 
						|
	        (filter-in around-method?
 | 
						|
			   normal-methods)
 | 
						|
		(filter-in (lambda (m) (not (around-method? m)))
 | 
						|
			   normal-methods))))))))
 | 
						|
 | 
						|
 | 
						|
;
 | 
						|
; And a simple example of using it.
 | 
						|
;
 | 
						|
;
 | 
						|
(define <baz> (make-class (list <object>) (list)))
 | 
						|
(define <bar> (make-class (list <baz>)    (list)))
 | 
						|
(define <foo> (make-class (list <bar>)    (list)))
 | 
						|
 | 
						|
 | 
						|
(define test-around
 | 
						|
    (lambda (generic)
 | 
						|
      (add-method generic
 | 
						|
	  (make-method        (list <foo>)
 | 
						|
	                      (lambda (cnm x) (cons 'foo (cnm)))))
 | 
						|
 | 
						|
      (add-method generic
 | 
						|
	  (make-around-method (list <bar>)
 | 
						|
			      (lambda (cnm x) (cons 'bar (cnm)))))
 | 
						|
 | 
						|
      (add-method generic
 | 
						|
	  (make-method        (list <baz>)
 | 
						|
	                      (lambda (cnm x) '(baz))))
 | 
						|
 | 
						|
      (generic (make <foo>))))
 | 
						|
 | 
						|
 | 
						|
(equal? (test-around (make-generic))        '(foo bar baz))
 | 
						|
(equal? (test-around (make-around-generic)) '(bar foo baz))
 |