Ticket #32: tiny-examples.scm

File tiny-examples.scm, 10.2 KB (added by Tony Sidaway, 16 years ago)

diff for tiny-examplesscm as downloaded from Xeroc Parc website--hacks to make it work for modern MIT Scheme

Line 
1; Mode: Scheme
2;
3;
4; **********************************************************************
5; Copyright (c) 1992 Xerox Corporation.
6; All Rights Reserved.
7;
8; Use, reproduction, and preparation of derivative works are permitted.
9; Any copy of this software or of any derivative work must include the
10; above copyright notice of Xerox Corporation, this paragraph and the
11; one after it. Any distribution of this software or derivative works
12; must comply with all applicable United States export control laws.
13;
14; This software is made available AS IS, and XEROX CORPORATION DISCLAIMS
15; ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
16; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17; PURPOSE, AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY
18; LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS
19; EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING
20; NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED
21; OF THE POSSIBILITY OF SUCH DAMAGES.
22; **********************************************************************
23;
24; Some simple examples of using Tiny CLOS and its MOP.
25;
26; Much of this stuff corresponds to stuff in AMOP (The Art of the
27; Metaobject Protocol).
28;
29
30(define-syntax test-pp
31 (syntax-rules()
32 ((_ x) (begin (newline)(pp 'x)(pp x)))))
33
34;***
35;
36; This is a useful sort of helper function. Note how it uses the
37; introspective part of the MOP. The first few pages of chapter
38; two of the AMOP discuss this.
39;
40; Note that this introspective MOP doesn't support back-links from
41; the classes to methods and generic functions. Is that worth adding?
42;
43;
44(define initialize-slots
45 (lambda (object initargs)
46 (let ((not-there (list 'shes-not-there)))
47 (for-each (lambda (slot)
48 (let ((name (car slot)))
49 (let ((value (getl initargs name not-there)))
50 (if (eq? value not-there)
51 'do-nothing
52 (slot-set! object name value)))))
53 (class-slots (class-of object))))))
54
55
56
57;***
58;
59; A simple class, just an instance of <class>. Note that we are using
60; make and <class> rather than make-class to make it. See Section 2.4
61; of AMOP for more on this.
62;
63;
64
65(define <pos> (make <class> ;[make-class
66 'direct-supers (list <object>) ; (list <object>)
67 'direct-slots (list 'x 'y))) ; (list 'x 'y)]
68
69(add-method initialize
70 (make-method (list <pos>)
71 (lambda (call-next-method pos initargs)
72 (call-next-method)
73 (initialize-slots pos initargs))))
74
75(define p1 (make <pos> 'x 1 'y 2))
76(define p2 (make <pos> 'x 3 'y 5))
77
78(test-pp p1)
79(test-pp (slot-ref p1 'x))
80(test-pp (slot-ref p1 'y))
81
82
83(test-pp p2)
84(test-pp (slot-ref p2 'x))
85(test-pp (slot-ref p2 'y))
86
87
88;***
89;
90; Another way of writing that class definition, that achives better
91; `encapsulation' by using slot names that are unique keys, rather
92; than symbols.
93;
94;
95
96(define <pos>)
97(define pos-x (make-generic))
98(define pos-y (make-generic))
99(define move (make-generic))
100
101(let ((x (vector 'x))
102 (y (vector 'y)))
103
104 (set! <pos> (make <class>
105 'direct-supers (list <object>)
106 'direct-slots (list x y)))
107
108 (add-method pos-x
109 (make-method (list <pos>)
110 (lambda (call-next-method pos) (slot-ref pos x))))
111 (add-method pos-y
112 (make-method (list <pos>)
113 (lambda (call-next-method pos) (slot-ref pos y))))
114
115 (add-method move
116 (make-method (list <pos>)
117 (lambda (call-next-method pos new-x new-y)
118 (slot-set! pos x new-x)
119 (slot-set! pos y new-y))))
120
121 (add-method initialize
122 (make-method (list <pos>)
123 (lambda (call-next-method pos initargs)
124 (move pos (getl initargs 'x 0) (getl initargs 'y 0)))))
125 )
126
127
128(define p3 (make <pos> 'x 1 'y 2))
129(define p4 (make <pos> 'x 3 'y 5))
130
131
132(test-pp p3)
133(test-pp (pos-x p3))
134(test-pp (pos-y p3))
135
136(test-pp p4)
137(test-pp (pos-x p4))
138(test-pp (pos-y p4))
139
140(move p4 10 11)
141
142(test-pp p4)
143(test-pp (pos-x p4))
144(test-pp (pos-y p4))
145
146
147;***
148;
149; Class allocated slots.
150;
151; In Scheme, this extension isn't worth a whole lot, but what the hell.
152;
153;
154
155(define <class-slots-class>
156 (make-class (list <class>)
157 (list)))
158
159(add-method compute-getter-and-setter
160 (make-method (list <class-slots-class>)
161 (lambda (call-next-method class slot allocator)
162 (if (not (memq ':class-allocation slot))
163 (call-next-method)
164 (let ((cell '()))
165 (list (lambda (o) cell)
166 (lambda (o new) (set! cell new) new)))))))
167
168
169;
170; Here's a silly program that uses class allocated slots.
171;
172;
173(define <ship>
174 (make <class-slots-class>
175 'direct-supers (list <object>)
176 'direct-slots (list 'name
177 '(all-ships :class-allocation))))
178
179(add-method initialize
180 (make-method (list <ship>)
181 (lambda (call-next-method ship initargs)
182 (call-next-method)
183 (initialize-slots ship initargs)
184 (slot-set! ship
185 'all-ships
186 (cons ship (slot-ref ship 'all-ships))))))
187
188(define siblings (make-generic))
189(add-method siblings
190 (make-method (list <ship>)
191 (lambda (call-next-method ship)
192 (remove ship (slot-ref ship 'all-ships)))))
193
194(define s1 (make <ship> 'name 's1))
195(define s2 (make <ship> 'name 's2))
196(test-pp s2)
197(test-pp (slot-ref s2 'name))
198(define s3 (make <ship> 'name 's3))
199
200(test-pp s1)
201(test-pp (slot-ref s1 'name))
202
203(test-pp s2)
204(test-pp (slot-ref s2 'name))
205
206(test-pp s3)
207(test-pp (slot-ref s3 'name))
208
209(test-pp <ship>)
210(test-pp (class-direct-slots <ship>))
211
212(test-pp (map (lambda(x) (cons x (slot-ref x 'name))) (siblings s1)))
213
214
215;***
216;
217; Here's a class of class that allocates some slots dynamically.
218;
219; It has a layered protocol (dynamic-slot?) that decides whether a given
220; slot should be dynamically allocated. This makes it easy to define a
221; subclass that allocates all its slots dynamically.
222;
223;
224(define <dynamic-class>
225 (make-class (list <class>)
226 (list 'alist-g-n-s)))
227
228
229(define dynamic-slot? (make-generic))
230
231(add-method dynamic-slot?
232 (make-method (list <dynamic-class>)
233 (lambda (call-next-method class slot)
234 (memq :dynamic-allocation (cdr slot)))))
235
236
237
238(define alist-getter-and-setter
239 (lambda (dynamic-class allocator)
240 (let ((old (slot-ref dynamic-class 'alist-g-n-s)))
241 (if (null? old)
242 (let ((new (allocator (lambda () '()))))
243 (slot-set! dynamic-class 'alist-g-n-s new)
244 new)
245 old))))
246
247
248(add-method compute-getter-and-setter
249 (make-method (list <dynamic-class>)
250 (lambda (call-next-method class slot allocator)
251 (if (null? (dynamic-slot? class slot))
252 (call-next-method)
253 (let* ((name (car slot))
254 (g-n-s (alist-getter-and-setter class allocator))
255 (alist-getter (car g-n-s))
256 (alist-setter (cadr g-n-s)))
257 (list (lambda (o)
258 (let ((entry (assq name (alist-getter o))))
259 (if (not entry)
260 '()
261 (cdr entry))))
262 (lambda (o new)
263 (let* ((alist (alist-getter o))
264 (entry (assq name alist)))
265 (if (not entry)
266 (alist-setter o
267 (cons (cons name new) alist))
268 (set-cdr! entry new))
269 new))))))))
270
271
272(define <all-dynamic-class>
273 (make-class (list <dynamic-class>)
274 (list)))
275
276(add-method dynamic-slot?
277 (make-method (list <all-dynamic-class>)
278 (lambda (call-next-method class slot) #t)))
279
280
281
282;
283; A silly program that uses this.
284;
285;
286(define <person> (make <all-dynamic-class>
287 'direct-supers (list <object>)
288 'direct-slots (list 'name 'age 'address)))
289
290(add-method initialize
291 (make-method (list <person>)
292 (lambda (call-next-method person initargs)
293 (initialize-slots person initargs))))
294
295
296(define person1 (make <person> 'name 'sally))
297(define person2 (make <person> 'name 'betty))
298(define person3 (make <person> 'name 'sue))
299
300(test-pp (slot-ref person1 'name))
301
302(slot-set! person1 'age 20)
303(test-pp (slot-ref person1 'age))
304
305
306;***
307;
308; A ``database'' class that stores slots externally.
309;
310;
311
312(define <db-class>
313 (make-class (list <class>)
314 (list 'id-g-n-s)))
315
316(define id-getter-and-setter
317 (lambda (db-class allocator)
318 (let ((old (slot-ref db-class 'id-g-n-s)))
319 (if (null? old)
320 (let ((new (allocator db-allocate-id)))
321 (slot-set! class 'id-g-n-s new)
322 new)
323 old))))
324
325(add-method compute-getter-and-setter
326 (make-method (list <db-class>)
327 (lambda (call-next-method class slot allocator)
328 (let* ((id-g-n-s (id-getter-and-setter class allocator))
329 (id-getter (car id-g-n-s))
330 (id-setter (cadr id-g-n-s))
331 (slot-name (car slot)))
332 (list (lambda (o)
333 (db-lookup (id-getter o) slot-name))
334 (lambda (o new)
335 (db-store (id-getter o) slot-name new)))))))
336
337
338;***
339;
340; A kind of generic that supports around methods.
341;
342;
343(define make-around-generic
344 (lambda () (make <around-generic>)))
345
346(define make-around-method
347 (lambda (specializers procedure)
348 (make <around-method>
349 'specializers specializers
350 'procedure procedure)))
351
352
353(define <around-generic> (make <entity-class>
354 'direct-supers (list <generic>)))
355(define <around-method> (make <class>
356 'direct-supers (list <method>)))
357
358
359(define around-method? (make-generic))
360
361(add-method around-method?
362 (make-method (list <method>)
363 (lambda (call-next-method x) #f)))
364(add-method around-method?
365 (make-method (list <around-method>)
366 (lambda (call-next-method x) #t)))
367
368
369(add-method compute-methods
370 (make-method (list <around-generic>)
371 (lambda (call-next-method generic)
372 (let ((normal-compute-methods (call-next-method)))
373 (lambda (args)
374 (let ((normal-methods (normal-compute-methods args)))
375 (append
376 (filter-in around-method?
377 normal-methods)
378 (filter-in (lambda (m) (not (around-method? m)))
379 normal-methods))))))))
380
381
382;
383; And a simple example of using it.
384;
385;
386(define <baz> (make-class (list <object>) (list)))
387(define <bar> (make-class (list <baz>) (list)))
388(define <foo> (make-class (list <bar>) (list)))
389
390
391(define test-around
392 (lambda (generic)
393 (add-method generic
394 (make-method (list <foo>)
395 (lambda (cnm x) (cons 'foo (cnm)))))
396
397 (add-method generic
398 (make-around-method (list <bar>)
399 (lambda (cnm x) (cons 'bar (cnm)))))
400
401 (add-method generic
402 (make-method (list <baz>)
403 (lambda (cnm x) '(baz))))
404
405 (generic (make <foo>))))
406
407(test-pp (equal? (test-around (make-generic)) '(foo bar baz)))
408
409(test-pp (equal? (test-around (make-around-generic)) '(bar foo baz)))
410
411'examples_all_done