]> gitweb.factorcode.org Git - factor.git/blob - basis/help/cookbook/cookbook.factor
add mention of run-file to scripting cookbook
[factor.git] / basis / help / cookbook / cookbook.factor
1 USING: help.markup help.syntax io kernel math parser
2 prettyprint sequences vocabs.loader namespaces stack-checker
3 help command-line see ;
4 IN: help.cookbook
5
6 ARTICLE: "cookbook-syntax" "Basic syntax cookbook"
7 "The following is a simple snippet of Factor code:"
8 { $example "10 sq 5 - ." "95" }
9 "You can click on it to evaluate it in the listener, and it will print the same output value as indicated above."
10 $nl
11 "Factor has a very simple syntax. Your program consists of " { $emphasis "words" } " and " { $emphasis "literals" } ". In the above snippet, the words are " { $link sq } ", " { $link - } " and " { $link . } ". The two integers 10 and 5 are literals."
12 $nl
13 "Factor evaluates code left to right, and stores intermediate values on a " { $emphasis "stack" } ". If you think of the stack as a pile of papers, then " { $emphasis "pushing" } " a value on the stack corresponds to placing a piece of paper at the top of the pile, while " { $emphasis "popping" } " a value corresponds to removing the topmost piece."
14 $nl
15 "All words have a " { $emphasis "stack effect declaration" } ", for example " { $snippet "( x y -- z )" } " denotes that a word takes two inputs, with " { $snippet "y" } " at the top of the stack, and returns one output. Stack effect declarations can be viewed by browsing source code, or using tools such as " { $link see } "; they are also checked by the compiler. See " { $link "effects" } "."
16 $nl
17 "Coming back to the example in the beginning of this article, the following series of steps occurs as the code is evaluated:"
18 { $table
19     { { $strong "Action" } { $strong "Stack contents" } }
20     { "10 is pushed on the stack." { $snippet "10" } }
21     { { "The " { $link sq } " word is executed. It pops one input from the stack (the integer 10) and squares it, pushing the result." } { $snippet "100" } }
22     { { "5 is pushed on the stack." } { $snippet "100 5" } }
23     { { "The " { $link - } " word is executed. It pops two inputs from the stack (the integers 100 and 5) and subtracts 5 from 100, pushing the result." } { $snippet "95" } }
24     { { "The " { $link . } " word is executed. It pops one input from the stack (the integer 95) and prints it in the listener's output area." } { } }
25 }
26 "Factor supports many other data types:"
27 { $code
28     "10.5"
29     "\"character strings\""
30     "{ 1 2 3 }"
31     "! by the way, this is a comment"
32 }
33 { $references
34     { "Factor's syntax can be extended, the parser can be called reflectively, and the " { $link . } " word is in fact a general facility for turning almost any object into a form which can be parsed back in again. If this interests you, consult the following sections:" }
35     "syntax"
36     "parser"
37     "prettyprint"
38 } ;
39
40 ARTICLE: "cookbook-colon-defs" "Shuffle word and definition cookbook"
41 "The " { $link dup } " word makes a copy of the value at the top of the stack:"
42 { $example "5 dup * ." "25" }
43 "The " { $link sq } " word is actually defined as follows:"
44 { $code ": sq ( x -- y ) dup * ;" }
45 "(You could have looked this up yourself by clicking on the " { $link sq } " word itself.)"
46 $nl
47 "Note the key elements in a word definition: The colon " { $link POSTPONE: : } " denotes the start of a word definition. The name of the new word and a stack effect declaration must immediately follow. The word definition then continues on until the " { $link POSTPONE: ; } " token signifies the end of the definition. This type of word definition is called a " { $emphasis "compound definition." }
48 $nl
49 "Factor is all about code reuse through short and logical colon definitions. Breaking up a problem into small pieces which are easy to test is called " { $emphasis "factoring." }
50 $nl
51 "Another example of a colon definition:"
52 { $code ": neg ( x -- -x ) 0 swap - ;" }
53 "Here the " { $link swap } " shuffle word is used to interchange the top two stack elements. Note the difference that " { $link swap } " makes in the following two snippets:"
54 { $code
55     "5 0 -       ! Computes 5-0"
56     "5 0 swap -  ! Computes 0-5"
57 }
58 "Also, in the above example a stack effect declaration is written between " { $snippet "(" } " and " { $snippet ")" } " with a mnemonic description of what the word does to the stack. See " { $link "effects" } " for details."
59 { $curious
60   "This syntax will be familiar to anybody who has used Forth before. However, unlike Forth, some additional static checks are performed. See " { $link "definition-checking" } " and " { $link "inference" } "."
61 }
62 { $references
63     { "A whole slew of shuffle words can be used to rearrange the stack. There are forms of word definition other than colon definition, words can be defined entirely at runtime, and word definitions can be " { $emphasis "annotated" } " with tracing calls and breakpoints without modifying the source code." }
64     "shuffle-words"
65     "words"
66     "generic"
67     "handbook-tools-reference"
68 } ;
69
70 ARTICLE: "cookbook-combinators" "Control flow cookbook"
71 "A " { $emphasis "quotation" } " is an object containing code which can be evaluated."
72 { $code
73     "2 2 + .     ! Prints 4"
74     "[ 2 2 + . ] ! Pushes a quotation"
75 }
76 "The quotation pushed by the second example will print 4 when called by " { $link call } "."
77 $nl
78 "Quotations are used to implement control flow. For example, conditional execution is done with " { $link if } ":"
79 { $code
80     ": sign-test ( n -- )"
81     "    dup 0 < ["
82     "        drop \"negative\""
83     "    ] ["
84     "        zero? [ \"zero\" ] [ \"positive\" ] if"
85     "    ] if print ;"
86 }
87 "The " { $link if } " word takes a boolean, a true quotation, and a false quotation, and executes one of the two quotations depending on the value of the boolean. In Factor, any object not equal to the special value " { $link f } " is considered true, while " { $link f } " is false."
88 $nl
89 "Another useful form of control flow is iteration. You can do something several times:"
90 { $code "10 [ \"Factor rocks!\" print ] times" }
91 "Now we can look at a new data type, the array:"
92 { $code "{ 1 2 3 }" }
93 "An array differs from a quotation in that it cannot be evaluated; it simply stores data."
94 $nl
95 "You can perform an operation on each element of an array:"
96 { $example
97     "{ 1 2 3 } [ \"The number is \" write . ] each"
98     "The number is 1\nThe number is 2\nThe number is 3"
99 }
100 "You can transform each element, collecting the results in a new array:"
101 { $example "{ 5 12 0 -12 -5 } [ sq ] map ." "{ 25 144 0 144 25 }" }
102 "You can create a new array, only containing elements which satisfy some condition:"
103 { $example
104     ": negative? ( n -- ? ) 0 < ;"
105     "{ -12 10 16 0 -1 -3 -9 } [ negative? ] filter ."
106     "{ -12 -1 -3 -9 }"
107 }
108 { $references
109     { "Since quotations are objects, they can be constructed and taken apart at will. You can write code that writes code. Arrays are just one of the various types of sequences, and the sequence operations such as " { $link each } " and " { $link map } " operate on all types of sequences. There are many more sequence iteration operations than the ones above, too." }
110     "combinators"
111     "sequences"
112 } ;
113
114 ARTICLE: "cookbook-variables" "Dynamic variables cookbook"
115 "A symbol is a word which pushes itself on the stack when executed. Try it:"
116 { $example "SYMBOL: foo" "foo ." "foo" }
117 "Before using a variable, you must define a symbol for it:"
118 { $code "SYMBOL: name" }
119 "Symbols can be passed to the " { $link get } " and " { $link set } " words to read and write variable values:"
120 { $unchecked-example "\"Slava\" name set" "name get print" "Slava" }
121 "If you set variables inside a " { $link with-scope } ", their values will be lost after leaving the scope:"
122 { $unchecked-example
123     ": print-name ( -- ) name get print ;"
124     "\"Slava\" name set"
125     "["
126     "    \"Diana\" name set"
127     "    \"There, the name is \" write  print-name"
128     "] with-scope"
129     "\"Here, the name is \" write  print-name"
130     "There, the name is Diana\nHere, the name is Slava"
131 }
132 { $references
133     "There is a lot more to be said about dynamically-scoped variables and namespaces."
134     "namespaces"
135 } ;
136
137 ARTICLE: "cookbook-vocabs" "Vocabularies cookbook"
138 "Rather than being in one flat list, words belong to vocabularies; every word is contained in exactly one. When parsing a word name, the parser searches through vocabularies. When working at the listener, a useful set of vocabularies is already available. In a source file, all used vocabularies must be imported."
139 $nl
140 "For example, a source file containing the following code will print a parse error if you try loading it:"
141 { $code "\"Hello world\" print" }
142 "The " { $link print } " word is contained inside the " { $vocab-link "io" } " vocabulary, which is available in the listener but must be explicitly added to the search path in source files:"
143 { $code
144     "USE: io"
145     "\"Hello world\" print"
146 }
147 "Typically a source file will refer to words in multiple vocabularies, and they can all be added to the search path in one go:"
148 { $code "USING: arrays kernel math ;" }
149 "New words go into the " { $vocab-link "scratchpad" } " vocabulary by default. You can change this with " { $link POSTPONE: IN: } ":"
150 { $code
151     "IN: time-machine"
152     ": time-travel ( when what -- ) frob fizz flap ;"
153 }
154 "Note that words must be defined before being referenced. The following is generally invalid:"
155 { $code
156     ": frob ( what -- ) accelerate particles ;"
157     ": accelerate ( -- ) accelerator on ;"
158     ": particles ( what -- ) [ (particles) ] each ;"
159 }
160 "You would have to place the first definition after the two others for the parser to accept the file. If you have a set of mutually recursive words, you can use " { $link POSTPONE: DEFER: } "."
161 { $references
162     { }
163     "word-search"
164     "words"
165     "parser"
166 } ;
167
168 ARTICLE: "cookbook-application" "Application cookbook"
169 "Vocabularies can define a main entry point:"
170 { $code "IN: game-of-life"
171 "..."
172 ": play-life ( -- ) ... ;"
173 ""
174 "MAIN: play-life"
175 }
176 "See " { $link POSTPONE: MAIN: } " for details. The " { $link run } " word loads a vocabulary if necessary, and calls its main entry point; try the following, it's fun:"
177 { $code "\"tetris\" run" }
178 "Factor can deploy stand-alone executables; they do not have any external dependencies and consist entirely of compiled native machine code:"
179 { $code "\"tetris\" deploy-tool" }
180 { $references
181     { }
182     "vocabs.loader"
183     "tools.deploy"
184     "ui.tools.deploy"
185     "cookbook-scripts"
186 } ;
187
188 ARTICLE: "cookbook-scripts" "Scripting cookbook"
189 "Factor can be used for command-line scripting on Unix-like systems."
190 $nl
191 "To run a script, simply pass it as an argument to the Factor executable:"
192 { $code "./factor cleanup.factor" }
193 "To test a script in the listener, you can use " { $link run-file } "." 
194 $nl
195 "The script may access command line arguments by inspecting the value of the " { $link command-line } " variable. It can also get its own path from the " { $link script } " variable."
196 { $heading "Example: ls" }
197 "Here is an example implementing a simplified version of the Unix " { $snippet "ls" } " command in Factor:"
198 { $code
199     "USING: command-line namespaces io io.files
200 io.pathnames tools.files sequences kernel ;
201
202 command-line get [
203     \".\" directory.
204 ] [
205     dup length 1 = [ first directory. ] [
206         [ [ nl write \":\" print ] [ directory. ] bi ] each
207     ] if
208 ] if-empty"
209 }
210 "You can put it in a file named " { $snippet "ls.factor" } ", and then run it, to list the " { $snippet "/usr/bin" } " directory for example:"
211 { $code "./factor ls.factor /usr/bin" }
212 { $heading "Example: grep" }
213 "The following is a more complicated example, implementing something like the Unix " { $snippet "grep" } " command:"
214 { $code "USING: kernel fry io io.files io.encodings.ascii sequences
215 regexp command-line namespaces ;
216 IN: grep
217
218 : grep-lines ( pattern -- )
219     '[ dup _ matches? [ print ] [ drop ] if ] each-line ;
220
221 : grep-file ( pattern filename -- )
222     ascii [ grep-lines ] with-file-reader ;
223
224 : grep-usage ( -- )
225     \"Usage: factor grep.factor <pattern> [<file>...]\" print ;
226
227 command-line get [
228     grep-usage
229 ] [
230     unclip <regexp> swap [
231         grep-lines
232     ] [
233         [ grep-file ] with each
234     ] if-empty
235 ] if-empty" }
236 "You can run it like so,"
237 { $code "./factor grep.factor '.*hello.*' myfile.txt" }
238 "You'll notice this script takes a while to start. This is because it is loading and compiling the " { $vocab-link "regexp" } " vocabulary every time. To speed up startup, load the vocabulary into your image, and save the image:"
239 { $code "USE: regexp" "save" }
240 "Now, the " { $snippet "grep.factor" } " script will start up much faster. See " { $link "images" } " for details."
241 { $heading "Executable scripts" }
242 "It is also possible to make executable scripts. A Factor file can begin with a 'shebang' like the following:"
243 { $code "#!/usr/bin/env factor" }
244 "If the text file is made executable, then it can be run, assuming the " { $snippet "factor" } " binary is in your " { $snippet "$PATH" } "."
245 { $references
246     { }
247     "command-line"
248     "cookbook-application"
249     "images"
250 } ;
251
252 ARTICLE: "cookbook-philosophy" "Factor philosophy"
253 "Learning a stack language is like learning to ride a bicycle: it takes a bit of practice and you might graze your knees a couple of times, but once you get the hang of it, it becomes second nature."
254 $nl
255 "The most common difficulty encountered by beginners is trouble reading and writing code as a result of trying to place too many values on the stack at a time."
256 $nl
257 "Keep the following guidelines in mind to avoid losing your sense of balance:"
258 { $list
259     "Simplify, simplify, simplify. Break your program up into small words which operate on a few values at a time. Most word definitions should fit on a single line; very rarely should they exceed two or three lines."
260     "In addition to keeping your words short, keep them meaningful. Give them good names, and make sure each word only does one thing. Try documenting your words; if the documentation for a word is unclear or complex, chances are the word definition is too. Don't be afraid to refactor your code."
261     "If your code looks repetitive, factor it some more."
262     "If after factoring, your code still looks repetitive, introduce combinators."
263     "If after introducing combinators, your code still looks repetitive, look into using meta-programming techniques."
264     "Try to place items on the stack in the order in which they are needed. If everything is in the correct order, no shuffling needs to be performed."
265     "If you find yourself writing a stack comment in the middle of a word, break the word up."
266     { "Use " { $link "cleave-combinators" } " and " { $link "spread-combinators" } " instead of " { $link "shuffle-words" } " to give your code more structure." }
267     { "Not everything has to go on the stack. The " { $vocab-link "namespaces" } " vocabulary provides dynamically-scoped variables, and the " { $vocab-link "locals" } " vocabulary provides lexically-scoped variables. Learn both and use them where they make sense, but keep in mind that overuse of variables makes code harder to factor." }
268     "Every time you define a word which simply manipulates sequences, hashtables or objects in an abstract way which is not related to your program domain, check the library to see if you can reuse an existing definition."
269     { "Write unit tests. Factor provides good support for unit testing; see " { $link "tools.test" } ". Once your program has a good test suite you can refactor with confidence and catch regressions early." }
270     "Don't write Factor as if it were C. Imperative programming and indexed loops are almost always not the most idiomatic solution."
271     { "Use sequences, assocs and objects to group related data. Object allocation is very cheap. Don't be afraid to create tuples, pairs and triples. Don't be afraid of operations which allocate new objects either, such as " { $link append } "." }
272     { "If you find yourself writing a loop with a sequence and an index, there's almost always a better way. Learn the " { $link "sequences-combinators" } " by heart." }
273     { "If you find yourself writing a heavily nested loop which performs several steps on each iteration, there is almost always a better way. Break the problem down into a series of passes over the data instead, gradually transforming it into the desired result with a series of simple loops. Factor the loops out and reuse them. If you're working on anything math-related, learn " { $link "math-vectors" } " by heart." }
274     { "If you find yourself wishing you could iterate over the datastack, or capture the contents of the datastack into a sequence, or push each element of a sequence onto the datastack, there is almost always a better way. Use " { $link "sequences" } " instead." }
275     "Don't use meta-programming if there's a simpler way."
276     "Don't worry about efficiency unless your program is too slow. Don't prefer complex code to simple code just because you feel it will be more efficient. The Factor compiler is designed to make idiomatic code run fast."
277     { "None of the above are hard-and-fast rules: there are exceptions to all of them. But one rule unconditionally holds: " { $emphasis "there is always a simpler way" } "." }
278 }
279 "Factor tries to implement as much of itself as possible, because this improves simplicity and performance. One consequence is that Factor exposes its internals for extension and study. You even have the option of using low-level features not usually found in high-level languages, such as manual memory management, pointer arithmetic, and inline assembly code."
280 $nl
281 "Unsafe features are tucked away so that you will not invoke them by accident, or have to use them to solve conventional programming problems. However when the need arises, unsafe features are invaluable, for example you might have to do some pointer arithmetic when interfacing directly with C libraries." ;
282
283 ARTICLE: "cookbook-pitfalls" "Pitfalls to avoid"
284 "Factor is a very clean and consistent language. However, it has some limitations and leaky abstractions you should keep in mind, as well as behaviors which differ from other languages you may be used to."
285 { $list
286     "Factor only makes use of one native thread, and Factor threads are scheduled co-operatively. C library calls block the entire VM."
287     "Factor does not hide anything from the programmer, all internals are exposed. It is your responsibility to avoid writing fragile code which depends too much on implementation detail."
288     { "If a literal object appears in a word definition, the object itself is pushed on the stack when the word executes, not a copy. If you intend to mutate this object, you must " { $link clone } " it first. See " { $link "syntax-literals" } "." }
289     { "Also, " { $link dup } " and related shuffle words don't copy entire objects or arrays; they only duplicate the reference to them. If you want to guard an object against mutation, use " { $link clone } "." }
290     { "For a discussion of potential issues surrounding the " { $link f } " object, see " { $link "booleans" } "." }
291     { "Factor's object system is quite flexible. Careless usage of union, mixin and predicate classes can lead to similar problems to those caused by “multiple inheritance” in other languages. In particular, it is possible to have two classes such that they have a non-empty intersection and yet neither is a subclass of the other. If a generic word defines methods on two such classes, various disambiguation rules are applied to ensure method dispatch remains deterministic, however they may not be what you expect. See " { $link "method-order" } " for details." }
292     { "If " { $link run-file } " throws a stack depth assertion, it means that the top-level form in the file left behind values on the stack. The stack depth is compared before and after loading a source file, since this type of situation is almost always an error. If you have a legitimate need to load a source file which returns data in some manner, define a word in the source file which produces this data on the stack and call the word after loading the file." }
293 } ;
294
295 ARTICLE: "cookbook-next" "Next steps"
296 "Once you have read through " { $link "first-program" } " and " { $link "cookbook" } ", the best way to keep learning Factor is to start looking at some simple example programs. Here are a few particularly nice vocabularies which should keep you busy for a little while:"
297 { $list
298     { $vocab-link "base64" }
299     { $vocab-link "roman" }
300     { $vocab-link "rot13" }
301     { $vocab-link "smtp" }
302     { $vocab-link "time-server" }
303     { $vocab-link "tools.hexdump" }
304     { $vocab-link "webapps.counter" }
305 }
306 "If you see code in there that you do not understand, use " { $link see } " and " { $link help } " to explore." ;
307
308 ARTICLE: "cookbook" "Factor cookbook"
309 "The Factor cookbook is a high-level overview of the most important concepts required to program in Factor."
310 { $subsections
311     "cookbook-syntax"
312     "cookbook-colon-defs"
313     "cookbook-combinators"
314     "cookbook-variables"
315     "cookbook-vocabs"
316     "cookbook-application"
317     "cookbook-scripts"
318     "cookbook-philosophy"
319     "cookbook-pitfalls"
320     "cookbook-next"
321 } ;
322
323 ABOUT: "cookbook"