]> gitweb.factorcode.org Git - factor.git/blobdiff - core/kernel/kernel-docs.factor
Merge OneEyed's patch
[factor.git] / core / kernel / kernel-docs.factor
index 01ef8d480da6071fdcd162ac79fbefa561519d94..9c5d6f56ea22a4642683575dd715dba38e0afaed 100644 (file)
@@ -57,6 +57,7 @@ HELP: clear
 { $description "Clears the data stack." } ;
 
 HELP: build
+{ $values { "n" integer } }
 { $description "The current build number. Factor increments this number whenever a new boot image is created." } ;
 
 HELP: hashcode*
@@ -359,6 +360,17 @@ HELP: 2bi*
     }
 } ;
 
+HELP: 2tri*
+{ $values { "u" object } { "v" object } { "w" object } { "x" object } { "y" object } { "z" object } { "p" { $quotation "( u v -- ... )" } } { "q" { $quotation "( w x -- ... )" } } { "r" { $quotation "( y z -- ... )" } } }
+{ $description "Applies " { $snippet "p" } " to " { $snippet "u" } " and " { $snippet "v" } ", then applies " { $snippet "q" } " to " { $snippet "w" } " and " { $snippet "x" } ", and finally applies " { $snippet "r" } " to " { $snippet "y" } " and " { $snippet "z" } "." }
+{ $examples
+    "The following two lines are equivalent:"
+    { $code
+        "[ p ] [ q ] [ r ] 2tri*"
+        "[ [ p ] 2dip q ] 2dip r"
+    }
+} ;
+
 HELP: tri*
 { $values { "x" object } { "y" object } { "z" object } { "p" { $quotation "( x -- ... )" } } { "q" { $quotation "( y -- ... )" } } { "r" { $quotation "( z -- ... )" } } }
 { $description "Applies " { $snippet "p" } " to " { $snippet "x" } ", then applies " { $snippet "q" } " to " { $snippet "y" } ", and finally applies " { $snippet "r" } " to " { $snippet "z" } "." }
@@ -418,6 +430,149 @@ HELP: tri@
     }
 } ;
 
+HELP: 2tri@
+{ $values { "u" object } { "v" object } { "w" object } { "x" object } { "y" object } { "z" object } { "quot" { $quotation "( obj1 obj2 -- ... )" } } }
+{ $description "Applies the quotation to " { $snippet "u" } " and " { $snippet "v" } ", then to " { $snippet "w" } " and " { $snippet "x" } ", and then to " { $snippet "y" } " and " { $snippet "z" } "." }
+{ $examples
+    "The following two lines are equivalent:"
+    { $code
+        "[ p ] 2tri@"
+        "[ [ p ] 2dip p ] 2dip p"
+    }
+    "The following two lines are also equivalent:"
+    { $code
+        "[ p ] 2tri@"
+        "[ p ] [ p ] [ p ] 2tri*"
+    }
+} ;
+
+HELP: bi-curry
+{ $values { "x" object } { "p" { $quotation "( x -- ... )" } } { "q" { $quotation "( x -- ... )" } } { "p'" { $snippet "[ x p ]" } } { "q'" { $snippet "[ x q ]" } } }
+{ $description "Partially applies " { $snippet "p" } " and " { $snippet "q" } " to " { $snippet "x" } "." }
+{ $notes
+  "The following two lines are equivalent:"
+  { $code
+    "[ p ] [ q ] bi-curry [ call ] bi@"
+    "[ p ] [ q ] bi"
+  }
+  "Higher-arity variants of " { $link bi } " can be built from " { $link bi-curry } ":"
+  { $code
+    "[ p ] [ q ] bi-curry bi == [ p ] [ q ] 2bi"
+    "[ p ] [ q ] bi-curry bi-curry bi == [ p ] [ q ] 3bi"
+  }
+  "The combination " { $snippet "bi-curry bi*" } " cannot be expressed with the non-currying dataflow combinators alone; it is equivalent to a stack shuffle preceding " { $link 2bi* } ":"
+  { $code
+    "[ p ] [ q ] bi-curry bi*"
+    "[ swap ] keep [ p ] [ q ] 2bi*"
+  }
+  "To put it another way, " { $snippet "bi-curry bi*" } " handles the case where you have three values " { $snippet "a b c" } " on the stack, and you wish to apply " { $snippet "p" } " to " { $snippet "a c" } " and " { $snippet "q" } " to " { $snippet "b c" } "."
+} ;
+
+HELP: tri-curry
+{ $values
+  { "x" object }
+  { "p" { $quotation "( x -- ... )" } }
+  { "q" { $quotation "( x -- ... )" } }
+  { "r" { $quotation "( x -- ... )" } }
+  { "p'" { $snippet "[ x p ]" } }
+  { "q'" { $snippet "[ x q ]" } }
+  { "r'" { $snippet "[ x r ]" } }
+}
+{ $description "Partially applies " { $snippet "p" } ", " { $snippet "q" } " and " { $snippet "r" } " to " { $snippet "x" } "." }
+{ $notes
+  "The following two lines are equivalent:"
+  { $code
+    "[ p ] [ q ] [ r ] tri-curry [ call ] tri@"
+    "[ p ] [ q ] [ r ] tri"
+  }
+  "Higher-arity variants of " { $link tri } " can be built from " { $link tri-curry } ":"
+  { $code
+    "[ p ] [ q ] [ r ] tri-curry tri == [ p ] [ q ] [ r ] 2tri"
+    "[ p ] [ q ] [ r ] tri-curry tri-curry bi == [ p ] [ q ] [ r ] 3tri"
+  }
+  "The combination " { $snippet "tri-curry tri*" } " cannot be expressed with the non-currying dataflow combinators alone; it handles the case where you have four values " { $snippet "a b c d" } " on the stack, and you wish to apply " { $snippet "p" } " to " { $snippet "a d" } ", " { $snippet "q" } " to " { $snippet "b d" } " and " { $snippet "r" } " to " { $snippet "c d" } "." } ;
+
+HELP: bi-curry*
+{ $values { "x" object } { "y" object } { "p" { $quotation "( x -- ... )" } } { "q" { $quotation "( y -- ... )" } } { "p'" { $snippet "[ x p ]" } } { "q'" { $snippet "[ y q ]" } } }
+{ $description "Partially applies " { $snippet "p" } " to " { $snippet "x" } ", and " { $snippet "q" } " to " { $snippet "y" } "." }
+{ $notes
+  "The following two lines are equivalent:"
+  { $code
+    "[ p ] [ q ] bi-curry* [ call ] bi@"
+    "[ p ] [ q ] bi*"
+  }
+  "The combination " { $snippet "bi-curry* bi" } " is equivalent to a stack shuffle preceding " { $link 2bi* } ":"
+  { $code
+    "[ p ] [ q ] bi-curry* bi"
+    "[ over ] dip [ p ] [ q ] 2bi*"
+  }
+  "In other words, " { $snippet "bi-curry* bi" } " handles the case where you have the three values " { $snippet "a b c" } " on the stack, and you wish to apply " { $snippet "p" } " to " { $snippet "a b" } " and " { $snippet "q" } " to " { $snippet "a c" } "."
+  $nl
+  "The combination " { $snippet "bi-curry* bi*" } " is equivalent to a stack shuffle preceding " { $link 2bi* } ":"
+  { $code
+    "[ p ] [ q ] bi-curry* bi*"
+    "[ swap ] dip [ p ] [ q ] 2bi*"
+  }
+  "In other words, " { $snippet "bi-curry* bi*" } " handles the case where you have the four values " { $snippet "a b c d" } " on the stack, and you wish to apply " { $snippet "p" } " to " { $snippet "a c" } " and " { $snippet "q" } " to " { $snippet "b d" } "."
+  
+} ;
+
+HELP: tri-curry*
+{ $values
+  { "x" object }
+  { "y" object }
+  { "z" object }
+  { "p" { $quotation "( x -- ... )" } }
+  { "q" { $quotation "( y -- ... )" } }
+  { "r" { $quotation "( z -- ... )" } }
+  { "p'" { $snippet "[ x p ]" } }
+  { "q'" { $snippet "[ y q ]" } }
+  { "r'" { $snippet "[ z r ]" } }
+}
+{ $description "Partially applies " { $snippet "p" } " to " { $snippet "x" } ", " { $snippet "q" } " to " { $snippet "y" } " and " { $snippet "r" } " to " { $snippet "z" } "." }
+{ $notes
+  "The following two lines are equivalent:"
+  { $code
+    "[ p ] [ q ] [ r ] tri-curry* [ call ] tri@"
+    "[ p ] [ q ] [ r ] tri*"
+  }
+  "The combination " { $snippet "tri-curry* tri" } " is equivalent to a stack shuffle preceding " { $link 2tri* } ":"
+  { $code
+    "[ p ] [ q ] [ r ] tri-curry* tri"
+    "[ [ over ] dip over ] dip [ p ] [ q ] [ r ] 2tri*"
+  }
+} ;
+
+HELP: bi-curry@
+{ $values { "x" object } { "y" object } { "q" { $quotation "( obj -- ... )" } } { "p'" { $snippet "[ x q ]" } } { "q'" { $snippet "[ y q ]" } } }
+{ $description "Partially applies " { $snippet "q" } " to " { $snippet "x" } " and " { $snippet "y" } "." }
+{ $notes
+  "The following two lines are equivalent:"
+  { $code
+    "[ q ] bi-curry@"
+    "[ q ] [ q ] bi-curry*"
+  }
+} ;
+
+HELP: tri-curry@
+{ $values
+  { "x" object }
+  { "y" object }
+  { "z" object }
+  { "q" { $quotation "( obj -- ... )" } }
+  { "p'" { $snippet "[ x q ]" } }
+  { "q'" { $snippet "[ y q ]" } }
+  { "r'" { $snippet "[ z q ]" } }
+}
+{ $description "Partially applies " { $snippet "q" } " to " { $snippet "x" } ", " { $snippet "y" } " and " { $snippet "z" } "." }
+{ $notes
+  "The following two lines are equivalent:"
+  { $code
+    "[ q ] tri-curry@"
+    "[ q ] [ q ] [ q ] tri-curry*"
+  }
+} ;
+
 HELP: if
 { $values { "?" "a generalized boolean" } { "true" quotation } { "false" quotation } }
 { $description "If " { $snippet "cond" } " is " { $link f } ", calls the " { $snippet "false" } " quotation. Otherwise calls the " { $snippet "true" } " quotation."
@@ -595,23 +750,31 @@ HELP: 2dip
 
 HELP: 3dip
 { $values { "x" object } { "y" object } { "z" object } { "quot" quotation } }
-{ $description "Calls " { $snippet "quot" } " with " { $snippet "obj1" } ", " { $snippet "obj2" } " and " { $snippet "obj3" } " hidden on the retain stack." }
+{ $description "Calls " { $snippet "quot" } " with " { $snippet "x" } ", " { $snippet "y" } " and " { $snippet "z" } " hidden on the retain stack." }
 { $notes "The following are equivalent:"
     { $code "[ [ [ foo bar ] dip ] dip ] dip" }
     { $code "[ foo bar ] 3dip" }
 } ;
 
+HELP: 4dip
+{ $values { "w" object } { "x" object } { "y" object } { "z" object } { "quot" quotation } }
+{ $description "Calls " { $snippet "quot" } " with " { $snippet "w" } ", " { $snippet "x" } ", " { $snippet "y" } " and " { $snippet "z" } " hidden on the retain stack." }
+{ $notes "The following are equivalent:"
+    { $code "[ [ [ [ foo bar ] dip ] dip ] dip ] dip" }
+    { $code "[ foo bar ] 4dip" }
+} ;
+
 HELP: while
-{ $values { "pred" { $quotation "( -- ? )" } } { "body" "a quotation" } { "tail" "a quotation" } }
-{ $description "Repeatedly calls " { $snippet "pred" } ". If it yields " { $link f } ", iteration stops, otherwise " { $snippet "body" } " is called. After iteration stops, " { $snippet "tail" } " is called." }
-{ $notes "In most cases, tail recursion should be used, because it is simpler both in terms of implementation and conceptually. However in some cases this combinator expresses intent better and should be used."
-$nl
-"Strictly speaking, the " { $snippet "tail" } " is not necessary, since the following are equivalent:"
-{ $code
-    "[ P ] [ Q ] [ T ] while"
-    "[ P ] [ Q ] [ ] while T"
-}
-"However, depending on the stack effects of " { $snippet "pred" } " and " { $snippet "quot" } ", the " { $snippet "tail" } " quotation might need to be non-empty in order to balance out the stack effect of branches for stack effect inference." } ;
+{ $values { "pred" { $quotation "( -- ? )" } } { "body" "a quotation" } }
+{ $description "Calls " { $snippet "body" } " until " { $snippet "pred" } " returns " { $link f } "." } ;
+
+HELP: until
+{ $values { "pred" { $quotation "( -- ? )" } } { "body" "a quotation" } }
+{ $description "Calls " { $snippet "body" } " until " { $snippet "pred" } " returns " { $link t } "." } ;
+
+HELP: do
+{ $values { "pred" { $quotation "( -- ? )" } } { "body" "a quotation" } }
+{ $description "Executes one iteration of a " { $link while } " or " { $link until } " loop." } ;
 
 HELP: loop
 { $values
@@ -623,10 +786,23 @@ HELP: loop
     "hi hi hi" }
     "A fun loop:"
     { $example "USING: kernel prettyprint math ; "
-    "3 [ dup . 7 + 11 mod dup 3 = not ] loop"
+    "3 [ dup . 7 + 11 mod dup 3 = not ] loop drop"
     "3\n10\n6\n2\n9\n5\n1\n8\n4\n0\n7" }
 } ;
 
+ARTICLE: "looping-combinators" "Looping combinators"
+"In most cases, loops should be written using high-level combinators (such as " { $link "sequences-combinators" } ") or tail recursion. However, sometimes, the best way to express intent is with a loop."
+{ $subsection while }
+{ $subsection until }
+"To execute one iteration of a loop, use the following word:"
+{ $subsection do }
+"This word is intended as a modifier. The normal " { $link while } " loop never executes the body if the predicate returns first on the first iteration. To ensure the body executes at least once, use " { $link do } ":"
+{ $code
+    "[ P ] [ Q ] do while"
+}
+"A simpler looping combinator which executes a single quotation until it returns " { $link f } ":"
+{ $subsection loop } ;
+
 HELP: assert
 { $values { "got" "the obtained value" } { "expect" "the expected value" } }
 { $description "Throws an " { $link assert } " error." }
@@ -636,11 +812,10 @@ HELP: assert=
 { $values { "a" object } { "b" object } }
 { $description "Throws an " { $link assert } " error if " { $snippet "a" } " does not equal " { $snippet "b" } "." } ;
 
-
 ARTICLE: "shuffle-words" "Shuffle words"
 "Shuffle words rearrange items at the top of the data stack. They control the flow of data between words that perform actions."
 $nl
-"The " { $link "cleave-combinators" } " and " { $link "spread-combinators" } " are closely related to shuffle words and should be used instead where possible because they can result in clearer code; also, see the advice in " { $link "cookbook-philosophy" } "."
+"The " { $link "cleave-combinators" } ", " { $link "spread-combinators" } " and " { $link "apply-combinators" } " are closely related to shuffle words and should be used instead where possible because they can result in clearer code; also, see the advice in " { $link "cookbook-philosophy" } "."
 $nl
 "Removing stack elements:"
 { $subsection drop }
@@ -715,7 +890,7 @@ $nl
 { $subsection "cleave-shuffle-equivalence" } ;
 
 ARTICLE: "spread-shuffle-equivalence" "Expressing shuffle words with spread combinators"
-"Spread combinators are defined in terms of shuffle words, and mappings from certain shuffle idioms to spread combinators are discussed in the documentation for " { $link bi* } ", " { $link 2bi* } ", and " { $link tri* } "."
+"Spread combinators are defined in terms of shuffle words, and mappings from certain shuffle idioms to spread combinators are discussed in the documentation for " { $link bi* } ", " { $link 2bi* } ", " { $link tri* } ", and " { $link 2tri* } "."
 $nl
 "Certain shuffle words can also be expressed in terms of the spread combinators. Internalizing such identities can help with understanding and writing code using spread combinators:"
 { $code
@@ -755,6 +930,7 @@ $nl
 { $subsection 2bi* }
 "Three quotations:"
 { $subsection tri* }
+{ $subsection 2tri* }
 "Technically, the spread combinators are redundant because they can be simulated using shuffle words and other combinators, and in addition, they do not reduce token counts by much, if at all. However, they can make code more readable by expressing intention and exploiting any inherent symmetry. For example, a piece of code which performs three operations on three related values can be written in one of two ways:"
 { $code
     "! First alternative; uses dip"
@@ -773,6 +949,7 @@ $nl
 { $subsection 2bi@ }
 "Three quotations:"
 { $subsection tri@ }
+{ $subsection 2tri@ }
 "A pair of utility words built from " { $link bi@ } ":"
 { $subsection both? }
 { $subsection either? } ;
@@ -784,6 +961,7 @@ $nl
 { $subsection dip }
 { $subsection 2dip }
 { $subsection 3dip }
+{ $subsection 4dip }
 "The slip combinators invoke a quotation further down on the stack. They are most useful for implementing other combinators:"
 { $subsection slip }
 { $subsection 2slip }
@@ -793,15 +971,61 @@ $nl
 { $subsection 2keep }
 { $subsection 3keep } ;
 
+ARTICLE: "curried-dataflow" "Curried dataflow combinators"
+"Curried cleave combinators:"
+{ $subsection bi-curry }
+{ $subsection tri-curry }
+"Curried spread combinators:"
+{ $subsection bi-curry* }
+{ $subsection tri-curry* }
+"Curried apply combinators:"
+{ $subsection bi-curry@ }
+{ $subsection tri-curry@ }
+{ $see-also "dataflow-combinators" } ;
+
+ARTICLE: "compositional-examples" "Examples of compositional combinator usage"
+"Consider printing the same message ten times:"
+{ $code ": print-10 ( -- ) 10 [ \"Hello, world.\" print ] times ;" }
+"if we wanted to abstract out the message into a parameter, we could keep it on the stack between iterations:"
+{ $code ": print-10 ( message -- ) 10 [ dup print ] times drop ;" }
+"However, keeping loop-invariant values on the stack doesn't always work out nicely. For example, a word to subtract a value from each element of a sequence:"
+{ $code ": subtract-n ( seq n -- seq' ) swap [ over - ] map nip ;" }
+"Three shuffle words are required to pass the value around. Instead, the loop-invariant value can be partially applied to a quotation using " { $link curry } ", yielding a new quotation that is passed to " { $link map } ":"
+{ $example
+  "USING: kernel math prettyprint sequences ;"
+  ": subtract-n ( seq n -- seq' ) [ - ] curry map ;"
+  "{ 10 20 30 } 5 subtract-n ."
+  "{ 5 15 25 }"
+}
+"Now consider the word that is dual to the one above; instead of subtracting " { $snippet "n" } " from each stack element, it subtracts each element from " { $snippet "n" } "."
+$nl
+"One way to write this is with a pair of " { $link swap } "s:"
+{ $code ": n-subtract ( n seq -- seq' ) swap [ swap - ] curry map ;" }
+"Since this pattern comes up often, " { $link with } " encapsulates it:"
+{ $example
+  "USING: kernel math prettyprint sequences ;"
+  ": n-subtract ( n seq -- seq' ) [ - ] with map ;"
+  "30 { 10 20 30 } n-subtract ."
+  "{ 20 10 0 }"
+}
+{ $see-also "fry.examples" } ;
+
 ARTICLE: "compositional-combinators" "Compositional combinators"
-"Quotations can be composed using efficient quotation-specific operations:"
+"Certain combinators transform quotations to produce a new quotation."
+{ $subsection "compositional-examples" }
+"Fundamental operations:"
 { $subsection curry }
+{ $subsection compose }
+"Derived operations:"
 { $subsection 2curry }
 { $subsection 3curry }
 { $subsection with }
-{ $subsection compose }
 { $subsection prepose }
-"Quotations also implement the sequence protocol, and can be manipulated with sequence words; see " { $link "quotations" } "." ;
+"These operations run in constant time, and in many cases are optimized out altogether by the " { $link "compiler" } ". " { $link "fry" } " are an abstraction built on top of these operations, and code that uses this abstraction is often clearer than direct calls to the below words."
+$nl
+"Curried dataflow combinators can be used to build more complex dataflow by combining cleave, spread and apply patterns in various ways."
+{ $subsection "curried-dataflow" }
+"Quotations also implement the sequence protocol, and can be manipulated with sequence words; see " { $link "quotations" } ". However, such runtime quotation manipulation will not be optimized by the optimizing compiler." ;
 
 ARTICLE: "implementing-combinators" "Implementing combinators"
 "The following pair of words invoke words and quotations reflectively:"
@@ -831,9 +1055,9 @@ $nl
 "Here is an array containing the " { $link f } " class:"
 { $example "{ POSTPONE: f } ." "{ POSTPONE: f }" }
 "The " { $link f } " object is an instance of the " { $link f } " class:"
-{ $example "f class ." "POSTPONE: f" }
+{ $example "USE: classes" "f class ." "POSTPONE: f" }
 "The " { $link f } " class is an instance of " { $link word } ":"
-{ $example "\\ f class ." "word" }
+{ $example "USE: classes" "\\ f class ." "word" }
 "On the other hand, " { $link t } " is just a word, and there is no class which it is a unique instance of."
 { $example "t \\ t eq? ." "t" }
 "Many words which search collections confuse the case of no element being present with an element being found equal to " { $link f } ". If this distinction is imporant, there is usually an alternative word which can be used; for example, compare " { $link at } " with " { $link at* } "." ;
@@ -872,7 +1096,7 @@ ARTICLE: "conditionals" "Conditionals and logic"
 { $see-also "booleans" "bitwise-arithmetic" both? either? } ;
 
 ARTICLE: "equality" "Equality"
-"There are two distinct notions of ``sameness'' when it comes to objects."
+"There are two distinct notions of “sameness” when it comes to objects."
 $nl
 "You can test if two references point to the same object (" { $emphasis "identity comparison" } "). This is rarely used; it is mostly useful with large, mutable objects where the object identity matters but the value is transient:"
 { $subsection eq? }
@@ -892,6 +1116,14 @@ ARTICLE: "assertions" "Assertions"
 { $subsection assert }
 { $subsection assert= } ;
 
+ARTICLE: "dataflow-combinators" "Data flow combinators"
+"Data flow combinators pass values between quotations:"
+{ $subsection "slip-keep-combinators" }
+{ $subsection "cleave-combinators" }
+{ $subsection "spread-combinators" }
+{ $subsection "apply-combinators" }
+{ $see-also "curried-dataflow" } ;
+
 ARTICLE: "dataflow" "Data and control flow"
 { $subsection "evaluator" }
 { $subsection "words" }
@@ -899,16 +1131,17 @@ ARTICLE: "dataflow" "Data and control flow"
 { $subsection "booleans" }
 { $subsection "shuffle-words" }
 "A central concept in Factor is that of a " { $emphasis "combinator" } ", which is a word taking code as input."
-{ $subsection "slip-keep-combinators" }
-{ $subsection "cleave-combinators" }
-{ $subsection "spread-combinators" }
-{ $subsection "apply-combinators" }
+{ $subsection "dataflow-combinators" }
 { $subsection "conditionals" }
+{ $subsection "looping-combinators" }
 { $subsection "compositional-combinators" }
 { $subsection "combinators" }
+"More combinators are defined for working on data structures, such as " { $link "sequences-combinators" } " and " { $link "assocs-combinators" } "."
+$nl
 "Advanced topics:"
 { $subsection "assertions" }
 { $subsection "implementing-combinators" }
+{ $subsection "macros" }
 { $subsection "errors" }
 { $subsection "continuations" } ;