]> gitweb.factorcode.org Git - factor.git/blobdiff - basis/help/cookbook/cookbook.factor
add mention of run-file to scripting cookbook
[factor.git] / basis / help / cookbook / cookbook.factor
index 9bb76f8d5a4767d73d973451d4c1b420ab7323c8..e4b753a961e7203fe9051870e5eb1506cf505cc7 100644 (file)
@@ -1,6 +1,6 @@
-USING: help.markup help.syntax io kernel math namespaces parser
+USING: help.markup help.syntax io kernel math parser
 prettyprint sequences vocabs.loader namespaces stack-checker
-help command-line multiline see ;
+help command-line see ;
 IN: help.cookbook
 
 ARTICLE: "cookbook-syntax" "Basic syntax cookbook"
@@ -12,16 +12,16 @@ $nl
 $nl
 "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."
 $nl
-"All words except those which only push literals on the stack must 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 "effect-declaration" } "."
+"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" } "."
 $nl
 "Coming back to the example in the beginning of this article, the following series of steps occurs as the code is evaluated:"
 { $table
-    { "Action" "Stack contents" }
+    { { $strong "Action" } { $strong "Stack contents" } }
     { "10 is pushed on the stack." { $snippet "10" } }
-    { { "The " { $link sq } " word is executed. It pops one input from the stack - the integer 10 - and squares it, pushing the result." } { $snippet "100" } }
+    { { "The " { $link sq } " word is executed. It pops one input from the stack (the integer 10) and squares it, pushing the result." } { $snippet "100" } }
     { { "5 is pushed on the stack." } { $snippet "100 5" } }
-    { { "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" } }
-    { { "The " { $link . } " word is executed. It pops one input from the stack - the integer 95 - and prints it in the listener's output area." } { } }
+    { { "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" } }
+    { { "The " { $link . } " word is executed. It pops one input from the stack (the integer 95) and prints it in the listener's output area." } { } }
 }
 "Factor supports many other data types:"
 { $code
@@ -29,7 +29,6 @@ $nl
     "\"character strings\""
     "{ 1 2 3 }"
     "! by the way, this is a comment"
-    "#! and so is this"
 }
 { $references
     { "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:" }
@@ -45,7 +44,7 @@ ARTICLE: "cookbook-colon-defs" "Shuffle word and definition cookbook"
 { $code ": sq ( x -- y ) dup * ;" }
 "(You could have looked this up yourself by clicking on the " { $link sq } " word itself.)"
 $nl
-"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 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." }
+"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." }
 $nl
 "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." }
 $nl
@@ -56,18 +55,9 @@ $nl
     "5 0 -       ! Computes 5-0"
     "5 0 swap -  ! Computes 0-5"
 }
-"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 "effect-declaration" } " for details."
+"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."
 { $curious
-    "This syntax will be familiar to anybody who has used Forth before. However the behavior is slightly different. In most Forth systems, the below code prints 2, because the definition of " { $snippet "b" } " still refers to the previous definition of " { $snippet "a" } ":"
-    { $code
-        ": a 1 ;"
-        ": b ( -- x ) a 1 + ;"
-        ": a 2 ;"
-        "b ."
-    }
-    "In Factor, this example will print 3 since word redefinition is explicitly supported."
-    $nl
-    "Indeed, redefining a word twice in the same source file is an error; this is almost always a mistake since there's no way to call the first definition. See " { $link "definition-checking" } "."
+  "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" } "."
 }
 { $references
     { "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." }
@@ -145,7 +135,7 @@ ARTICLE: "cookbook-variables" "Dynamic variables cookbook"
 } ;
 
 ARTICLE: "cookbook-vocabs" "Vocabularies cookbook"
-"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 the " { $emphasis "vocabulary search path" } ". When working at the listener, a useful set of vocabularies is already available. In a source file, all used vocabularies must be imported."
+"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."
 $nl
 "For example, a source file containing the following code will print a parse error if you try loading it:"
 { $code "\"Hello world\" print" }
@@ -163,65 +153,23 @@ $nl
 }
 "Note that words must be defined before being referenced. The following is generally invalid:"
 { $code
-    ": frob accelerate particles ;"
-    ": accelerate accelerator on ;"
-    ": particles [ (particles) ] each ;"
+    ": frob ( what -- ) accelerate particles ;"
+    ": accelerate ( -- ) accelerator on ;"
+    ": particles ( what -- ) [ (particles) ] each ;"
 }
-"You would have to place the first definition after the two others for the parser to accept the file."
+"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: } "."
 { $references
     { }
-    "vocabulary-search"
+    "word-search"
     "words"
     "parser"
 } ;
 
-ARTICLE: "cookbook-io" "Input and output cookbook"
-"Ask the user for their age, and print it back:"
-{ $code
-    "USING: io math.parser ;"
-    ": ask-age ( -- ) \"How old are you?\" print ;"
-    ": read-age ( -- n ) readln string>number ;"
-    ": print-age ( n -- )"
-    "    \"You are \" write"
-    "    number>string write"
-    "    \" years old.\" print ;"
-    ": example ( -- ) ask-age read-age print-age ;"
-    "example"
-}
-"Print the lines of a file in sorted order:"
-{ $code
-    "USING: io io.encodings.utf8 io.files sequences sorting ;"
-    "\"lines.txt\" utf8 file-lines natural-sort [ print ] each"
-}
-"Read 1024 bytes from a file:"
-{ $code
-    "USING: io io.encodings.binary io.files ;"
-    "\"data.bin\" binary [ 1024 read ] with-file-reader"
-}
-"Convert a file of 4-byte cells from little to big endian or vice versa, by directly mapping it into memory and operating on it with sequence words:"
-{ $code
-    "USING: accessors grouping io.files io.mmap.char kernel sequences ;"
-    "\"mydata.dat\" ["
-    "    4 <sliced-groups> [ reverse-here ] change-each"
-    "] with-mapped-char-file"
-}
-"Send some bytes to a remote host:"
-{ $code
-    "USING: io io.encodings.ascii io.sockets strings ;"
-    "\"myhost\" 1033 <inet> ascii"
-    "[ B{ 12 17 102 } write ] with-client"
-}
-{ $references
-    { }
-    "number-strings"
-    "io"
-} ;
-
 ARTICLE: "cookbook-application" "Application cookbook"
 "Vocabularies can define a main entry point:"
 { $code "IN: game-of-life"
 "..."
-": play-life ... ;"
+": play-life ( -- ) ... ;"
 ""
 "MAIN: play-life"
 }
@@ -242,26 +190,28 @@ ARTICLE: "cookbook-scripts" "Scripting cookbook"
 $nl
 "To run a script, simply pass it as an argument to the Factor executable:"
 { $code "./factor cleanup.factor" }
+"To test a script in the listener, you can use " { $link run-file } "." 
+$nl
 "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."
 { $heading "Example: ls" }
 "Here is an example implementing a simplified version of the Unix " { $snippet "ls" } " command in Factor:"
 { $code
-    <" USING: command-line namespaces io io.files
+    "USING: command-line namespaces io io.files
 io.pathnames tools.files sequences kernel ;
 
 command-line get [
-    current-directory get directory.
+    \".\" directory.
 ] [
     dup length 1 = [ first directory. ] [
-        [ [ nl write ":" print ] [ directory. ] bi ] each
+        [ [ nl write \":\" print ] [ directory. ] bi ] each
     ] if
-] if-empty">
+] if-empty"
 }
 "You can put it in a file named " { $snippet "ls.factor" } ", and then run it, to list the " { $snippet "/usr/bin" } " directory for example:"
 { $code "./factor ls.factor /usr/bin" }
 { $heading "Example: grep" }
 "The following is a more complicated example, implementing something like the Unix " { $snippet "grep" } " command:"
-{ $code <" USING: kernel fry io io.files io.encodings.ascii sequences
+{ $code "USING: kernel fry io io.files io.encodings.ascii sequences
 regexp command-line namespaces ;
 IN: grep
 
@@ -272,7 +222,7 @@ IN: grep
     ascii [ grep-lines ] with-file-reader ;
 
 : grep-usage ( -- )
-    "Usage: factor grep.factor <pattern> [<file>...]" print ;
+    \"Usage: factor grep.factor <pattern> [<file>...]\" print ;
 
 command-line get [
     grep-usage
@@ -282,21 +232,19 @@ command-line get [
     ] [
         [ grep-file ] with each
     ] if-empty
-] if-empty"> }
+] if-empty" }
 "You can run it like so,"
 { $code "./factor grep.factor '.*hello.*' myfile.txt" }
 "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:"
 { $code "USE: regexp" "save" }
 "Now, the " { $snippet "grep.factor" } " script will start up much faster. See " { $link "images" } " for details."
 { $heading "Executable scripts" }
-"It is also possible to make executable scripts. A Factor file can begin with a comment like the following:"
-{ $code "#! /usr/bin/env factor" }
+"It is also possible to make executable scripts. A Factor file can begin with a 'shebang' like the following:"
+{ $code "#!/usr/bin/env factor" }
 "If the text file is made executable, then it can be run, assuming the " { $snippet "factor" } " binary is in your " { $snippet "$PATH" } "."
-$nl
-"The space between " { $snippet "#!" } " and " { $snippet "/usr/bin/env" } " is necessary, since " { $link POSTPONE: #! } " is a parsing word, and a syntax error would otherwise result."
 { $references
     { }
-    "cli"
+    "command-line"
     "cookbook-application"
     "images"
 } ;
@@ -318,7 +266,6 @@ $nl
     { "Use " { $link "cleave-combinators" } " and " { $link "spread-combinators" } " instead of " { $link "shuffle-words" } " to give your code more structure." }
     { "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." }
     "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."
-    { "Learn to use the " { $link "inference" } " tool." }
     { "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." }
     "Don't write Factor as if it were C. Imperative programming and indexed loops are almost always not the most idiomatic solution."
     { "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 } "." }
@@ -329,26 +276,19 @@ $nl
     "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."
     { "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" } "." }
 }
-"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 manual memory management, pointer arithmetic, and inline assembly code."
+"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."
 $nl
 "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." ;
+
 ARTICLE: "cookbook-pitfalls" "Pitfalls to avoid"
 "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."
 { $list
     "Factor only makes use of one native thread, and Factor threads are scheduled co-operatively. C library calls block the entire VM."
     "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."
-    { "When a source file uses two vocabularies which define words with the same name, the order of the vocabularies in the " { $link POSTPONE: USE: } " or " { $link POSTPONE: USING: } " forms is important. The " { $link POSTPONE: QUALIFIED: } " word implements qualified naming, which can be used to resolve ambiguities." }
     { "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" } "." }
+    { "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 } "." }
     { "For a discussion of potential issues surrounding the " { $link f } " object, see " { $link "booleans" } "." }
     { "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." }
-    { "Performance-sensitive code should have a static stack effect so that it can be compiled by the optimizing word compiler, which generates more efficient code than the non-optimizing quotation compiler. See " { $link "inference" } " and " { $link "compiler" } "."
-    $nl
-    "This means that methods defined on performance sensitive, frequently-called core generic words such as " { $link nth } " should have static stack effects which are consistent with each other, since a generic word will only have a static stack effect if all methods do."
-    $nl
-    "Unit tests for the " { $vocab-link "stack-checker" } " vocabulary can be used to ensure that any methods your vocabulary defines on core generic words have static stack effects:"
-    { $code "\"stack-checker\" test" }
-    "In general, you should strive to write code with inferable stack effects, even for sections of a program which are not performance sensitive; the " { $link infer. } " tool together with the optimizing compiler's error reporting can catch many bugs ahead of time." }
-    { "Be careful when calling words which access variables from a " { $link make-assoc } " which constructs an assoc with arbitrary keys, since those keys might shadow variables." }
     { "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." }
 } ;
 
@@ -367,16 +307,17 @@ ARTICLE: "cookbook-next" "Next steps"
 
 ARTICLE: "cookbook" "Factor cookbook"
 "The Factor cookbook is a high-level overview of the most important concepts required to program in Factor."
-{ $subsection "cookbook-syntax" }
-{ $subsection "cookbook-colon-defs" }
-{ $subsection "cookbook-combinators" }
-{ $subsection "cookbook-variables" }
-{ $subsection "cookbook-vocabs" }
-{ $subsection "cookbook-io" }
-{ $subsection "cookbook-application" }
-{ $subsection "cookbook-scripts" }
-{ $subsection "cookbook-philosophy" }
-{ $subsection "cookbook-pitfalls" }
-{ $subsection "cookbook-next" } ;
+{ $subsections
+    "cookbook-syntax"
+    "cookbook-colon-defs"
+    "cookbook-combinators"
+    "cookbook-variables"
+    "cookbook-vocabs"
+    "cookbook-application"
+    "cookbook-scripts"
+    "cookbook-philosophy"
+    "cookbook-pitfalls"
+    "cookbook-next"
+} ;
 
 ABOUT: "cookbook"