]> gitweb.factorcode.org Git - factor.git/blob - core/classes/tuple/tuple-docs.factor
Builtinn types now use new slot accessors; tuple slot type declaration work in progress
[factor.git] / core / classes / tuple / tuple-docs.factor
1 USING: generic help.markup help.syntax kernel
2 classes.tuple.private classes slots quotations words arrays
3 generic.standard sequences definitions compiler.units
4 growable vectors sbufs ;
5 IN: classes.tuple
6
7 ARTICLE: "parametrized-constructors" "Parameterized constructors"
8 "A " { $emphasis "parametrized constructor" } " is a word which directly or indirectly calls " { $link new } " or " { $link boa } ", but instead of passing a literal class symbol, it takes the class symbol as an input from the stack."
9 $nl
10 "Parametrized constructors are useful in many situations, in particular with subclassing. For example, consider the following code:"
11 { $code
12     "TUPLE: vehicle max-speed occupants ;"
13     ""
14     ": add-occupant ( person vehicle -- ) occupants>> push ;"
15     ""
16     "TUPLE: car < vehicle engine ;"
17     ": <car> ( max-speed engine -- car )"
18     "    car new"
19     "        V{ } clone >>occupants"
20     "        swap >>engine"
21     "        swap >>max-speed ;"
22     ""
23     "TUPLE: aeroplane < vehicle max-altitude ;"
24     ": <aeroplane> ( max-speed max-altitude -- aeroplane )"
25     "    aeroplane new"
26     "        V{ } clone >>occupants"
27     "        swap >>max-altitude"
28     "        swap >>max-speed ;"
29 }
30 "The two constructors depend on the implementation of " { $snippet "vehicle" } " because they are responsible for initializing the " { $snippet "occupants" } " slot to an empty vector. If this slot is changed to contain a hashtable instead, there will be two places instead of one. A better approach is to use a parametrized constructor for vehicles:"
31 { $code
32     "TUPLE: vehicle max-speed occupants ;"
33     ""
34     ": add-occupant ( person vehicle -- ) occupants>> push ;"
35     ""
36     ": new-vehicle ( class -- vehicle )"
37     "    new"
38     "        V{ } clone >>occupants ;"
39     ""
40     "TUPLE: car < vehicle engine ;"
41     ": <car> ( max-speed engine -- car )"
42     "    car new-vehicle"
43     "        swap >>engine"
44     "        swap >>max-speed ;"
45     ""
46     "TUPLE: aeroplane < vehicle max-altitude ;"
47     ": <aeroplane> ( max-speed max-altitude -- aeroplane )"
48     "    aeroplane new-vehicle"
49     "        swap >>max-altitude"
50     "        swap >>max-speed ;"
51 }
52 "The naming convention for parametrized constructors is " { $snippet "new-" { $emphasis "class" } } "." ;
53
54 ARTICLE: "tuple-constructors" "Tuple constructors"
55 "Tuples are created by calling one of two constructor primitives:"
56 { $subsection new }
57 { $subsection boa }
58 "A shortcut for defining BOA constructors:"
59 { $subsection POSTPONE: C: }
60 "By convention, construction logic is encapsulated in a word named after the tuple class surrounded in angle brackets; for example, the constructor word for a " { $snippet "point" } " class might be named " { $snippet "<point>" } "."
61 $nl
62 "All tuple construction should be done through constructor words, and construction primitives should be encapsulated and never called outside of the vocabulary where the class is defined, because this encourages looser coupling. For example, a constructor word could be changed to use memoization instead of always constructing a new instance, or it could be changed to construt a different class, without breaking callers."
63 $nl
64 "Examples of constructors:"
65 { $code
66     "TUPLE: color red green blue alpha ;"
67     ""
68     "! The following two are equivalent"
69     "C: <rgba> rgba"
70     ": <rgba> color boa ;"
71     ""
72     "! We can define constructors which call other constructors"
73     ": <rgb> f <rgba> ;"
74     ""
75     "! The following two are equivalent"
76     ": <color> color new ;"
77     ": <color> f f f f <rgba> ;"
78 }
79 { $subsection "parametrized-constructors" } ;
80
81 ARTICLE: "tuple-inheritance-example" "Tuple subclassing example"
82 "Rectangles, parallelograms and circles are all shapes. We support two operations on shapes:"
83 { $list
84     "Computing the area"
85     "Computing the perimiter"
86 }
87 "Rectangles and parallelograms use the same algorithm for computing the area, whereas they use different algorithms for computing perimiter. Also, rectangles and parallelograms both have " { $snippet "width" } " and " { $snippet "height" } " slots. We can exploit this with subclassing:"
88 { $code
89     "GENERIC: area ( shape -- n )"
90     "GENERIC: perimiter ( shape -- n )"
91     ""
92     "TUPLE: shape ;"
93     ""
94     "TUPLE: circle < shape radius ;"
95     "M: area circle radius>> sq pi * ;"
96     "M: perimiter circle radius>> 2 * pi * ;"
97     ""
98     "TUPLE: quad < shape width height"
99     "M: area quad [ width>> ] [ height>> ] bi * ;"
100     ""
101     "TUPLE: rectangle < quad ;"
102     "M: rectangle perimiter [ width>> 2 * ] [ height>> 2 * ] bi + ;"
103     ""
104     ": hypot ( a b -- c ) [ sq ] bi@ + sqrt ;"
105     ""
106     "TUPLE: parallelogram < quad skew ;"
107     "M: parallelogram perimiter"
108     "    [ width>> 2 * ] [ [ height>> ] [ skew>> ] bi hypot 2 * ] bi + ;"
109 } ;
110
111 ARTICLE: "tuple-inheritance-anti-example" "When not to use tuple subclassing"
112 "Tuple subclassing should only be used for " { $emphasis "is-a" } " relationships; for example, a car " { $emphasis "is a" } " vehicle, and a circle " { $emphasis "is a" } " shape."
113 { $heading "Anti-pattern #1: subclassing for has-a" }
114 "Subclassing should not be used for " { $emphasis "has-a" } " relationships. For example, if a shape " { $emphasis "has a" } " color, then " { $snippet "shape" } " should not subclass " { $snippet "color" } ". Using tuple subclassing in inappropriate situations leads to code which is more brittle and less flexible than it should be."
115 $nl
116 "For example, suppose that " { $snippet "shape" } " inherits from " { $snippet "color" } ":"
117 { $code
118     "TUPLE: color r g b ;"
119     "TUPLE: shape < color ... ;"
120 }
121 "Now, the implementation of " { $snippet "shape" } " depends on a specific representation of colors as RGB colors. If a new generic color protocol is devised which also allows HSB and YUV colors to be used, the shape class will not be able to take advantage of them without changes. A better approach is to store the color in a slot:"
122 { $code
123     "TUPLE: rgb-color r g b ;"
124     "TUPLE: hsv-color h s v ;"
125     "..."
126     "TUPLE: shape color ... ;"
127 }
128 "The " { $vocab-link "delegate" } " library provides a language abstraction for expressing has-a relationships."
129 { $heading "Anti-pattern #2: subclassing for implementation sharing only" }
130 "Tuple subclassing purely for sharing implementations of methods is not a good idea either. If a class " { $snippet "A" } " is a subclass of a class " { $snippet "B" } ", then instances of " { $snippet "A" } " should be usable anywhere that an instance of " { $snippet "B" } " is. If this properly does not hold, then subclassing should not be used."
131 $nl
132 "There are two alternatives which are preferred to subclassing in this case. The first is " { $link "mixins" } "."
133 $nl
134 "The second is to use ad-hoc slot polymorphism. If two classes define a slot with the same name, then code which uses " { $link "accessors" } " can operate on instances of both objects, assuming the values stored in that slot implement a common protocol. This allows code to be shared without creating contrieved relationships between classes."
135 { $heading "Anti-pattern #3: subclassing to override a method definition" }
136 "While method overriding is a very powerful tool, improper use can cause tight coupling of code and lead to difficulty in testing and refactoring. Subclassing should not be used as a means of ``monkey patching'' methods to fix bugs and add features. Only subclass from classes which were designed to be inherited from, and when writing classes of your own which are intended to be subclassed, clearly document that subclasses may and may not do. This includes construction policy; document whether subclasses should use " { $link new } ", " { $link boa } ", or a custom parametrized constructor."
137 { $see-also "parametrized-constructors" } ;
138
139 ARTICLE: "tuple-subclassing" "Tuple subclassing"
140 "Tuple subclassing can be used to express natural relationships between classes at the language level. For example, every car " { $emphasis "is a" } " vehicle, so if the " { $snippet "car" } " class subclasses the " { $snippet "vehicle" } " class, it can " { $emphasis "inherit" } " the slots and methods of " { $snippet "vehicle" } "."
141 $nl
142 "To define one tuple class as a subclass of another, use the optional superclass parameter to " { $link POSTPONE: TUPLE: } ":"
143 { $code
144     "TUPLE: subclass < superclass ... ;"
145 }
146 { $subsection "tuple-inheritance-example" }
147 { $subsection "tuple-inheritance-anti-example" } 
148 { $see-also "call-next-method" "parametrized-constructors" "unions" "mixins" } ;
149
150 ARTICLE: "tuple-introspection" "Tuple introspection"
151 "In addition to the slot reader and writer words which " { $link POSTPONE: TUPLE: } " defines for every tuple class, it is possible to construct and take apart entire tuples in a generic way."
152 { $subsection >tuple }
153 { $subsection tuple>array }
154 { $subsection tuple-slots }
155 "Tuple classes can also be defined at run time:"
156 { $subsection define-tuple-class }
157 { $see-also "slots" "mirrors" } ;
158
159 ARTICLE: "tuple-examples" "Tuple examples"
160 "An example:"
161 { $code "TUPLE: employee name salary position ;" }
162 "This defines a class word named " { $snippet "employee" } ", a predicate " { $snippet "employee?" } ", and the following slot accessors:"
163 { $table
164     { "Reader" "Writer" "Setter" "Changer" }
165     { { $snippet "name>>" }    { $snippet "(>>name)" }    { $snippet ">>name" }    { $snippet "change-name" }    }
166     { { $snippet "salary>>" } { $snippet "(>>salary)" } { $snippet ">>salary" } { $snippet "change-salary" } }
167     { { $snippet "position>>" }   { $snippet "(>>position)" }   { $snippet ">>position" }   { $snippet "change-position" }   }
168 }
169 "We can define a constructor which makes an empty employee:"
170 { $code ": <employee> ( -- employee )"
171     "    employee new ;" }
172 "Or we may wish the default constructor to always give employees a starting salary:"
173 { $code
174     ": <employee> ( -- employee )"
175     "    employee new"
176     "        40000 >>salary ;"
177 }
178 "We can define more refined constructors:"
179 { $code
180     ": <manager> ( -- manager )"
181     "    <employee> \"project manager\" >>position ;" }
182 "An alternative strategy is to define the most general BOA constructor first:"
183 { $code
184     ": <employee> ( name position -- person )"
185     "    40000 employee boa ;"
186 }
187 "Now we can define more specific constructors:"
188 { $code
189     ": <manager> ( name -- person )"
190     "    \"manager\" <person> ;" }
191 "An example using reader words:"
192 { $code
193     "TUPLE: check to amount number ;"
194     ""
195     "SYMBOL: checks"
196     ""
197     ": <check> ( to amount -- check )"
198     "    checks counter check boa ;"
199     ""
200     ": biweekly-paycheck ( employee -- check )"
201     "    dup name>> swap salary>> 26 / <check> ;"
202 }
203 "An example of using a changer:"
204 { $code
205     ": positions"
206     "    {"
207     "        \"junior programmer\""
208     "        \"senior programmer\""
209     "        \"project manager\""
210     "        \"department manager\""
211     "        \"executive\""
212     "        \"CTO\""
213     "        \"CEO\""
214     "        \"enterprise Java world dictator\""
215     "    } ;"
216     ""
217     ": next-position ( role -- newrole )"
218     "    positions [ index 1+ ] keep nth ;"
219     ""
220     ": promote ( person -- person )"
221     "    [ 1.2 * ] change-salary"
222     "    [ next-position ] change-position ;"
223 }
224 "An example using subclassing can be found in " { $link "tuple-inheritance-example" } "." ;
225
226 ARTICLE: "tuple-redefinition" "Tuple redefinition"
227 "In the following, the " { $emphasis "direct slots" } " of a tuple class refers to the slot names specified in the " { $link POSTPONE: TUPLE: } " form defining the tuple class, and the " { $emphasis "effective slots" } " refers to the concatenation of the direct slots together with slots defined on superclasses."
228 $nl
229 "When a tuple class is redefined, all instances of the class, including subclasses, are updated. For each instance, the list of effective slots is compared with the previous list. If any slots were removed, the values are removed from the instance and are lost forever. If any slots were added, the instance gains these slots with an initial value of " { $link f } "."
230 $nl
231 "There are three ways to change the list of effective slots of a class:"
232 { $list
233     "Adding or removing direct slots of the class"
234     "Adding or removing direct slots of a superclass of the class"
235     "Changing the inheritance hierarchy by redefining a class to have a different superclass"
236 }
237 "In all cases, the new effective slots are compared with the old effective slots, and each instance is updated as follows:"
238 { $list
239     "If any slots were removed, the values are removed from the instance and are lost forever."
240     { "If any slots were added, the instance gains these slots with an initial value of " { $link f } "." }
241     "If any slots are permuted, their values in instances do not change; only the layout of the instance changes in memory."
242     "If the number or order of effective slots changes, any BOA constructors are recompiled."
243 }
244 "Note that if a slot is moved from a class to its superclass (or vice versa) in the same compilation unit, the value of the slot is preserved in existing instances, because tuple instance update always runs at the end of a compilation unit. However, if it is removed in one compilation unit and added in another, the value in existing instances is lost." ;
245
246 ARTICLE: "protocol-slots" "Protocol slots"
247 "A " { $emphasis "protocol slot" } " is one which is assumed to exist by the implementation of a class, without being defined on the class itself. The burden is on subclasses (or mixin instances) to provide this slot."
248 $nl
249 "Protocol slots are defined using a parsing word:"
250 { $subsection POSTPONE: SLOT: }
251 "Protocol slots are used where the implementation of a superclass needs to assume that each subclass defines certain slots, however the slots of each subclass are potentially declared with different class specializers, thus preventing the slots from being defined in the superclass."
252 $nl
253 "For example, the " { $link growable } " mixin provides an implementation of the sequence protocol which wraps an underlying sequence, resizing it as necessary when elements are added beyond the length of the sequence. It assumes that the concrete mixin instances define two slots, " { $snippet "length" } " and " { $snippet "underlying" } ". These slots are defined as protocol slots:"
254 { $snippet "SLOT: length" "SLOT: underlying" }
255 "An alternate approach would be to define " { $link growable } " as a tuple class with these two slots, and have other classes subclass it as required. However, this rules out subclasses defining these slots with custom type declarations."
256 $nl
257 "For example, compare the definitions of the " { $link sbuf } " class,"
258 { $code
259     "TUPLE: sbuf"
260     "{ \"underlying\" string }"
261     "{ \"length\" array-capacity } ;"
262     ""
263     "INSTANCE: sbuf growable"
264 }
265 "with that of the " { $link vector } " class:"
266 { $code
267     "TUPLE: vector"
268     "{ \"underlying\" array }"
269     "{ \"length\" array-capacity } ;"
270     ""
271     "INSTANCE: vector growable"
272 } ;
273
274 ARTICLE: "tuples" "Tuples"
275 "Tuples are user-defined classes composed of named slots."
276 { $subsection "tuple-examples" }
277 "A parsing word defines tuple classes:"
278 { $subsection POSTPONE: TUPLE: }
279 "For each tuple class, several words are defined. First, there is the class word, a class predicate, and accessor words for each slot."
280 $nl
281 "The class word is used for defining methods on the tuple class; it has the same name as the tuple class. The predicate is named " { $snippet { $emphasis "name" } "?" } ". Tuple slots are accessed via accessor words:"
282 { $subsection "accessors" }
283 "Initially, no specific words are defined for constructing new instances of the tuple. Constructors must be defined explicitly:"
284 { $subsection "tuple-constructors" }
285 "Expressing relationships through the object system:"
286 { $subsection "tuple-subclassing" }
287 "Protocol slots:"
288 { $subsection "protocol-slots" }
289 "Introspection:"
290 { $subsection "tuple-introspection" }
291 "Tuple classes can be redefined; this updates existing instances:"
292 { $subsection "tuple-redefinition" }
293 "Tuple literal syntax is documented in " { $link "syntax-tuples" } "." ;
294
295 ABOUT: "tuples"
296
297 HELP: tuple=
298 { $values { "tuple1" tuple } { "tuple2" tuple } { "?" "a boolean" } }
299 { $description "Low-level tuple equality test. User code should use " { $link = } " instead." }
300 { $warning "This word is in the " { $vocab-link "classes.tuple.private" } " vocabulary because it does not do any type checking. Passing values which are not tuples can result in memory corruption." } ;
301
302 HELP: tuple
303 { $class-description "The class of tuples. This class is further partitioned into disjoint subclasses; each tuple shape defined by " { $link POSTPONE: TUPLE: } " is a new class."
304 $nl
305 "Tuple classes have additional word properties:"
306 { $list
307     { { $snippet "\"constructor\"" } " - a word for creating instances of this tuple class" }
308     { { $snippet "\"predicate\"" } " - a quotation which tests if the top of the stack is an instance of this tuple class" }
309     { { $snippet "\"slots\"" } " - a sequence of " { $link slot-spec } " instances" }
310     { { $snippet "\"slot-names\"" } " - a sequence of strings naming the tuple's slots" }
311     { { $snippet "\"tuple-size\"" } " - the number of slots" }
312 } } ;
313
314 HELP: define-tuple-predicate
315 { $values { "class" tuple-class } }
316 { $description "Defines a predicate word that tests if the top of the stack is an instance of " { $snippet "class" } ". This will only work if " { $snippet "class" } " is a tuple class." }
317 $low-level-note ;
318
319 HELP: redefine-tuple-class
320 { $values { "class" class } { "superclass" class } { "slots" "a sequence of strings" } }
321 { $description "If the new slot layout differs from the existing one, updates all existing instances of this tuple class, and forgets any slot accessor words which are no longer needed."
322 $nl
323 "If the class is not a tuple class word, this word does nothing." }
324 $low-level-note ;
325
326 HELP: tuple-slots
327 { $values { "tuple" tuple } { "seq" sequence } }
328 { $description "Pushes a sequence of tuple slot values, not including the tuple class word." } ;
329
330 { tuple-slots tuple>array } related-words
331
332 HELP: define-tuple-slots
333 { $values { "class" tuple-class } }
334 { $description "Defines slot accessor and mutator words for the tuple." }
335 $low-level-note ;
336
337 HELP: check-tuple
338 { $values { "class" class } }
339 { $description "Throws a " { $link check-tuple } " error if " { $snippet "word" } " is not a tuple class word." }
340 { $error-description "Thrown if " { $link POSTPONE: C: } " is called with a word which does not name a tuple class." } ;
341
342 HELP: define-tuple-class
343 { $values { "class" word } { "superclass" class } { "slots" "a sequence of strings" } }
344 { $description "Defines a tuple class inheriting from " { $snippet "superclass" } " with slots named by " { $snippet "slots" } ". This is the run time equivalent of " { $link POSTPONE: TUPLE: } "." }
345 { $notes "This word must be called from inside " { $link with-compilation-unit } "." }
346 { $side-effects "class" } ;
347
348 { tuple-class define-tuple-class POSTPONE: TUPLE: } related-words
349
350 HELP: >tuple
351 { $values { "seq" sequence } { "tuple" tuple } }
352 { $description "Creates a tuple with slot values taken from a sequence. The first element of the sequence must be a tuple class word and the remainder the declared slots."
353 $nl
354 "If the sequence has too many elements, they are ignored, and if it has too few, the remaining slots in the tuple are set to " { $link f } "." }
355 { $errors "Throws an error if the first element of the sequence is not a tuple class word." } ;
356
357 HELP: tuple>array ( tuple -- array )
358 { $values { "tuple" tuple } { "array" array } }
359 { $description "Outputs an array having the tuple's slots as elements. The first element is the tuple class word and remainder are declared slots." } ;
360
361 HELP: <tuple> ( layout -- tuple )
362 { $values { "layout" tuple-layout } { "tuple" tuple } }
363 { $description "Low-level tuple constructor. User code should never call this directly, and instead use " { $link new } "." } ;
364
365 HELP: <tuple-boa> ( ... layout -- tuple )
366 { $values { "..." "values" } { "layout" tuple-layout } { "tuple" tuple } }
367 { $description "Low-level tuple constructor. User code should never call this directly, and instead use " { $link boa } "." } ;
368
369 HELP: new
370 { $values { "class" tuple-class } { "tuple" tuple } }
371 { $description "Creates a new instance of " { $snippet "class" } " with all slots initially set to " { $link f } "." }
372 { $examples
373     { $example
374         "USING: kernel prettyprint ;"
375         "IN: scratchpad"
376         "TUPLE: employee number name department ;"
377         "employee new ."
378         "T{ employee f f f f }"
379     }
380 } ;
381
382 HELP: construct
383 { $values { "..." "slot values" } { "slots" "a sequence of setter words" } { "class" tuple-class } { "tuple" tuple } }
384 { $description "Creates a new instance of " { $snippet "class" } ", storing consecutive stack values into the slots of the new tuple using setter words in " { $snippet "slots" } ". The top-most stack element is stored in the right-most slot." }
385 { $examples
386     "We can define a class:"
387     { $code "TUPLE: color red green blue alpha ;" }
388     "Together with two constructors:"
389     { $code
390         ": <rgb> ( r g b -- color )"
391         "    { set-color-red set-color-green set-color-blue }"
392         "    color construct ;"
393         ""
394         ": <rgba> ( r g b a -- color )"
395         "    { set-color-red set-color-green set-color-blue set-color-alpha }"
396         "    color construct ;"
397     }
398     "The last definition is actually equivalent to the following:"
399     { $code ": <rgba> ( r g b a -- color ) rgba boa ;" }
400     "Which can be abbreviated further:"
401     { $code "C: <rgba> color" }
402 } ;
403
404 HELP: boa
405 { $values { "..." "slot values" } { "class" tuple-class } { "tuple" tuple } }
406 { $description "Creates a new instance of " { $snippet "class" } " and fill in the slots from the stack, with the top-most stack element being stored in the right-most slot." }
407 { $notes "The name " { $snippet "boa" } " is shorthand for ``by order of arguments'', and ``BOA constructor'' is a pun on ``boa constrictor''." } ;