source: project/release/4/prometheus/prometheus-2/doc/prometheus.texinfo @ 14451

Last change on this file since 14451 was 14451, checked in by sjamaan, 11 years ago

Port Prometheus-2 to Chicken, using the clean, unmodified code from the release

File size: 19.5 KB
Line 
1\input texinfo.tex     @c -*-texinfo-*-
2@comment %**start of header
3@setfilename prometheus.info
4@settitle The Prometheus prototype-based object system
5@afourpaper
6@comment %**end of header
7
8@copying
9Copyright @copyright{} Jorgen Sch@"afer
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
2402111-1307, USA.
25@end copying
26
27@dircategory The Algorithmic Language Scheme
28@direntry
29* prometheus:: The Prometheus prototype-based object system
30@end direntry
31
32@titlepage
33@title Prometheus
34@subtitle A prototype-based object system for Scheme
35@author Jorgen Sch@"afer
36
37@page
38@vskip 0pt plus 1filll
39@insertcopying
40@end titlepage
41
42@contents
43
44@ifnottex
45@node top, Introduction, (dir), (dir)
46@top Prometheus: A prototype-based object system for Scheme
47
48Prometheus is a prototype-based message-passing object system for Scheme
49similar to the @uref{http://research.sun.com/self/, Self language}.
50
51This manual documents version 2 of Prometheus.
52
53@end ifnottex
54
55@menu
56* Introduction::               
57* Installation::               
58* Prometheus::                 
59* Examples::                   
60* Pitfalls::                   
61* Hermes::                     
62@end menu
63
64
65@node Introduction, Installation, top, top
66@chapter Introduction
67
68Prometheus is a prototype-based message-passing object system for Scheme
69similar to the @uref{http://research.sun.com/self/, Self language}
70
71In a prototype-based object system, an object is just a set of slots. A
72slot has a name and a value, or a @emph{handler procedure} which reacts
73on messages associated with the slot. Some slots are special, so-called
74@emph{parent slots}, whose use will become apparent shortly.
75
76Objects receive messages. A message consists of a symbol called a
77@emph{selector}, and zero or more arguments. When an object receives a
78message, the object searches for a slot whose name is equal (@code{eq?},
79actually) to the message selector. When it finds such a slot, it invokes
80the slot's handler, or returnes the slot's value, as appropriate. When
81the slot is not in the object, all objects in parent slots are queried
82for that slot.
83
84An object is created by @emph{cloning} an existing object. The new
85object is empty except for a single parent slot, which points to the
86cloned object. This way, the new object behaves exactly like the old
87one.
88
89In a prototype-based object system, objects are created and modified
90until they behave as required. Then, that object is cloned to create the
91real objects to work with---it forms the @emph{prototype} for the other
92objects.
93
94This manual documents version 2 of Prometheus. The canonical URL is
95@url{http://www.forcix.cx/software/prometheus.html}.
96
97
98@node Installation, Prometheus, Introduction, top
99@chapter Installation
100
101Prometheus is shipped as a package for @uref{http://www.s48.org/, Scheme
10248}. The structure @code{prometheus} serves as the main user API. To use
103it, issue the following commands at the REPL:
104
105@example
106> ,config ,load .../prometheus/scheme/packages.scm
107> ,open prometheus
108@end example
109
110A simple test would be the following:
111
112@example
113> (define o (*the-root-object* 'clone))
114> (o 'add-value-slot! 'fnord 'set-fnord! 23)
115> (o 'fnord)
11623
117@end example
118
119@noindent
120The package works unmodified with @uref{http://www.scsh.net/, the Scheme
121Shell}. Prometheus is written in R5RS Scheme with a few SRFI
122dependencies documented in @file{packages.scm}, and should be easy to
123port to other implementations of Scheme.
124
125
126@node Prometheus, Examples, Installation, top
127@chapter Prometheus
128
129@menu
130* Objects::                     
131* Slots::                       
132* Inheritance::                 
133* Root Objects::               
134* Syntactic Sugar::             
135* Private Messages::           
136@end menu
137
138
139@node Objects, Slots, Prometheus, Prometheus
140@section Objects
141
142Prometheus objects are implemented as closures. To send a message to
143that object, the closure is applied to the message selector (i.e., the
144slot name), followed by a number of arguments. The return value(s) of
145the message are returned from this application.
146
147
148@node Slots, Inheritance, Objects, Prometheus
149@section Slots
150
151Prometheus knows about three kinds of slots.
152
153@emph{Value slots} merely store a value which is returned when the
154corresponding message is received.
155
156@emph{Parent slots} are just like value slots, but have a special flag
157marking them as parents.
158
159@emph{Method slots} contain a procedure which is invoked for messages
160corresponding to this slot.
161
162The procedure is called with at least two arguments, conventionally
163called @var{self} and @var{resend}. If the message received any
164arguments, they are also passed, after @var{resend}. @var{Self} is the
165object which received the messages, as opposed to the object where this
166message handler was found in. @var{Resend} is a procedure which can be
167used to resend the message to further parents, if the current method
168does not wish to handle the message. @xref{Inheritance}, for more
169information about this.
170
171A typical method handler could thus look like:
172
173@lisp
174(lambda (self resend a b)
175  (/ (+ a b)
176     2))
177@end lisp
178
179Every slot, regardless of its kind, can be associated with a
180@emph{setter method} when it is created. Setter methods receive a single
181argument, and replaces the value of the corresponding slot with this
182argument. Setter methods can be created automatically when a given slot
183is created, and are removed when the corresponding getter slot is
184removed (but not vice-versa). Because of this, they are sometimes not
185considered to be slots, even if they are. @xref{Setters are Methods},
186for an example where this distinction is important.
187
188
189@node Inheritance, Root Objects, Slots, Prometheus
190@section Inheritance
191
192When a slot for a message is not found in the current object, all its
193parent slots are queried recursively, i.e. parent objects which don't
194know the slot query @emph{their} parents, etc.
195
196If no parent knows the slot, the original message receiving object is
197sent a @code{message-not-understood} message. If more than one parent
198knows the slot, the original message receiving object is sent a
199@code{ambiguous-message-send} message. @xref{Root Objects}, for a
200documentation of those messages. By default, they signal an error.
201
202Method slots can decide not to handle the message, but rather search the
203inheritance tree for other handlers. For this purpose, they are passed a
204procedure commonly called @var{resend}. @xref{Slots}, for an explanation
205of method slots.
206
207It is important to understand the difference between sending a message
208to an object, and @emph{re}sending it to the object. When a message is
209just sent to an object, methods will get that object as the @var{self}
210argument. When the method wants information about the object it is
211handling messages for, this is usually not what is intended.
212
213Consider an account object, which inherited from the account prototype.
214All the methods are in the account prototype. When a message to modify
215the account value is sent to the actual account object, the message
216receiver is the account object. It does not handle this message, so it
217resends the message to the parent object, the account prototype. The
218method handler to modify the account value should now know to modify the
219account object, not the prototype. Hence, the @var{self} argument should
220point to the account object, but if the message was just sent directly
221to the prototype, it @var{self} would be the prototype. Hence, resending
222exists. The @var{resend} procedure allows a method to manually request
223such a resending.
224
225
226@deffn Procedure resend @var{whereto} @var{message} @var{args} @dots{}
227
228This procedure will try to find a different handler for the given
229@var{message}. The handler can be searched for further up the
230inheritance tree, or even in a totally different object and its parents.
231
232@var{Whereto} can be one of the following values.
233
234@table @asis
235@item @code{#f}
236Use any parent of the object where this handler was found in.
237
238@item A symbol
239Use the object in the parent slot with this name.
240
241@item Any object
242Search for handlers in that object.
243@end table
244
245
246@var{Resend} is roughly equivalent in concept to CLOS'
247@code{(next-method)}.
248@end deffn
249
250
251@node Root Objects, Syntactic Sugar, Inheritance, Prometheus
252@section Root Objects
253
254Since objects are created by sending a @code{clone} message to other
255objects, there has to be a kind of root object. Prometheus provides a
256procedure to create such root objects.
257
258@deffn Procedure make-prometheus-root-object
259
260This creates a new root object from which other objects can be cloned.
261This object is independent of any other object, and thus creates a new
262inheritance tree.
263@end deffn
264
265@noindent
266Prometheus also provides a single existing root object, created with
267the procedure above. Unless specifically wanted otherwise, using this
268object as the root object ensures that all prometheus objects share a
269common ancestor.
270
271@deffn Variable *the-root-object*
272
273This is the default root object. If not really intended otherwise, this
274should be used as the root of the object hierarchy in a program.
275@end deffn
276
277@noindent
278Root objects contain a number of slots by default.
279
280
281@deffn Message clone
282
283Return a clone of the message recipient. This creates a new object with
284a single slot, @code{parent}, which points to the cloned object
285@end deffn
286
287
288@deffn Message add-value-slot! @var{getter} @var{value}
289@deffnx Message add-value-slot! @var{getter} @var{setter} @var{value}
290
291Add a new value slot to the recipient. The value of the slot can be
292retrieved with the @var{getter} message. If a @var{setter} message is
293given, that message can be used to change the value of the slot.
294@end deffn
295
296
297@deffn Message add-method-slot! @var{getter} @var{proc}
298@deffnx Message add-method-slot! @var{getter} @var{setter} @var{proc}
299
300Add a method to the recipient. Sending the object a @var{getter} message
301now invokes @var{proc} with the same arguments as the message, in
302addition to a @var{self} argument pointing to the current object and a
303@var{resend} procedure available to resend the message if the method
304does not want to handle it directly.
305
306The @var{setter} message can later be used to change the procedure.
307@end deffn
308
309
310@deffn Message add-parent-slot! @var{getter} @var{parent}
311@deffnx Message add-parent-slot! @var{getter} @var{setter} @var{parent}
312
313Add a parent slot to the recipient. Parent slots are searched for slots
314not found directly in the object. The @var{setter} message, if given,
315can be used to later change the value of the parent slot.
316@end deffn
317
318
319@deffn Message delete-slot! @var{name}
320
321Delete the slot named @var{name} from the receiving object. This also
322removes the setter corresponding to @var{name}, if any. Beware that the
323parents might contain the same slot, so a message send can still succeed
324even after a slot is deleted.
325@end deffn
326
327
328@deffn Message immediate-slot-list
329
330This message returns a list of slots in this object. The elements of the
331list are lists with four elements:
332
333@itemize @bullet
334@item
335@var{getter-name}
336
337@item
338@var{setter-name}
339
340@item
341@code{#f}
342
343@item
344@var{type}, which can be one of the symbols @code{value}, @code{method}
345or @code{parent}.
346@end itemize
347@end deffn
348
349@deffn Message message-not-understood @var{message} @var{args}
350
351This is received when the message @var{message} with arguments
352@var{args} to the object was not understood. The root object just
353signals an error.
354@end deffn
355
356
357@deffn Message ambiguous-message-send @var{message} @var{args}
358
359This is received when the message @var{message} with arguments
360@var{args} to the object would have reached multiple parents. The root
361object just signals an error.
362@end deffn
363
364
365@node Syntactic Sugar, Private Messages, Root Objects, Prometheus
366@section Syntactic Sugar
367
368Prometheus provides two forms of syntactic sugar for common operations
369on objects.
370
371A very common operation is to add method slots to an object, which
372usually looks like this:
373
374@lisp
375(obj 'add-method-slot!
376     'average
377     (lambda (self resend a b)
378       (/ (+ a b)
379          2)))
380@end lisp
381
382@noindent
383Using the special form of @code{define-method}, this can be shortened
384to:
385
386@lisp
387(define-method (obj 'average self resend a b)
388  (/ (+ a b)
389     2))
390@end lisp
391
392@deffn Syntax define-method (@var{obj} @var{'message} @var{self} @var{resend} . @var{args}) @var{body} @dots{}
393
394This is syntactic sugar for the often-used idiom to define a method
395slot, by sending a @code{add-method-slot!} message with a @var{message}
396name and a lambda form with @var{self}, @var{resend} and @var{args}
397formals, and a @var{body}.
398@end deffn
399
400@noindent
401Another common operation is to clone an object, and add a number of
402value and method slots:
403
404@lisp
405(define o (*the-root-object* 'clone))
406(o 'add-value-slot! 'constant 'set-constant! 5)
407(o 'add-method-slot! 'add
408   (lambda (self resend summand)
409     (+ summand (self 'constant))))
410@end lisp
411
412@noindent
413This can be more succintly written as:
414
415@lisp
416(define-object o (*the-root-object*)
417  (constant set-constant! 5)
418  ((add self resend summand)
419   (+ summand (self 'constant)))
420@end lisp
421
422@deffn Syntax define-object @var{name} (@var{parent} @var{other-parents} @dots{}) @var{slots} @dots{}
423
424This is syntactic sugar for the typical actions of cloning an object
425from a @var{parent} object, and adding more slots.
426
427@var{other-parents} is a list of @code{(name object)} lists, where each
428@var{object} is added as a parent slot named @var{name}.
429
430@var{slots} is a list of slot specifications, either @code{(getter
431value)} or @code{(getter setter value)} for value slots, or @code{((name
432self resend args @dots{}) body @dots{})} for method slots.
433@end deffn
434
435
436@node Private Messages,  , Syntactic Sugar, Prometheus
437@section Private Messages
438
439Message names in Prometheus don't have any required type. They are only
440compared using @code{eq?}. Because of this, any kind of Scheme object
441can be used as a message name. This means that it is possible to use a
442private Scheme value---for example, a freshly-allocated list---as a slot
443name. This can be used to keep slot names private, since it is not
444possible to create an object which is @code{eq?} to such an object
445except by receiving a reference to that object.
446
447
448@node Examples, Pitfalls, Prometheus, top
449@chapter Examples
450
451@menu
452* Simple Account Object::       
453* Creating Slots on Use::       
454* Diamond Inheritance::         
455@end menu
456
457
458@node Simple Account Object, Creating Slots on Use, Examples, Examples
459@section Simple Account Object
460
461This is from the file @file{examples/account.scm} in the Prometheus
462distribution:
463
464@verbatiminclude examples/account.scm
465
466
467@node Creating Slots on Use, Diamond Inheritance, Simple Account Object, Examples
468@section Creating Slots on Use
469
470This is from the file @file{examples/create-on-use.scm} in the
471Prometheus distribution:
472
473@verbatiminclude examples/create-on-use.scm
474
475
476@node Diamond Inheritance,  , Creating Slots on Use, Examples
477@section Diamond Inheritance
478
479This is from the file @file{examples/diamond.scm} in the Prometheus
480distribution:
481
482@verbatiminclude examples/diamond.scm
483
484
485@node Pitfalls, Hermes, Examples, top
486@chapter Pitfalls
487
488@menu
489* Setters are Methods::         
490@end menu
491
492
493@node Setters are Methods,  , Pitfalls, Pitfalls
494@section Setters are Methods
495
496Since Prometheus does not allow for ambiguous message sends, and setter
497methods are just messages, this can lead to a confusing situation.
498Consider the following code:
499
500@lisp
501(define o1 (*the-root-object* 'clone))
502(o1 'add-value-slot! 'foo 'set-foo! 1)
503(define o2 (o1 'clone))
504(define o3 (o2 'clone))
505(o3 'add-parent-slot! 'parent2 o1)
506@end lisp
507
508@noindent
509This creates a diamond-shaped inheritance tree. Now it is possible to
510send a @code{set-foo!} message to @code{o3}, though it inherits this
511slot from two parents, the slot is ultimately inherited from the same
512object. But now witness the following:
513
514@lisp
515> (o3 'foo)
516@result{} 3
517> (o2 'set-foo! 2)
518> (o3 'set-foo! 3)
519@error{} Ambiguous message send
520@end lisp
521
522@noindent
523What happened here? The @code{set-foo!} message added the @code{foo}
524slot to @code{o2}, but with it, also the associated method to mutate
525that slot, @code{set-foo!}. So, sending @code{set-foo!} to @code{o3}
526will find the same message both in @code{o1} and @code{o2}, and cause an
527ambiguous message send.
528
529Conclusion: Be extra careful with multiple inheritance.
530
531
532@node Hermes,  , Pitfalls, top
533@appendix Hermes
534
535Hermes is a very small object system based solely on messages. It's the
536basis upon which Prometheus is built. It should be considered internal
537information of Prometheus, but someone might find it useful, and it is
538visible in every Prometheus object.
539
540A Hermes object contains messages. A message has a name, a procedure to
541handle this message, and a type flag on whether the return value of this
542message should be treated as a parent object. A message-handling
543procedure is applied to the arguments of the message in addition to two
544arguments, probably known from Prometheus' method slots by now:
545@var{Self} and @var{resend}. Indeed, Prometheus' method slots are just a
546very thing wrapper around Hermes messages.
547
548The @code{hermes} structure exports a single procedure.
549
550@deffn Procedure make-hermes-object
551
552Return a new Hermes object which knows the basic messages.
553@end deffn
554
555@noindent
556The basic messages known by all Hermes objects are as follows.
557
558@deffn Message add-message! @var{name} @var{handler} [@var{parent?}]
559
560This adds a message named @var{name} to the object, upon receiving
561which, @var{handler} is applied to @var{self}, @var{resend}, and the
562arguments to the message. If @var{parent?} is supplied and not false,
563this message returns a parent object. In this case, it must be callable
564with no arguments, and must not use @var{resend}.
565@end deffn
566
567@deffn Message delete-message! @var{name}
568
569Remove the handler for the message named @var{name}. This causes such
570messages to be handled by parent objects in the future again.
571@end deffn
572
573@deffn Message %get-handler @var{name} @var{receiver} @var{args} @var{visited}
574
575The message upon which inheritance in Hermes is built. This returns a
576procedure of no arguments which handles the receiving of the message.
577This is delayed so that Hermes can check for duplicate handlers, which
578would be an error.
579
580@var{Name} is the name of the message we are looking for. @var{Receiver}
581is the original receiver of the message, to be used as the @var{self}
582argument to the handler procedure. @var{Args} is a list of arguments.
583@var{Visited} is a list of objects we have seen so far. This is used to
584detect cycles in the inheritance graph.
585
586This message returns two values. The first one is the handler and the
587other one the object in which this handler was found. The handler value
588can also be one of two symbols to signify an error condition. If it's
589the symbol @code{message-not-understood}, then neither this object nor
590any parents knew how to handle this message. If it's
591@code{ambiguous-message-send}, the same message could be handled by
592multiple parents in the inheritance graph. The user needs to add a
593message which resends the ambiguous message unambiguously to the correct
594parent. In either case, the second return value is @code{#f}. The
595handler procedure itself accepts no arguments, and just runs the
596message.
597@end deffn
598
599@noindent
600It is absolutely sufficient for an object to handle only the
601@code{%get-handler} message to participate in the inheritance handling
602of Hermes.
603
604@bye
Note: See TracBrowser for help on using the repository browser.