1 | [[tags: egg]] |
---|
2 | |
---|
3 | == protobj |
---|
4 | |
---|
5 | [[toc:]] |
---|
6 | |
---|
7 | === Description |
---|
8 | |
---|
9 | Prototype-Delegation Object Model in Scheme |
---|
10 | |
---|
11 | === Author |
---|
12 | |
---|
13 | Neil van Dyke |
---|
14 | |
---|
15 | === Documentation |
---|
16 | |
---|
17 | To use the definitions described in this document import its bindings |
---|
18 | by evaluating the {{(import protobj)}} form in the current scope. |
---|
19 | |
---|
20 | Protobj is a Scheme library that implements a simple prototype-delegation |
---|
21 | object model, somewhat similar to that of [Self], and also related to |
---|
22 | [SLIB-Object] and [OScheme]. Protobj was written mainly as a |
---|
23 | {{syntax-rules}} learning exercise, but also because people ask about |
---|
24 | prototype object models for Scheme from time to time. Like most object |
---|
25 | systems, it should be regarded as an amusement. The Protobj library defines |
---|
26 | both a verbose set of procedures, and terse special syntax. |
---|
27 | |
---|
28 | Protobj is based on objects with named slots that can contain |
---|
29 | arbitrary values. Object have immediate slots, and single parent |
---|
30 | objects from which additional slots are inherited. When setting in a |
---|
31 | child object a slot inherited from the parent, a new immediate slot is |
---|
32 | created in the child so that the parent is unaffected and the slot is |
---|
33 | no longer inherited. |
---|
34 | |
---|
35 | Methods are simply closures stored in slots. When a method is |
---|
36 | applied, the first term of the closure is the receiver object. Unlike |
---|
37 | Self, getting the contents of the slot is distinguished from |
---|
38 | invoking a method contained in the slot. This distinction was made |
---|
39 | due to the way first-class closures are often used in Scheme. |
---|
40 | |
---|
41 | An object is cloned by invoking the {{clone}} method. The default |
---|
42 | root object's {{clone}} method creates a new child object without any |
---|
43 | immediate slots, rather than copying any slots. This behavior can be |
---|
44 | overridden to always copy certain slots, to copy immediate slots, or |
---|
45 | to copy all inherited slots. An overriding {{clone}} method can be |
---|
46 | implemented to apply its parent's {{clone}} method to itself and then |
---|
47 | set certain slots in the new child appropriately. |
---|
48 | |
---|
49 | The following is a quick tour of Protobj using the terse special |
---|
50 | syntax. |
---|
51 | |
---|
52 | Bind {{a}} to the new object that is created by cloning the default root |
---|
53 | object ({{%}} is special syntax for invoking the {{clone}} method): |
---|
54 | |
---|
55 | (define a (%)) |
---|
56 | |
---|
57 | Verify that {{a}} is an object and that {{a}}'s parent is the default |
---|
58 | root object: |
---|
59 | |
---|
60 | (object? a) => #t |
---|
61 | (eq? (^ a) (current-root-object)) => #t |
---|
62 | |
---|
63 | Add to {{a}} a slot named {{x}} with value {{1}}: |
---|
64 | |
---|
65 | (! a x 1) |
---|
66 | |
---|
67 | Get {{a}}'s slot {{x}}'s value: |
---|
68 | |
---|
69 | (? a x) => 1 |
---|
70 | |
---|
71 | Bind {{b}} to a clone of {{a}}: |
---|
72 | |
---|
73 | (define b (% a)) |
---|
74 | |
---|
75 | Get {{b}}'s slot {{x}}'s value, which is inherited from {{a}}: |
---|
76 | |
---|
77 | (? b x) => 1 |
---|
78 | |
---|
79 | Set {{a}}'s slot {{x}}'s value to {{42}}, and observe that {{b}} |
---|
80 | inherits the new value: |
---|
81 | |
---|
82 | (! a x 42) |
---|
83 | (? a x) => 42 |
---|
84 | (? b x) => 42 |
---|
85 | |
---|
86 | Set {{b}}'s slot {{x}}'s value to {{69}}, and observe that {{a}} |
---|
87 | retains its own {{x}} value although {{b}}'s {{x}} value has been |
---|
88 | changed: |
---|
89 | |
---|
90 | (! b x 69) |
---|
91 | (? a x) => 42 |
---|
92 | (? b x) => 69 |
---|
93 | |
---|
94 | Add to {{a}} an {{xplus}} slot containing a closure that implements a |
---|
95 | method of the object: |
---|
96 | |
---|
97 | (! a xplus (lambda (self n) (+ (? self x) n))) |
---|
98 | |
---|
99 | Apply the method to the {{a}} and {{b}} objects ({{b}} inherits |
---|
100 | any new slots added to {{a}}): |
---|
101 | |
---|
102 | (@ a xplus 7) => 49 |
---|
103 | (@ b xplus 7) => 76 |
---|
104 | |
---|
105 | Observe the shorthand syntax for applying methods to an object |
---|
106 | multiple times, with the syntax having the value of the lastmost |
---|
107 | application: |
---|
108 | |
---|
109 | (@ a (xplus 1000) (xplus 7)) => 49 |
---|
110 | |
---|
111 | Bind to {{c}} an object that clones {{a}} and adds slot {{y}} with |
---|
112 | value {{101}}: |
---|
113 | |
---|
114 | (define c (% a (y 101))) |
---|
115 | |
---|
116 | Get the values of both the {{x}} and {{y}} slots of {{c}}: |
---|
117 | |
---|
118 | (? c x y) => 42 101 |
---|
119 | |
---|
120 | Finally, bind {{d}} to a clone of {{a}} that overrides {{a}}'s {{x}} |
---|
121 | slot: |
---|
122 | |
---|
123 | (define d (% a (x 1) (y 2) (z 3))) |
---|
124 | (? d x y z) => 1 2 3 |
---|
125 | |
---|
126 | The basic interface of Protobj is a set of procedures. |
---|
127 | |
---|
128 | <procedure>(object? x)</procedure> |
---|
129 | |
---|
130 | Predicate for whether or not {{x}} is a Protobj object. |
---|
131 | |
---|
132 | <procedure>(object-parent obj)</procedure> |
---|
133 | |
---|
134 | Yields the parent object of object {{obj}}. |
---|
135 | |
---|
136 | <procedure>(object-set! obj slot-symbol val)</procedure> |
---|
137 | |
---|
138 | Sets the slot identified by symbol {{slot-symbol}} in object {{obj}} to |
---|
139 | value {{val}}. |
---|
140 | |
---|
141 | <procedure>(object-get obj slot-symbol)</procedure> |
---|
142 | |
---|
143 | Yields the value of slot named by symbol {{slot-symbol}} in object |
---|
144 | {{obj}} (immediate or inherited). If no slot of that name exists, an |
---|
145 | error is signaled. |
---|
146 | |
---|
147 | <procedure>(object-get obj slot-symbol noslot-thunk)</procedure> |
---|
148 | |
---|
149 | Yields the value of slot named by symbol {{slot-symbol}} in object |
---|
150 | {{obj}} (immediate or inherited), if any such slot exists. If no slot |
---|
151 | of that name exists, then yields the value of applying closure |
---|
152 | {{noslot-thunk}}. |
---|
153 | |
---|
154 | <procedure>(object-apply obj slot-symbol { arg }*) |
---|
155 | |
---|
156 | Applies the method (closure) in the slot named by {{slot-symbol}} of |
---|
157 | object {{obj}}. The first term of the method is {{obj}}, and one or |
---|
158 | more {{arg}} are the remaining terms. If no such slot exists, an |
---|
159 | error is signaled. |
---|
160 | |
---|
161 | <procedure>(object-apply/noslot-thunk obj noslot-thunk slot-symbol { arg }*)</procedure> |
---|
162 | |
---|
163 | Like {{object-apply}}, except that, if the slot does not exist, |
---|
164 | instead of signalling an error, the value is the result of applying |
---|
165 | {{noslot-thunk}}. |
---|
166 | |
---|
167 | <procedure>(object-raw-clone/no-slots-copy obj)</procedure> |
---|
168 | <procedure>(object-raw-clone/copy-immed-slots obj)</procedure> |
---|
169 | <procedure>(object-raw-clone/copy-all-slots obj)</procedure> |
---|
170 | |
---|
171 | These procedures implement different ways of cloning an object, and are |
---|
172 | generally bound as {{clone}} methods in root objects. |
---|
173 | {{/no-slots-copy}} does not copy any slots, {{/copy-immed-slots}} |
---|
174 | copes immediate slots, and {{/copy-all-slots}} copies all slots |
---|
175 | including inherited ones. |
---|
176 | |
---|
177 | <parameter>(current-root-object)</parameter> |
---|
178 | |
---|
179 | Parameter for the default root object. The initial value is a root |
---|
180 | object that has {{object-raw-clone/no-slots-copy}} in its {{clone}} |
---|
181 | slot. |
---|
182 | |
---|
183 | Since Protobj's raison d'etre was to play with syntax, here it is. |
---|
184 | Note that slot names are never quoted. |
---|
185 | |
---|
186 | <macro>(^ obj)</macro> |
---|
187 | |
---|
188 | Parent of {{obj}}. |
---|
189 | |
---|
190 | <macro>(! obj slot val)</macro> |
---|
191 | <macro>(! obj)</macro> |
---|
192 | |
---|
193 | Sets object {{obj}}'s slot {{slot}}'s value to {{val}}. In the second |
---|
194 | form of this syntax, multiple slots of {{obj}} may be set at once, and |
---|
195 | are set in the order given. |
---|
196 | |
---|
197 | <macro>(? obj { slot }+)</macro> |
---|
198 | |
---|
199 | Yields the values of the given {{slot}}s of {{obj}}. If more than one |
---|
200 | {{slot}} is given, a multiple-value return is used. |
---|
201 | |
---|
202 | <macro>(@ obj slot { arg }*)</macro> |
---|
203 | <macro>(@ obj { (slot { arg }* ) }+)</macro> |
---|
204 | |
---|
205 | Applies {{obj}}'s {{slot}} method, with {{obj}} as the first term and |
---|
206 | {{arg}}s as the remaining terms. In the second form of this syntax, |
---|
207 | multiple methods may be applied, and the value is the value of the |
---|
208 | last method application. |
---|
209 | |
---|
210 | <macro>(% [ obj { (slot val) }* ])</macro> |
---|
211 | |
---|
212 | Clones object {{obj}}, binding any given {{slot}}s to respective given |
---|
213 | {{val}}s. |
---|
214 | |
---|
215 | You can override the method {{print}} to customize printing of objects: |
---|
216 | |
---|
217 | <enscript highlight=scheme> |
---|
218 | (define x (%)) |
---|
219 | (! x print |
---|
220 | (lambda (self #!optional (port (current-output-port))) |
---|
221 | (fprintf port "#<my object>"))) |
---|
222 | </enscript> |
---|
223 | |
---|
224 | ==== References |
---|
225 | |
---|
226 | ; [LGPL] : Free Software Foundation, "GNU Lesser General Public License," Version |
---|
227 | 2.1, February 1999, 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
---|
228 | USA. [[http://www.gnu.org/copyleft/lesser.html]] |
---|
229 | |
---|
230 | ; [OScheme] : Anselm Baird-Smith, "OScheme." [[http://koala.ilog.fr/abaird/oscheme/om.html]] |
---|
231 | |
---|
232 | ; [Self] : David Ungar and Randall B. Smith, "Self: The Power of Simplicity," ''Lisp and Symbolic Computation'', 4, 3, 1991. |
---|
233 | [[http://bibliography.selflanguage.org/_static/self-power.pdf]] |
---|
234 | |
---|
235 | ; [SLIB-Object] : Wade Humeniuk, "Macroless Object System," SLIB {{object}}. |
---|
236 | [[http://swissnet.ai.mit.edu/~jaffer/slib_7.html#SEC180]] |
---|
237 | |
---|
238 | ; [SRFI-9] : Richard Kelsey, "Defining Record Types," SRFI 9, 9 September 1999. |
---|
239 | [[http://srfi.schemers.org/srfi-9/srfi-9.html]] |
---|
240 | |
---|
241 | ; [SRFI-23] : Stephan Houben, "Error reporting mechanism," SRFI 23, 26 April 2001. |
---|
242 | [[http://srfi.schemers.org/srfi-23/srfi-23.html]] |
---|
243 | |
---|
244 | ; [SRFI-39] : Marc Feeley, "Parameter objects," SRFI 39, 30 June 2003. |
---|
245 | [[http://srfi.schemers.org/srfi-39/srfi-39.html]] |
---|
246 | |
---|
247 | ; [Testeez] : Neil W. Van Dyke, "Testeez: Simple Test Mechanism for Scheme," Version 0.1. |
---|
248 | [[http://www.neilvandyke.org/testeez/]] |
---|
249 | |
---|
250 | === Changelog |
---|
251 | |
---|
252 | * 0.3 Added {{print}} method |
---|
253 | * 0.2 Bugfix, {{$}} changed to {{?}} |
---|
254 | * 0.1 Initial release |
---|
255 | |
---|
256 | === License |
---|
257 | |
---|
258 | Copyright (c) 2005 Neil W. Van Dyke. This program is Free |
---|
259 | Software; you can redistribute it and/or modify it under the terms of the |
---|
260 | GNU Lesser General Public License as published by the Free Software |
---|
261 | Foundation; either version 2.1 of the License, or (at your option) any |
---|
262 | later version. This program is distributed in the hope that it will be |
---|
263 | useful, but without any warranty; without even the implied warranty of |
---|
264 | merchantability or fitness for a particular purpose. See the GNU Lesser |
---|
265 | General Public License [LGPL] for details. For other license options and |
---|
266 | commercial consulting, contact the author. |
---|
267 | |
---|