]> gitweb.factorcode.org Git - factor.git/blob - core/classes/tuple/tuple-docs.factor
9f8ce8324074f94dd97cf3fc994a4f1a207c16bf
[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 IN: classes.tuple
5
6 ARTICLE: "parametrized-constructors" "Parameterized constructors"
7 "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."
8 $nl
9 "Parametrized constructors are useful in many situations, in particular with subclassing. For example, consider the following code:"
10 { $code
11     "TUPLE: vehicle max-speed occupants ;"
12     ""
13     ": add-occupant ( person vehicle -- ) occupants>> push ;"
14     ""
15     "TUPLE: car < vehicle engine ;"
16     ": <car> ( max-speed engine -- car )"
17     "    car new"
18     "        V{ } clone >>occupants"
19     "        swap >>engine"
20     "        swap >>max-speed ;"
21     ""
22     "TUPLE: aeroplane < vehicle max-altitude ;"
23     ": <aeroplane> ( max-speed max-altitude -- aeroplane )"
24     "    aeroplane new"
25     "        V{ } clone >>occupants"
26     "        swap >>max-altitude"
27     "        swap >>max-speed ;"
28 }
29 "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:"
30 { $code
31     "TUPLE: vehicle max-speed occupants ;"
32     ""
33     ": add-occupant ( person vehicle -- ) occupants>> push ;"
34     ""
35     ": new-vehicle ( class -- vehicle )"
36     "    new"
37     "        V{ } clone >>occupants ;"
38     ""
39     "TUPLE: car < vehicle engine ;"
40     ": <car> ( max-speed engine -- car )"
41     "    car new-vehicle"
42     "        swap >>engine"
43     "        swap >>max-speed ;"
44     ""
45     "TUPLE: aeroplane < vehicle max-altitude ;"
46     ": <aeroplane> ( max-speed max-altitude -- aeroplane )"
47     "    aeroplane new-vehicle"
48     "        swap >>max-altitude"
49     "        swap >>max-speed ;"
50 }
51 "The naming convention for parametrized constructors is " { $snippet "new-" { $emphasis "class" } } "." ;
52
53 ARTICLE: "tuple-constructors" "Tuple constructors"
54 "Tuples are created by calling one of two constructor primitives:"
55 { $subsection new }
56 { $subsection boa }
57 "A shortcut for defining BOA constructors:"
58 { $subsection POSTPONE: C: }
59 "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>" } "."
60 $nl
61 "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."
62 $nl
63 "Examples of constructors:"
64 { $code
65     "TUPLE: color red green blue alpha ;"
66     ""
67     "! The following two are equivalent"
68     "C: <rgba> rgba"
69     ": <rgba> color boa ;"
70     ""
71     "! We can define constructors which call other constructors"
72     ": <rgb> f <rgba> ;"
73     ""
74     "! The following two are equivalent"
75     ": <color> color new ;"
76     ": <color> f f f f <rgba> ;"
77 }
78 { $subsection "parametrized-constructors" } ;
79
80 ARTICLE: "tuple-inheritance-example" "Tuple subclassing example"
81 "Rectangles, parallelograms and circles are all shapes. We support two operations on shapes:"
82 { $list
83     "Computing the area"
84     "Computing the perimiter"
85 }
86 "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:"
87 { $code
88     "GENERIC: area ( shape -- n )"
89     "GENERIC: perimiter ( shape -- n )"
90     ""
91     "TUPLE: shape ;"
92     ""
93     "TUPLE: circle < shape radius ;"
94     "M: area circle radius>> sq pi * ;"
95     "M: perimiter circle radius>> 2 * pi * ;"
96     ""
97     "TUPLE: quad < shape width height"
98     "M: area quad [ width>> ] [ height>> ] bi * ;"
99     ""
100     "TUPLE: rectangle < quad ;"
101     "M: rectangle perimiter [ width>> 2 * ] [ height>> 2 * ] bi + ;"
102     ""
103     ": hypot ( a b -- c ) [ sq ] bi@ + sqrt ;"
104     ""
105     "TUPLE: parallelogram < quad skew ;"
106     "M: parallelogram perimiter"
107     "    [ width>> 2 * ] [ [ height>> ] [ skew>> ] bi hypot 2 * ] bi + ;"
108 } ;
109
110 ARTICLE: "tuple-inheritance-anti-example" "When not to use tuple subclassing"
111 "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."
112 { $heading "Anti-pattern #1: subclassing for has-a" }
113 "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."
114 $nl
115 "For example, suppose that " { $snippet "shape" } " inherits from " { $snippet "color" } ":"
116 { $code
117     "TUPLE: color r g b ;"
118     "TUPLE: shape < color ... ;"
119 }
120 "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:"
121 { $code
122     "TUPLE: rgb-color r g b ;"
123     "TUPLE: hsv-color h s v ;"
124     "..."
125     "TUPLE: shape color ... ;"
126 }
127 "The " { $vocab-link "delegate" } " library provides a language abstraction for expressing has-a relationships."
128 { $heading "Anti-pattern #2: subclassing for implementation sharing only" }
129 "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."
130 $nl
131 "There are two alternatives which are preferred to subclassing in this case. The first is " { $link "mixins" } "."
132 $nl
133 "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."
134 { $heading "Anti-pattern #3: subclassing to override a method definition" }
135 "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."
136 { $see-also "parametrized-constructors" } ;
137
138 ARTICLE: "tuple-subclassing" "Tuple subclassing"
139 "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" } "."
140 $nl
141 "To define one tuple class as a subclass of another, use the optional superclass parameter to " { $link POSTPONE: TUPLE: } ":"
142 { $code
143     "TUPLE: subclass < superclass ... ;"
144 }
145 { $subsection "tuple-inheritance-example" }
146 { $subsection "tuple-inheritance-anti-example" } 
147 { $see-also "call-next-method" "parametrized-constructors" "unions" "mixins" } ;
148
149 ARTICLE: "tuple-introspection" "Tuple introspection"
150 "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."
151 { $subsection >tuple }
152 { $subsection tuple>array }
153 { $subsection tuple-slots }
154 "Tuple classes can also be defined at run time:"
155 { $subsection define-tuple-class }
156 { $see-also "slots" "mirrors" } ;
157
158 ARTICLE: "tuple-examples" "Tuple examples"
159 "An example:"
160 { $code "TUPLE: employee name salary position ;" }
161 "This defines a class word named " { $snippet "employee" } ", a predicate " { $snippet "employee?" } ", and the following slot accessors:"
162 { $table
163     { "Reader" "Writer" "Setter" "Changer" }
164     { { $snippet "name>>" }    { $snippet "(>>name)" }    { $snippet ">>name" }    { $snippet "change-name" }    }
165     { { $snippet "salary>>" } { $snippet "(>>salary)" } { $snippet ">>salary" } { $snippet "change-salary" } }
166     { { $snippet "position>>" }   { $snippet "(>>position)" }   { $snippet ">>position" }   { $snippet "change-position" }   }
167 }
168 "We can define a constructor which makes an empty employee:"
169 { $code ": <employee> ( -- employee )"
170     "    employee new ;" }
171 "Or we may wish the default constructor to always give employees a starting salary:"
172 { $code
173     ": <employee> ( -- employee )"
174     "    employee new"
175     "        40000 >>salary ;"
176 }
177 "We can define more refined constructors:"
178 { $code
179     ": <manager> ( -- manager )"
180     "    <employee> \"project manager\" >>position ;" }
181 "An alternative strategy is to define the most general BOA constructor first:"
182 { $code
183     ": <employee> ( name position -- person )"
184     "    40000 employee boa ;"
185 }
186 "Now we can define more specific constructors:"
187 { $code
188     ": <manager> ( name -- person )"
189     "    \"manager\" <person> ;" }
190 "An example using reader words:"
191 { $code
192     "TUPLE: check to amount number ;"
193     ""
194     "SYMBOL: checks"
195     ""
196     ": <check> ( to amount -- check )"
197     "    checks counter check boa ;"
198     ""
199     ": biweekly-paycheck ( employee -- check )"
200     "    dup name>> swap salary>> 26 / <check> ;"
201 }
202 "An example of using a changer:"
203 { $code
204     ": positions"
205     "    {"
206     "        \"junior programmer\""
207     "        \"senior programmer\""
208     "        \"project manager\""
209     "        \"department manager\""
210     "        \"executive\""
211     "        \"CTO\""
212     "        \"CEO\""
213     "        \"enterprise Java world dictator\""
214     "    } ;"
215     ""
216     ": next-position ( role -- newrole )"
217     "    positions [ index 1+ ] keep nth ;"
218     ""
219     ": promote ( person -- person )"
220     "    [ 1.2 * ] change-salary"
221     "    [ next-position ] change-position ;"
222 }
223 "An example using subclassing can be found in " { $link "tuple-inheritance-example" } "." ;
224
225 ARTICLE: "tuple-redefinition" "Tuple redefinition"
226 "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."
227 $nl
228 "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 } "."
229 $nl
230 "There are three ways to change the list of effective slots of a class:"
231 { $list
232     "Adding or removing direct slots of the class"
233     "Adding or removing direct slots of a superclass of the class"
234     "Changing the inheritance hierarchy by redefining a class to have a different superclass"
235 }
236 "In all cases, the new effective slots are compared with the old effective slots, and each instance is updated as follows:"
237 { $list
238     "If any slots were removed, the values are removed from the instance and are lost forever."
239     { "If any slots were added, the instance gains these slots with an initial value of " { $link f } "." }
240     "If any slots are permuted, their values in instances do not change; only the layout of the instance changes in memory."
241     "If the number or order of effective slots changes, any BOA constructors are recompiled."
242 }
243 "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." ;
244
245 ARTICLE: "tuples" "Tuples"
246 "Tuples are user-defined classes composed of named slots."
247 { $subsection "tuple-examples" }
248 "A parsing word defines tuple classes:"
249 { $subsection POSTPONE: TUPLE: }
250 "For each tuple class, several words are defined. First, there is the class word, a class predicate, and accessor words for each slot."
251 $nl
252 "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:"
253 { $subsection "accessors" }
254 "Initially, no specific words are defined for constructing new instances of the tuple. Constructors must be defined explicitly:"
255 { $subsection "tuple-constructors" }
256 "Expressing relationships through the object system:"
257 { $subsection "tuple-subclassing" }
258 "Introspection:"
259 { $subsection "tuple-introspection" }
260 "Tuple classes can be redefined; this updates existing instances:"
261 { $subsection "tuple-redefinition" }
262 "Tuple literal syntax is documented in " { $link "syntax-tuples" } "." ;
263
264 ABOUT: "tuples"
265
266 HELP: tuple=
267 { $values { "tuple1" tuple } { "tuple2" tuple } { "?" "a boolean" } }
268 { $description "Low-level tuple equality test. User code should use " { $link = } " instead." }
269 { $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." } ;
270
271 HELP: tuple
272 { $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."
273 $nl
274 "Tuple classes have additional word properties:"
275 { $list
276     { { $snippet "\"constructor\"" } " - a word for creating instances of this tuple class" }
277     { { $snippet "\"predicate\"" } " - a quotation which tests if the top of the stack is an instance of this tuple class" }
278     { { $snippet "\"slots\"" } " - a sequence of " { $link slot-spec } " instances" }
279     { { $snippet "\"slot-names\"" } " - a sequence of strings naming the tuple's slots" }
280     { { $snippet "\"tuple-size\"" } " - the number of slots" }
281 } } ;
282
283 HELP: define-tuple-predicate
284 { $values { "class" tuple-class } }
285 { $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." }
286 $low-level-note ;
287
288 HELP: redefine-tuple-class
289 { $values { "class" class } { "superclass" class } { "slots" "a sequence of strings" } }
290 { $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."
291 $nl
292 "If the class is not a tuple class word, this word does nothing." }
293 $low-level-note ;
294
295 HELP: tuple-slots
296 { $values { "tuple" tuple } { "seq" sequence } }
297 { $description "Pushes a sequence of tuple slot values, not including the tuple class word." } ;
298
299 { tuple-slots tuple>array } related-words
300
301 HELP: define-tuple-slots
302 { $values { "class" tuple-class } }
303 { $description "Defines slot accessor and mutator words for the tuple." }
304 $low-level-note ;
305
306 HELP: check-tuple
307 { $values { "class" class } }
308 { $description "Throws a " { $link check-tuple } " error if " { $snippet "word" } " is not a tuple class word." }
309 { $error-description "Thrown if " { $link POSTPONE: C: } " is called with a word which does not name a tuple class." } ;
310
311 HELP: define-tuple-class
312 { $values { "class" word } { "superclass" class } { "slots" "a sequence of strings" } }
313 { $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: } "." }
314 { $notes "This word must be called from inside " { $link with-compilation-unit } "." }
315 { $side-effects "class" } ;
316
317 { tuple-class define-tuple-class POSTPONE: TUPLE: } related-words
318
319 HELP: >tuple
320 { $values { "seq" sequence } { "tuple" tuple } }
321 { $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."
322 $nl
323 "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 } "." }
324 { $errors "Throws an error if the first element of the sequence is not a tuple class word." } ;
325
326 HELP: tuple>array ( tuple -- array )
327 { $values { "tuple" tuple } { "array" array } }
328 { $description "Outputs an array having the tuple's slots as elements. The first element is the tuple class word and remainder are declared slots." } ;
329
330 HELP: <tuple> ( layout -- tuple )
331 { $values { "layout" tuple-layout } { "tuple" tuple } }
332 { $description "Low-level tuple constructor. User code should never call this directly, and instead use " { $link new } "." } ;
333
334 HELP: <tuple-boa> ( ... layout -- tuple )
335 { $values { "..." "values" } { "layout" tuple-layout } { "tuple" tuple } }
336 { $description "Low-level tuple constructor. User code should never call this directly, and instead use " { $link boa } "." } ;
337
338 HELP: new
339 { $values { "class" tuple-class } { "tuple" tuple } }
340 { $description "Creates a new instance of " { $snippet "class" } " with all slots initially set to " { $link f } "." }
341 { $examples
342     { $example
343         "USING: kernel prettyprint ;"
344         "IN: scratchpad"
345         "TUPLE: employee number name department ;"
346         "employee new ."
347         "T{ employee f f f f }"
348     }
349 } ;
350
351 HELP: construct
352 { $values { "..." "slot values" } { "slots" "a sequence of setter words" } { "class" tuple-class } { "tuple" tuple } }
353 { $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." }
354 { $examples
355     "We can define a class:"
356     { $code "TUPLE: color red green blue alpha ;" }
357     "Together with two constructors:"
358     { $code
359         ": <rgb> ( r g b -- color )"
360         "    { set-color-red set-color-green set-color-blue }"
361         "    color construct ;"
362         ""
363         ": <rgba> ( r g b a -- color )"
364         "    { set-color-red set-color-green set-color-blue set-color-alpha }"
365         "    color construct ;"
366     }
367     "The last definition is actually equivalent to the following:"
368     { $code ": <rgba> ( r g b a -- color ) rgba boa ;" }
369     "Which can be abbreviated further:"
370     { $code "C: <rgba> color" }
371 } ;
372
373 HELP: boa
374 { $values { "..." "slot values" } { "class" tuple-class } { "tuple" tuple } }
375 { $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." }
376 { $notes "The name " { $snippet "boa" } " is shorthand for ``by order of arguments'', and ``BOA constructor'' is a pun on ``boa constrictor''." } ;