From cc60477996e7074b90c0574ed7a300f78aab4805 Mon Sep 17 00:00:00 2001 From: John Benediktsson Date: Sun, 23 Sep 2012 19:01:43 -0700 Subject: [PATCH] help.tutorial: some feedback from real world tutees. --- basis/help/tutorial/tutorial.factor | 61 +++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/basis/help/tutorial/tutorial.factor b/basis/help/tutorial/tutorial.factor index ebbadb6fb2..d30a676430 100644 --- a/basis/help/tutorial/tutorial.factor +++ b/basis/help/tutorial/tutorial.factor @@ -9,12 +9,15 @@ ARTICLE: "first-program-start" "Creating a vocabulary for your first program" $nl "Start by loading the scaffold tool:" { $code "USE: tools.scaffold" } +$nl "Then, ask the scaffold tool to create a new vocabulary named " { $snippet "palindrome" } ":" { $code "\"resource:work\" \"palindrome\" scaffold-vocab" } +$nl "If you look at the output, you will see that a few files were created in your “work” directory, and that the new source file was loaded." $nl "The following phrase will print the full path of your work directory:" { $code "\"work\" resource-path ." } +$nl "The work directory is one of several " { $link "vocabs.roots" } " where Factor searches for vocabularies. It is possible to define new vocabulary roots; see " { $link "add-vocab-roots" } ". To keep things simple in this tutorial, we'll just use the work directory, though." $nl "Open the work directory in your file manager, and open the subdirectory named " { $snippet "palindrome" } ". Inside this subdirectory you will see a file named " { $snippet "palindrome.factor" } ". Open this file in your text editor." @@ -26,17 +29,20 @@ ARTICLE: "first-program-logic" "Writing some logic in your first program" $nl "Your " { $snippet "palindrome.factor" } " file should look like the following after the previous section:" { $code - "! Copyright (C) 2011 " + "! Copyright (C) 2012 Your name." "! See http://factorcode.org/license.txt for BSD license." "USING: ;" "IN: palindrome" } -"Notice that the file ends with an " { $link POSTPONE: IN: } " form telling Factor that all definitions in this source file should go into the " { $snippet "palindrome" } " vocabulary using the " { $link POSTPONE: IN: } " word. We will add new definitions after the " { $link POSTPONE: IN: } " form." +$nl +"Notice that the file ends with an " { $link POSTPONE: IN: } " form telling Factor that all definitions in this source file should go into the " { $snippet "palindrome" } " vocabulary using the " { $link POSTPONE: IN: } " word. We will be adding new definitions after the " { $link POSTPONE: IN: } " form." $nl "In order to be able to call the words defined in the " { $snippet "palindrome" } " vocabulary, you need to issue the following command in the listener:" { $code "USE: palindrome" } +$nl "Now, we will be making some additions to the file. Since the file was loaded by the scaffold tool in the previous step, you need to tell Factor to reload it if it changes. Factor has a handy feature for this; pressing " { $command tool "common" refresh-all } " in the listener window will reload any changed source files. You can also force a single vocabulary to reload, in case the refresh feature does not pick up changes from disk:" { $code "\"palindrome\" reload" } +$nl "We will now write our first word using " { $link POSTPONE: : } ". This word will test if a string is a palindrome; it will take a string as input, and give back a boolean as output. We will call this word " { $snippet "palindrome?" } ", following a naming convention that words returning booleans have names ending with " { $snippet "?" } "." $nl "Recall that a string is a palindrome if it is spelled the same forwards or backwards; that is, if the string is equal to its reverse. We can express this in Factor as follows:" @@ -51,10 +57,12 @@ $nl $nl "Go back to the third line in your source file and change it to:" { $code "USING: kernel ;" } +$nl "Next, find out what vocabulary " { $link reverse } " lives in; type the word name " { $snippet "reverse" } " in the listener's input area, and press " { $operation com-browse } "." $nl "It lives in the " { $vocab-link "sequences" } " vocabulary, so we add that to the search path:" { $code "USING: kernel sequences ;" } +$nl "Finally, check what vocabulary " { $link = } " lives in, and confirm that it's in the " { $vocab-link "kernel" } " vocabulary, which we've already added to the search path." $nl "Now press " { $command tool "common" refresh-all } " again, and the source file should reload without any errors. You can now go on and learn about " { $link "first-program-test" } "." ; @@ -62,29 +70,36 @@ $nl ARTICLE: "first-program-test" "Testing your first program" "Your " { $snippet "palindrome.factor" } " file should look like the following after the previous section:" { $code - "! Copyright (C) 2011 " + "! Copyright (C) 2012 Your name." "! See http://factorcode.org/license.txt for BSD license." "USING: kernel sequences ;" "IN: palindrome" "" - ": palindrome? ( str -- ? ) dup reverse = ;" + ": palindrome? ( string -- ? ) dup reverse = ;" } +$nl "We will now test our new word in the listener. If you haven't done so already, add the palindrome vocabulary to the listener's vocabulary search path:" { $code "USE: palindrome" } -"Next, push a string on the stack:" +$nl +"Next, push a string on the stack (by surrounding text with quotes in the listener and then hitting " { $snippet "ENTER" } "):" { $code "\"hello\"" } +$nl "Note that the stack display in the listener now shows this string. Having supplied the input, we call our word:" { $code "palindrome?" } +$nl "The stack display should now have a boolean false - " { $link f } " - which is the word's output. Since “hello” is not a palindrome, this is what we expect. We can get rid of this boolean by calling " { $link drop } ". The stack should be empty after this is done." $nl "Now, let's try it with a palindrome; we will push the string and call the word in the same line of code:" { $code "\"racecar\" palindrome?" } +$nl "The stack should now contain a boolean true - " { $link t } ". We can print it and drop it using the " { $link . } " word:" { $code "." } +$nl "What we just did is called " { $emphasis "interactive testing" } ". A more advanced technique which comes into play with larger programs is " { $link "tools.test" } "." $nl "Create a test harness file using the scaffold tool:" { $code "\"palindrome\" scaffold-tests" } +$nl "Now, open the file named " { $snippet "palindrome-tests.factor" } "; it is located in the same directory as " { $snippet "palindrome.factor" } ", and it was created by the scaffold tool." $nl "We will add some unit tests, which are similar to the interactive tests we did above. Unit tests are defined with the " { $link POSTPONE: unit-test } " word, which takes a sequence of expected outputs, and a piece of code. It runs the code, and asserts that it outputs the expected values." @@ -94,8 +109,10 @@ $nl "[ f ] [ \"hello\" palindrome? ] unit-test" "[ t ] [ \"racecar\" palindrome? ] unit-test" } +$nl "Now, you can run unit tests:" { $code "\"palindrome\" test" } +$nl "It should report that all tests have passed. Now you can read about " { $link "first-program-extend" } "." ; ARTICLE: "first-program-extend" "Extending your first program" @@ -103,54 +120,76 @@ ARTICLE: "first-program-extend" "Extending your first program" $nl "For example, we'd like it to identify the following as a palindrome:" { $code "\"A man, a plan, a canal: Panama.\"" } +$nl "However, right now, the simplistic algorithm we use says this is not a palindrome:" { $unchecked-example "\"A man, a plan, a canal: Panama.\" palindrome? ." "f" } +$nl "We would like it to output " { $link t } " there. We can encode this requirement with a unit test that we add to " { $snippet "palindrome-tests.factor" } ":" { $code "[ t ] [ \"A man, a plan, a canal: Panama.\" palindrome? ] unit-test" } +$nl "If you now run unit tests, you will see a unit test failure:" { $code "\"palindrome\" test" } +$nl "The next step is to, of course, fix our code so that the unit test can pass." $nl "We begin by writing a word which removes blanks and non-alphabetical characters from a string, and then converts the string to lower case. We call this word " { $snippet "normalize" } ". To figure out how to write this word, we begin with some interactive experimentation in the listener." $nl "Start by pushing a character on the stack; notice that characters are really just integers:" { $code "CHAR: a" } +$nl "Now, use the " { $link Letter? } " word to test if it is an alphabetical character, upper or lower case:" { $unchecked-example "Letter? ." "t" } +"Note: you might receive an error message that asks if you want to use the " { $link "ascii" } " or " { $link "unicode" } " versions of the " { $link Letter? } " word. Choosing the Unicode version will allow Factor to continue running your code." +$nl "This gives the expected result." $nl "Now try with a non-alphabetical character:" { $code "CHAR: #" } { $unchecked-example "Letter? ." "f" } +$nl "What we want to do is given a string, remove all characters which do not match the " { $link Letter? } " predicate. Let's push a string on the stack:" { $code "\"A man, a plan, a canal: Panama.\"" } +$nl "Now, place a quotation containing " { $link Letter? } " on the stack; quoting code places it on the stack instead of executing it immediately:" { $code "[ Letter? ]" } -"Finally, pass the string and the quotation to the " { $link filter } " word:" +"Note: " { $link "quotations" } " are similar to anonymous functions or blocks of code that have not been executed yet." +$nl +"Finally, we pass the string and the quotation to the " { $link filter } " word, which will run your quotation and return a new string that contains only characters for which " { $link Letter? } " returns \"true\":" { $code "filter" } -"Now the stack should contain the following string:" -{ "\"AmanaplanacanalPanama\"" } ". " +$nl +"The stack should now contain the following string: " +{ $snippet "AmanaplanacanalPanama" } ". " "This is almost what we want; we just need to convert the string to lower case now. This can be done by calling " { $link >lower } "; the " { $snippet ">" } " prefix is a naming convention for conversion operations, and should be read as “to”:" { $code ">lower" } +$nl "Finally, let's print the top of the stack and discard it:" { $code "." } +$nl "This will output " { $snippet "amanaplanacanalpanama" } ". This string is in the form that we want, and we evaluated the following code to get it into this form:" { $code "[ Letter? ] filter >lower" } +$nl "This code starts with a string on the stack, removes non-alphabetical characters, and converts the result to lower case, leaving a new string on the stack. We put this code in a new word, and add the new word to " { $snippet "palindrome.factor" } ":" -{ $code ": normalize ( str -- newstr ) [ Letter? ] filter >lower ;" } +{ $code ": normalize ( string -- string' ) [ Letter? ] filter >lower ;" } +$nl "You will need to add " { $vocab-link "unicode.case" } " and " { $vocab-link "unicode.categories" } " to the vocabulary search path, so that " { $link >lower } " and " { $link Letter? } " can be used in the source file." $nl "We modify " { $snippet "palindrome?" } " to first apply " { $snippet "normalize" } " to its input:" -{ $code ": palindrome? ( str -- ? ) normalize dup reverse = ;" } +{ $code ": palindrome? ( string -- ? ) normalize dup reverse = ;" } +$nl "Factor compiles the file from the top down. So, be sure to place the definition for " { $snippet "normalize" } " above the definition for " { $snippet "palindrome?" } "." $nl "Now if you press " { $command tool "common" refresh-all } ", the source file should reload without any errors. You can run unit tests again, and this time, they will all pass:" -{ $code "\"palindrome\" test" } ; +{ $code "\"palindrome\" test" } +$nl +"Congratulations, you have now completed " { $link "first-program" } "!"; ARTICLE: "first-program" "Your first program" "In this tutorial, we will write a simple Factor program which prompts the user to enter a word, and tests if it is a palindrome (that is, the word is spelled the same backwards and forwards)." $nl "In this tutorial, you will learn about basic Factor development tools." +$nl +"Note: when you come across boxes with Factor code examples, you can click on them to copy and paste the code into your listener, to be run by then hitting " { $snippet "ENTER" } "." +$nl { $subsections "first-program-start" "first-program-logic"