]> gitweb.factorcode.org Git - factor.git/blob - core/locals/locals-docs.factor
locals-docs: clarify docs for mutable locals
[factor.git] / core / locals / locals-docs.factor
1 USING: help.syntax help.markup kernel
2 combinators arrays generalizations sequences ;
3 IN: locals
4
5 HELP: [|
6 { $syntax "[| bindings... | body... ]" }
7 { $description "A literal quotation with named variable bindings. When the quotation is " { $link call } "ed, it will take values off the datastack and place them into the bindings from left to right. The body may then refer to these bindings. The quotation may also bind to named variables in an enclosing scope to create a closure." }
8 { $examples "See " { $link "locals-examples" } "." } ;
9
10 HELP: [let
11 { $syntax "[let code :> var code :> var code... ]" }
12 { $description "Establishes a new scope for lexical variable bindings. Variables bound with " { $link POSTPONE: :> } " within the body of the " { $snippet "[let" } " will be lexically scoped to the body of the " { $snippet "[let" } " form." }
13 { $examples "See " { $link "locals-examples" } "." } ;
14
15 HELP: :>
16 { $syntax ":> var" ":> var!" ":> ( var-1 var-2 ... )" }
17 { $description "Binds one or more new lexical variables. In the " { $snippet ":> var" } " form, the value on the top of the datastack is bound to a new lexical variable named " { $snippet "var" } " and is scoped to the enclosing quotation, " { $link POSTPONE: [let } " form, or " { $link POSTPONE: :: } " definition."
18 $nl
19 "The " { $snippet ":> ( var-1 ... )" } " form binds multiple variables to the top values of the datastack in right to left order, with the last variable bound to the top of the datastack. These two snippets have the same effect:"
20 { $code ":> c :> b :> a" }
21 { $code ":> ( a b c )" }
22 $nl
23 "If any " { $snippet "var" } " name is followed by an exclamation point (" { $snippet "!" } "), that new variable is mutable. See " { $link "locals-mutable" } " for more information." }
24 { $notes
25     "This syntax can only be used inside a lexical scope established by a " { $link POSTPONE: :: } " definition, " { $link POSTPONE: [let } " form, or " { $link POSTPONE: [| } " quotation. Normal quotations have their own lexical scope only if they are inside an outer scope. Definition forms such as " { $link POSTPONE: : } " do not establish a lexical scope by themselves unless documented otherwise, nor is there a lexical scope available at the top level of source files or in the listener. " { $link POSTPONE: [let } " can be used to create a lexical scope where one is not otherwise available." }
26 { $examples "See " { $link "locals-examples" } "." } ;
27
28 { POSTPONE: [let POSTPONE: :> } related-words
29
30 HELP: ::
31 { $syntax ":: word ( vars... -- outputs... ) body... ;" }
32 { $description "Defines a word with named inputs. The word binds its input values to lexical variables from left to right, then executes the body with those bindings in scope."
33 $nl
34 "If any " { $snippet "var" } " name is followed by an exclamation point (" { $snippet "!" } "), the corresponding new variable is made mutable. See " { $link "locals-mutable" } " for more information." }
35 { $notes "The names of the " { $snippet "outputs" } " do not affect the word's behavior. However, the compiler verifies that the stack effect accurately represents the number of outputs as with " { $link POSTPONE: : } " definitions." }
36 { $examples "See " { $link "locals-examples" } "." } ;
37
38 { POSTPONE: : POSTPONE: :: } related-words
39
40 HELP: MACRO::
41 { $syntax "MACRO:: word ( vars... -- outputs... ) body... ;" }
42 { $description "Defines a macro with named inputs. The macro binds its input variables to lexical variables from left to right, then executes the body with those bindings in scope."
43 $nl
44 "If any " { $snippet "var" } " name is followed by an exclamation point (" { $snippet "!" } "), the corresponding new variable is made mutable. See " { $link "locals-mutable" } " for more information." }
45 { $notes "The expansion of a macro cannot reference lexical variables bound in the outer scope. There are also limitations on passing arguments involving lexical variables into macros. See " { $link "locals-limitations" } " for details." }
46 { $examples "See " { $link "locals-examples" } "." } ;
47
48 { POSTPONE: MACRO: POSTPONE: MACRO:: } related-words
49
50 HELP: MEMO::
51 { $syntax "MEMO:: word ( vars... -- outputs... ) body... ;" }
52 { $description "Defines a memoized word with named inputs. The word binds its input values to lexical variables from left to right, then executes the body with those bindings in scope."
53 $nl
54 "If any " { $snippet "var" } " name is followed by an exclamation point (" { $snippet "!" } "), the corresponding new variable is made mutable. See " { $link "locals-mutable" } " for more information." }
55 { $examples "See " { $link "locals-examples" } "." } ;
56
57 { POSTPONE: MEMO: POSTPONE: MEMO:: } related-words
58
59 HELP: M::
60 { $syntax "M:: class generic ( vars... -- outputs... ) body... ;" }
61 { $description "Defines a new method on " { $snippet "generic" } " for " { $snippet "class" } " with named inputs. The method binds its input values to lexical variables from left to right, then executes the body with those bindings in scope."
62 $nl
63 "If any " { $snippet "var" } " name is followed by an exclamation point (" { $snippet "!" } "), the corresponding new variable is made mutable. See " { $link "locals-mutable" } " for more information." }
64 { $notes "The names of the " { $snippet "outputs" } " do not affect the word's behavior. However, the compiler verifies that the stack effect accurately represents the number of outputs as with " { $link POSTPONE: M: } " definitions." }
65 { $examples "See " { $link "locals-examples" } "." } ;
66
67 { POSTPONE: M: POSTPONE: M:: } related-words
68
69 ARTICLE: "locals-examples" "Examples of lexical variables"
70 { $heading "Definitions with lexical variables" }
71 "The following example demonstrates lexical variable bindings in word definitions. The " { $snippet "quadratic-roots" } " word is defined with " { $link POSTPONE: :: } ", so it takes its inputs from the top three elements of the datastack and binds them to the variables " { $snippet "a" } ", " { $snippet "b" } ", and " { $snippet "c" } ". In the body, the " { $snippet "disc" } " variable is bound using " { $link POSTPONE: :> } " and then used in the following line of code."
72 { $example "USING: locals math math.functions kernel ;
73 IN: scratchpad
74 :: quadratic-roots ( a b c -- x y )
75     b sq 4 a c * * - sqrt :> disc
76     b neg disc [ + ] [ - ] 2bi [ 2 a * / ] bi@ ;
77 1.0 1.0 -6.0 quadratic-roots"
78 "\n--- Data stack:\n2.0\n-3.0"
79 }
80 "If you wanted to perform the quadratic formula interactively from the listener, you could use " { $link POSTPONE: [let } " to provide a scope for the variables:"
81 { $example "USING: locals math math.functions kernel ;
82 IN: scratchpad
83 [let 1.0 :> a 1.0 :> b -6.0 :> c
84     b sq 4 a c * * - sqrt :> disc
85     b neg disc [ + ] [ - ] 2bi [ 2 a * / ] bi@
86 ]"
87 "\n--- Data stack:\n2.0\n-3.0"
88 }
89
90 $nl
91
92 { $heading "Quotations with lexical variables, and closures" }
93 "These next two examples demonstrate lexical variable bindings in quotations defined with " { $link POSTPONE: [| } ". In this example, the values " { $snippet "5" } " and " { $snippet "3" } " are put on the datastack. When the quotation is called, it takes those values as inputs and binds them respectively to " { $snippet "m" } " and " { $snippet "n" } " before executing the quotation:"
94 { $example
95     "USING: kernel locals math ;"
96     "IN: scratchpad"
97     "5 3 [| m n | m n - ] call( x x -- x )"
98     "\n--- Data stack:\n2"
99 }
100 $nl
101
102 "In this example, the " { $snippet "adder" } " word creates a quotation that closes over its argument " { $snippet "n" } ". When called, the result quotation of " { $snippet "5 adder" } " pulls " { $snippet "3" } " off the datastack and binds it to " { $snippet "m" } ", which is added to the value " { $snippet "5" } " bound to " { $snippet "n" } " in the outer scope of " { $snippet "adder" } ":"
103 { $example
104     "USING: kernel locals math ;"
105     "IN: scratchpad"
106     ":: adder ( n -- quot ) [| m | m n + ] ;"
107     "3 5 adder call( x -- x )"
108     "\n--- Data stack:\n8"
109 }
110 $nl
111
112 { $heading "Mutable bindings" }
113 "This next example demonstrates closures and mutable variable bindings. The " { $snippet "<counter>" } " word outputs a tuple containing a pair of quotations that respectively increment and decrement an internal counter in the mutable " { $snippet "value" } " variable and then return the new value. The quotations close over the counter, so each invocation of the word gives new quotations with a new internal counter."
114 { $example
115 "USING: accessors locals kernel math ;
116 IN: scratchpad
117
118 TUPLE: counter adder subtractor ;
119
120 :: <counter> ( -- counter )
121     0 :> value!
122     counter new
123     [ value 1 + dup value! ] >>adder
124     [ value 1 - dup value! ] >>subtractor ;
125 <counter>
126 [ adder>>      call( -- x ) ]
127 [ adder>>      call( -- x ) ]
128 [ subtractor>> call( -- x ) ] tri"
129 "\n--- Data stack:\n1\n2\n1"
130 }
131     $nl
132     "The same variable name can be bound multiple times in the same scope. This is different from reassigning the value of a mutable variable. The most recent binding for a variable name will mask previous bindings for that name. However, the old binding referring to the previous value can still persist in closures. The following contrived example demonstrates this:"
133     { $example
134 "USING: kernel locals ;
135 IN: scratchpad
136 :: rebinding-example ( -- quot1 quot2 )
137     5 :> a [ a ]
138     6 :> a [ a ] ;
139 :: mutable-example ( -- quot1 quot2 )
140     5 :> a! [ a ]
141     6 a! [ a ] ;
142 rebinding-example [ call( -- x ) ] bi@
143 mutable-example [ call( -- x ) ] bi@"
144 "\n--- Data stack:\n5\n6\n6\n6"
145 }
146     "In " { $snippet "rebinding-example" } ", the binding of " { $snippet "a" } " to " { $snippet "5" } " is closed over in the first quotation, and the binding of " { $snippet "a" } " to " { $snippet "6" } " is closed over in the second, so calling both quotations results in " { $snippet "5" } " and " { $snippet "6" } " respectively. By contrast, in " { $snippet "mutable-example" } ", both quotations close over a single binding of " { $snippet "a" } ". Even though " { $snippet "a" } " is assigned to " { $snippet "6" } " after the first quotation is made, calling either quotation will output the new value of " { $snippet "a" } "."
147 { $heading "Lexical variables in literals" }
148 "Some kinds of literals can include references to lexical variables as described in " { $link "locals-literals" } ". For example, the " { $link 3array } " word could be implemented as follows:"
149 { $example
150 "USING: locals ;
151 IN: scratchpad
152
153 :: my-3array ( x y z -- array ) { x y z } ;
154 1 \"two\" 3.0 my-3array"
155 "\n--- Data stack:\n{ 1 \"two\" 3.0 }"
156 } ;
157
158 ARTICLE: "locals-literals" "Lexical variables in literals"
159 "Certain data type literals are permitted to contain lexical variables. Any such literals are rewritten into code which constructs an instance of the type with the values of the variables spliced in. Conceptually, this is similar to the transformation applied to quotations containing free variables."
160 $nl
161 "The data types which receive this special handling are the following:"
162 { $list
163     { $link "arrays" }
164     { $link "hashtables" }
165     { $link "vectors" }
166     { $link "tuples" }
167     { $link "wrappers" }
168 }
169 { $heading "Object identity" }
170 "This feature changes the semantics of literal object identity. An ordinary word containing a literal pushes the same literal on the stack every time it is invoked:"
171 { $example
172     "USING: kernel ;"
173     "IN: scratchpad"
174     "TUPLE: person first-name last-name ;"
175     ": ordinary-word-test ( -- tuple )"
176     "    T{ person { first-name \"Alan\" } { last-name \"Kay\" } } ;"
177     "ordinary-word-test ordinary-word-test eq?"
178     "\n--- Data stack:\nt"
179 }
180 "Inside a lexical scope, literals which do not contain lexical variables still behave in the same way:"
181 { $example
182     "USING: kernel locals ;"
183     "IN: scratchpad"
184     "TUPLE: person first-name last-name ;"
185     ":: locals-word-test ( -- tuple )"
186     "    T{ person { first-name \"Alan\" } { last-name \"Kay\" } } ;"
187     "locals-word-test locals-word-test eq?"
188     "\n--- Data stack:\nt"
189 }
190 "However, literals with lexical variables in them actually construct a new object:"
191 { $example
192     "USING: locals kernel splitting ;"
193     "IN: scratchpad"
194     "TUPLE: person first-name last-name ;"
195     ":: constructor-test ( -- tuple )"
196     "    \"Jane Smith\" \" \" split1 :> last :> first"
197     "    T{ person { first-name first } { last-name last } } ;"
198     "constructor-test constructor-test eq?"
199     "\n--- Data stack:\nf"
200 }
201 "One exception to the above rule is that array instances containing free lexical variables (that is, immutable lexical variables not referenced in a closure) do retain identity. This allows macros such as " { $link cond } " to expand at compile time even when their arguments reference variables." ;
202
203
204 ARTICLE: "locals-mutable" "Mutable lexical variables"
205 "When a lexical variable is bound using " { $link POSTPONE: :> } ", " { $link POSTPONE: :: } ", or " { $link POSTPONE: [| } ", the variable may be made mutable by suffixing its name with an exclamation point (" { $snippet "!" } ")."
206 $nl
207 "A mutable lexical variable creates two new words in its scope. Assuming that we define a mutable variable with " { $snippet "data :> var!" } ", then:"
208 $nl
209 { $snippet "var" } " will push the value of the variable, " { $snippet "data" } " to the stack,"
210 $nl
211 { $snippet "var!" } " will consume a value from the stack, and set the variable to that value."
212 $nl
213 "Note that using " { $link POSTPONE: :> } " will always create a new local, and will not mutate the variable. Creating a new local with the same name may cause confusion, and have undesired effects."
214 $nl
215 "The value of any variable can be modified by a word that modifies its arguments e.g. " { $link push } ". These words ignore mutable and immutable bindings."
216 $nl
217 "Mutable bindings are implemented in a manner similar to that taken by the ML language. Each mutable binding is actually an immutable binding of a mutable cell. Reading the binding automatically unboxes the value from the cell, and writing to the binding stores into it."
218 $nl
219 "Writing to mutable variables from outer lexical scopes is fully supported and has full closure semantics. See " { $link "locals-examples" } " for examples of mutable lexical variables in action." ;
220
221 ARTICLE: "locals-fry" "Lexical variables and fry"
222 "Lexical variables integrate with " { $link "fry" } " so that mixing variables with fried quotations gives intuitive results."
223 $nl
224 "The following two code snippets are equivalent:"
225 { $code "'[ sq _ + ]" }
226 { $code "[ [ sq ] dip + ] curry" }
227 "The semantics of " { $link dip } " and " { $link curry } " are such that the first example behaves as if the top of the stack as “inserted” in the “hole” in the quotation's second element."
228 $nl
229 "Conceptually, " { $link curry } " is defined so that the following two code snippets are equivalent:"
230 { $code "3 [ - ] curry" }
231 { $code "[ 3 - ]" }
232 "When quotations take named parameters using " { $link POSTPONE: [| } ", " { $link curry } " fills in the variable bindings from right to left. The following two snippets are equivalent:"
233 { $code "3 [| a b | a b - ] curry" }
234 { $code "[| a | a 3 - ]" }
235 "Because of this, the behavior of " { $snippet "fry" } " changes when applied to such a quotation to ensure that fry conceptually behaves the same as with normal quotations, placing the fried values “underneath” the variable bindings. Thus, the following snippets are no longer equivalent:"
236 { $code "'[ [| a | _ a - ] ]" }
237 { $code "'[ [| a | a - ] curry ] call" }
238 "Instead, the first line above expands into something like the following:"
239 { $code "[ [ swap [| a | a - ] ] curry call ]" }
240 $nl
241 "The precise behavior is as follows. When frying a " { $link POSTPONE: [| } " quotation, a stack shuffle (" { $link mnswap } ") is prepended so that the " { $snippet "m" } " curried values, which start off at the top of the stack, are transposed with the quotation's " { $snippet "n" } " named input bindings." ;
242
243 ARTICLE: "locals-limitations" "Limitations of lexical variables"
244 "There are two main limitations of the current implementation, and both concern macros."
245 { $heading "Macro expansions with free variables" }
246 "The expansion of a macro cannot reference lexical variables bound in the outer scope. For example, the following macro is invalid:"
247 { $code "MACRO:: twice ( quot -- ) [ quot call quot call ] ;" }
248 "The following is fine, though:"
249 { $code "MACRO:: twice ( quot -- ) quot quot '[ @ @ ] ;" }
250 { $heading "Static stack effect inference and macros" }
251 "A macro will only expand at compile-time if all of its inputs are literal. Likewise, the word containing the macro will only have a static stack effect and compile successfully if the macro's inputs are literal. When lexical variables are used in a macro's literal arguments, there is an additional restriction: The literals must immediately precede the macro call lexically."
252 $nl
253 "For example, all of the following three code snippets are superficially equivalent, but only the first will compile:"
254 { $code
255     ":: good-cond-usage ( a -- ... )"
256     "    {"
257     "        { [ a 0 < ] [ ... ] }"
258     "        { [ a 0 > ] [ ... ] }"
259     "        { [ a 0 = ] [ ... ] }"
260     "    } cond ;"
261 }
262 "The next two snippets will not compile because the argument to " { $link cond } " does not immediately precede the call:"
263 { $code
264     ": my-cond ( alist -- ) cond ; inline"
265     ""
266     ":: bad-cond-usage ( a -- ... )"
267     "    {"
268     "        { [ a 0 < ] [ ... ] }"
269     "        { [ a 0 > ] [ ... ] }"
270     "        { [ a 0 = ] [ ... ] }"
271     "    } my-cond ;"
272 }
273 { $code
274     ":: bad-cond-usage ( a -- ... )"
275     "    {"
276     "        { [ a 0 < ] [ ... ] }"
277     "        { [ a 0 > ] [ ... ] }"
278     "        { [ a 0 = ] [ ... ] }"
279     "    } swap swap cond ;"
280 }
281 "The reason is that lexical variable references are rewritten into stack code at parse time, whereas macro expansion is performed later during compile time. To circumvent this problem, the " { $vocab-link "macros.expander" } " vocabulary is used to rewrite simple macro usages prior to lexical variable transformation. However, " { $vocab-link "macros.expander" } " cannot deal with more complicated cases where the literal inputs to the macro do not immediately precede the macro call in the source." ;
282
283 ARTICLE: "locals" "Lexical variables"
284 "The " { $vocab-link "locals" } " vocabulary provides lexically scoped local variables. Full closure semantics, both downward and upward, are supported. Mutable variable bindings are also provided, supporting assignment to bindings in the current scope or in outer scopes."
285 { $subsections
286     "locals-examples"
287 }
288 "Word definitions where the inputs are bound to lexical variables:"
289 { $subsections
290     POSTPONE: ::
291     POSTPONE: M::
292     POSTPONE: MEMO::
293     POSTPONE: MACRO::
294 }
295 "Lexical scoping and binding forms:"
296 { $subsections
297     POSTPONE: [let
298     POSTPONE: :>
299 }
300 "Quotation literals where the inputs are bound to lexical variables:"
301 { $subsections POSTPONE: [| }
302 "Additional topics:"
303 { $subsections
304     "locals-literals"
305     "locals-mutable"
306     "locals-fry"
307     "locals-limitations"
308 }
309 "Lexical variables complement " { $link "namespaces" } "." ;
310
311 ABOUT: "locals"