]> gitweb.factorcode.org Git - factor.git/blob - basis/locals/locals-docs.factor
Delete empty unit tests files, remove 1- and 1+, reorder IN: lines in a lot of places...
[factor.git] / basis / locals / locals-docs.factor
1 USING: help.syntax help.markup kernel macros prettyprint
2 memoize combinators arrays generalizations see ;
3 IN: locals
4
5 HELP: [|
6 { $syntax "[| bindings... | body... ]" }
7 { $description "A lambda abstraction. When called, reads stack values into the bindings from left to right; the body may then refer to these bindings." }
8 { $examples
9     { $example
10         "USING: kernel locals math prettyprint ;"
11         "IN: scratchpad"
12         ":: adder ( n -- quot ) [| m | m n + ] ;"
13         "3 5 adder call ."
14         "8"
15     }
16 } ;
17
18 HELP: [let
19 { $syntax "[let | binding1 [ value1... ]\n       binding2 [ value2... ]\n       ... |\n    body... ]" }
20 { $description "Introduces a set of lexical bindings and evaluates the body. The values are evaluated in parallel, and may not refer to other bindings within the same " { $link POSTPONE: [let } " form; for Lisp programmers, this means that " { $link POSTPONE: [let } " is equivalent to the Lisp " { $snippet "let" } ", not " { $snippet "let*" } "." }
21 { $examples
22     { $example
23         "USING: kernel locals math math.functions prettyprint sequences ;"
24         "IN: scratchpad"
25         ":: frobnicate ( n seq -- newseq )"
26         "    [let | n' [ n 6 * ] |"
27         "        seq [ n' gcd nip ] map ] ;"
28         "6 { 36 14 } frobnicate ."
29         "{ 36 2 }"
30     }
31 } ;
32
33 HELP: [let*
34 { $syntax "[let* | binding1 [ value1... ]\n        binding2 [ value2... ]\n        ... |\n    body... ]" }
35 { $description "Introduces a set of lexical bindings and evaluates the body. The values are evaluated sequentially, and may refer to previous bindings from the same " { $link POSTPONE: [let* } " form; for Lisp programmers, this means that " { $link POSTPONE: [let* } " is equivalent to the Lisp " { $snippet "let*" } ", not " { $snippet "let" } "." }
36 { $examples
37     { $example
38         "USING: kernel locals math math.functions prettyprint sequences ;"
39         "IN: scratchpad"
40         ":: frobnicate ( n seq -- newseq )"
41         "    [let* | a [ n 3 + ]"
42         "            b [ a 4 * ] |"
43         "        seq [ b / ] map ] ;"
44         "1 { 32 48 } frobnicate ."
45         "{ 2 3 }"
46     }
47 } ;
48
49 { POSTPONE: [let POSTPONE: [let* } related-words
50
51 HELP: [wlet
52 { $syntax "[wlet | binding1 [ body1... ]\n        binding2 [ body2... ]\n        ... |\n     body... ]" }
53 { $description "Introduces a set of lexically-scoped non-recursive local functions. The bodies may not refer to other bindings within the same " { $link POSTPONE: [wlet } " form; for Lisp programmers, this means that Factor's " { $link POSTPONE: [wlet } " is equivalent to the Lisp " { $snippet "flet" } ", not " { $snippet "labels" } "." }
54 { $examples
55     { $example
56         "USING: locals math prettyprint sequences ;"
57         "IN: scratchpad"
58         ":: quuxify ( n seq -- newseq )"
59         "    [wlet | add-n [| m | m n + ] |"
60         "        seq [ add-n ] map ] ;"
61         "2 { 1 2 3 } quuxify ."
62         "{ 3 4 5 }"
63     }
64 } ;
65
66 HELP: :>
67 { $syntax ":> binding" }
68 { $description "Introduces a new binding, lexically scoped to the enclosing quotation or definition." }
69 { $notes
70     "This word can only be used inside a lambda word, lambda quotation or let binding form."
71     $nl
72     "Lambda and let forms are really just syntax sugar for " { $link POSTPONE: :> } "."
73     $nl
74     "Lambdas desugar as follows:"
75     { $code
76         "[| a b | a b + b / ]"
77         "[ :> b :> a a b + b / ]"
78     }
79     "Let forms desugar as follows:"
80     { $code
81         "[|let | x [ 10 random ] | { x x } ]"
82         "10 random :> x { x x }"
83     }
84 }
85 { $examples
86     { $code
87         "USING: locals math kernel ;"
88         "IN: scratchpad"
89         ":: quadratic ( a b c -- x y )"
90         "    b sq 4 a c * * - sqrt :> disc"
91         "    b neg disc [ + ] [ - ] 2bi [ 2 a * / ] bi@ ;"
92     }
93 } ;
94
95 HELP: ::
96 { $syntax ":: word ( bindings... -- outputs... ) body... ;" }
97 { $description "Defines a word with named inputs; it reads stack values into bindings from left to right, then executes the body with those bindings in lexical scope." }
98 { $notes "The output names do not affect the word's behavior, however the compiler attempts to check the stack effect as with other definitions." }
99 { $examples "See " { $link POSTPONE: [| } ", " { $link POSTPONE: [let } " and " { $link POSTPONE: [wlet } "." } ;
100
101 { POSTPONE: : POSTPONE: :: } related-words
102
103 HELP: MACRO::
104 { $syntax "MACRO:: word ( bindings... -- outputs... ) body... ;" }
105 { $description "Defines a macro with named inputs; it reads stack values into bindings from left to right, then executes the body with those bindings in lexical scope." }
106 { $notes "The output names do not affect the word's behavior, however the compiler attempts to check the stack effect as with other definitions." } ;
107
108 { POSTPONE: MACRO: POSTPONE: MACRO:: } related-words
109
110 HELP: MEMO::
111 { $syntax "MEMO:: word ( bindings... -- outputs... ) body... ;" }
112 { $description "Defines a memoized word with named inputs; it reads stack values into bindings from left to right, then executes the body with those bindings in lexical scope." } ;
113
114 { POSTPONE: MEMO: POSTPONE: MEMO:: } related-words
115                                           
116 HELP: M::
117 { $syntax "M:: class generic ( bindings... -- outputs... ) body... ;" }
118 { $description "Defines a method with named inputs; it reads stack values into bindings from left to right, then executes the body with those bindings in lexical scope." }
119 { $notes "The output names do not affect the word's behavior, however the compiler attempts to check the stack effect as with other definitions." } ;
120
121 { POSTPONE: M: POSTPONE: M:: } related-words
122
123                                                  
124 ARTICLE: "locals-literals" "Locals in literals"
125 "Certain data type literals are permitted to contain free variables. Any such literals are written into code which constructs an instance of the type with the free variable values spliced in. Conceptually, this is similar to the transformation applied to quotations containing free variables."
126 $nl
127 "The data types which receive this special handling are the following:"
128 { $list
129     { $link "arrays" }
130     { $link "hashtables" }
131     { $link "vectors" }
132     { $link "tuples" }
133     { $link "wrappers" }
134 }
135 { $heading "Object identity" }
136 "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:"
137 { $example
138     "IN: scratchpad"
139     "TUPLE: person first-name last-name ;"
140     ": ordinary-word-test ( -- tuple )"
141     "    T{ person { first-name \"Alan\" } { last-name \"Kay\" } } ;"
142     "ordinary-word-test ordinary-word-test eq? ."
143     "t"
144 }
145 "In a word with locals, literals which do not contain locals still behave in the same way:"
146 { $example
147     "USE: locals"
148     "IN: scratchpad"
149     "TUPLE: person first-name last-name ;"
150     ":: locals-word-test ( -- tuple )"
151     "    T{ person { first-name \"Alan\" } { last-name \"Kay\" } } ;"
152     "locals-word-test locals-word-test eq? ."
153     "t"
154 }
155 "However, literals with locals in them actually expand into code for constructing a new object:"
156 { $example
157     "USING: locals splitting ;"
158     "IN: scratchpad"
159     "TUPLE: person first-name last-name ;"
160     ":: constructor-test ( -- tuple )"
161     "    \"Jane Smith\" \" \" split1 :> last :> first"
162     "    T{ person { first-name first } { last-name last } } ;"
163     "constructor-test constructor-test eq? ."
164     "f"
165 }
166 "One exception to the above rule is that array instances containing no free variables do retain identity. This allows macros such as " { $link cond } " to recognize that the array is constant and expand at compile-time."
167 { $heading "Example" }
168 "Here is an implementation of the " { $link 3array } " word which uses this feature:"
169 { $code ":: 3array ( x y z -- array ) { x y z } ;" } ;
170
171 ARTICLE: "locals-mutable" "Mutable locals"
172 "In the list of bindings supplied to " { $link POSTPONE: :: } ", " { $link POSTPONE: [let } ", " { $link POSTPONE: [let* } " or " { $link POSTPONE: [| } ", a mutable binding may be introduced by suffixing its named with " { $snippet "!" } ". Mutable bindings are read by giving their name as usual; the suffix is not part of the binding's name. To write to a mutable binding, use the binding's name with the " { $snippet "!" } " suffix."
173 $nl
174 "Here is a example word which outputs a pair of quotations which increment and decrement an internal counter, and then return the new value. The quotations are closed over the counter and each invocation of the word yields new quotations with their unique internal counter:"
175 { $code
176     ":: counter ( -- )"
177     "    [let | value! [ 0 ] |"
178     "        [ value 1 + dup value! ]"
179     "        [ value 1 - dup value! ] ] ;"
180 }
181 "Mutable bindings are implemented in a manner similar to the ML language; each mutable binding is actually an immutable binding of a mutable cell (in Factor's case, a 1-element array); reading the binding automatically dereferences the array, and writing to the binding stores into the array."
182 $nl
183 "Unlike some languages such as Python and Java, writing to mutable locals in outer scopes is fully supported and has the expected semantics." ;
184
185 ARTICLE: "locals-fry" "Locals and fry"
186 "Locals integrate with " { $link "fry" } " so that mixing locals with fried quotations gives intuitive results."
187 $nl
188 "Recall that the following two code snippets are equivalent:"
189 { $code "'[ sq _ + ]" }
190 { $code "[ [ sq ] dip + ] curry" }
191 "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."
192 $nl
193 "Conceptually, " { $link curry } " is defined so that the following two code snippets are equivalent:"
194 { $code "3 [ - ] curry" }
195 { $code "[ 3 - ]" }
196 "With lambdas, " { $link curry } " behaves differently. Rather than prepending an element, it fills in named parameters from right to left. The following two snippets are equivalent:"
197 { $code "3 [| a b | a b - ] curry" }
198 { $code "[| a | a 3 - ]" }
199 "Because of this, the behavior of fry changes when applied to a lambda, to ensure that conceptually, fry behaves as with quotations. So the following snippets are no longer equivalent:"
200 { $code "'[ [| a | _ a - ] ]" }
201 { $code "'[ [| a | a - ] curry ] call" }
202 "Instead, the first line above expands into something like the following:"
203 { $code "[ [ swap [| a | a - ] ] curry call ]" }
204 "This ensures that the fried value appears “underneath” the local variable " { $snippet "a" } " when the quotation calls."
205 $nl
206 "The precise behavior is the following. When frying a lambda, a stack shuffle (" { $link mnswap } ") is prepended to the lambda so that the " { $snippet "m" } " curried values, which start off at the top of the stack, are transposed with the " { $snippet "n" } " inputs to the lambda." ;
207
208 ARTICLE: "locals-limitations" "Limitations of locals"
209 "There are two main limitations of the current locals implementation, and both concern macros."
210 { $heading "Macro expansions with free variables" }
211 "The expansion of a macro cannot reference local variables bound in the outer scope. For example, the following macro is invalid:"
212 { $code "MACRO:: twice ( quot -- ) [ quot call quot call ] ;" }
213 "The following is fine, though:"
214 { $code "MACRO:: twice ( quot -- ) quot quot '[ @ @ ] ;" }
215 { $heading "Static stack effect inference and macros" }
216 "Recall that a macro will only expand at compile-time, and the word containing it will only get a static stack effect, if all inputs to the macro are literal. When locals are used, there is an additional restriction; the literals must immediately precede the macro call, lexically."
217 $nl
218 "For example, all of the following three examples are equivalent semantically, but only the first will have a static stack effect and compile with the optimizing compiler:"
219 { $code
220     ":: good-cond-usage ( a -- ... )"
221     "    {"
222     "        { [ a 0 < ] [ ... ] }"
223     "        { [ a 0 > ] [ ... ] }"
224     "        { [ a 0 = ] [ ... ] }"
225     "    } cond ;"
226 }
227 "The following two will not, and will run slower as a result:"
228 { $code
229     ": my-cond ( alist -- ) cond ; inline"
230     ""
231     ":: bad-cond-usage ( a -- ... )"
232     "    {"
233     "        { [ a 0 < ] [ ... ] }"
234     "        { [ a 0 > ] [ ... ] }"
235     "        { [ a 0 = ] [ ... ] }"
236     "    } my-cond ;"
237 }
238 { $code
239     ":: bad-cond-usage ( a -- ... )"
240     "    {"
241     "        { [ a 0 < ] [ ... ] }"
242     "        { [ a 0 > ] [ ... ] }"
243     "        { [ a 0 = ] [ ... ] }"
244     "    } swap swap cond ;"
245 }
246 "The reason is that locals 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 local transformation, however "{ $vocab-link "macros.expander" } " does not deal with more complicated cases where the literal inputs to the macro do not immediately precede the macro call in the source." ;
247
248 ARTICLE: "locals" "Lexical variables and closures"
249 "The " { $vocab-link "locals" } " vocabulary implements lexical scope with full closures, both downward and upward. Mutable bindings are supported, including assignment to bindings in outer scope."
250 $nl
251 "Compile-time transformation is used to compile local variables to efficient code; prettyprinter extensions are defined so that " { $link see } " can display original word definitions with local variables and not the closure-converted concatenative code which results."
252 $nl
253 "Applicative word definitions where the inputs are named local variables:"
254 { $subsection POSTPONE: :: }
255 { $subsection POSTPONE: M:: }
256 { $subsection POSTPONE: MEMO:: }
257 { $subsection POSTPONE: MACRO:: }
258 "Lexical binding forms:"
259 { $subsection POSTPONE: [let }
260 { $subsection POSTPONE: [let* }
261 { $subsection POSTPONE: [wlet }
262 "Lambda abstractions:"
263 { $subsection POSTPONE: [| }
264 "Lightweight binding form:"
265 { $subsection POSTPONE: :> }
266 "Additional topics:"
267 { $subsection "locals-literals" }
268 { $subsection "locals-mutable" }
269 { $subsection "locals-fry" }
270 { $subsection "locals-limitations" }
271 "Locals complement dynamically scoped variables implemented in the " { $vocab-link "namespaces" } " vocabulary." ;
272
273 ABOUT: "locals"