A big part of the productivity of Factor comes from the deep integration of the language and libraries with the tools around them, which are embodied in the listener. Many functions of the listener can be used programmatically, and vice versa. You have seen some examples of this:
• | The help is navigable online, but you can also invoke it with help and print help items with print-content ; |
• | The F2 shortcut or the words refresh and refresh-all can be used to refresh vocabularies from disk while continuing working in the listener; |
• | The edit word gives you editor integration, but you can also click on file names in the help pages for vocabularies to open them. |
The refresh is an efficient mechanism. Whenever a word is redefined, words that depend on it are recompiled against the new definition. You can check by yourself doing
: inc ( x -- y ) 1 + ;
: inc-print ( x -- ) inc . ;
5 inc-print
and then
: inc ( x -- y ) 2 + ;
5 inc-print
This allows you to keep a listener open, improve your definitions, periodically save your definitions to a file and refresh to view your changes, without ever having to reload Factor.
You can also save the state of your current session with the word
save-image and later restore it by starting Factor with
./factor -i=path-to-image
In fact, Factor is image-based and only uses files when loading and refreshing vocabularies.
The power of the listener does not end here. Elements of the stack can be inspected by clicking on them, or by calling the word
inspector. For instance try writing
TUPLE: trilogy first second third ;
: <trilogy> ( first second third -- trilogy ) trilogy boa ;
"A new hope" "The Empire strikes back" "Return of the Jedi" <trilogy>
"George Lucas" 2array
You will get an item that looks like
{ ~trilogy~ "George Lucas" }
on the stack. Try clicking on it: you will be able to see the slots of the array. You can inspect a slot shown in the inspector by double clicking on it. This is extremely useful for interactive prototyping. Special objects can customize the inspector by implementing the
content-gadget method.
There is another inspector for errors. Whenever an error arises, it can be inspected with
F3. This allows you to investigate exceptions, bad stack effect declarations and so on. The debugger allows you to step into code, both forwards and backwards, and you should take a moment to get some familiarity with it. You can also trigger the debugger manually, by entering some code in the listener and pressing
Ctrl+w.
The listener has provisions for benchmarking code. As an example, here is an intentionally inefficient Fibonacci:
DEFER: fib-rec
: fib ( n -- f(n) ) dup 2 < [ ] [ fib-rec ] if ;
: fib-rec ( n -- f(n) ) [ 1 - fib ] [ 2 - fib ] bi + ;
(notice the use of
DEFER: to define two mutually recursive words). You can benchmark the running time writing
40 fib and then pressing Ctrl+t instead of Enter. You will get timing information, as well as other statistics. Programmatically, you can use the
time word on a quotation to do the same.
You can also add watches on words, to print inputs and outputs on entry and exit. Try writing
\ fib watch
and then run
10 fib to see what happens. You can then remove the watch with
\ fib reset.
Another useful tool is the
lint vocabulary. This scans word definitions to find duplicated code that can be factored out. As an example, let us define a word to check if a string starts with another one. Create a test vocabulary
"lintme" scaffold-work
and add the following definition:
USING: kernel sequences ;
IN: lintme
: startswith? ( str sub -- ? ) dup length swapd head = ;
Load the lint tool with
USE: lint and write
"lintme" lint-vocab. You will get a report mentioning that the word sequence
length swapd is already used in the word
(split) of
splitting.
private, hence it could be factored out.
Modifying the source of a word in the standard library is unadvisable - let alone a private one - but in more complex cases the lint tool can help you prevent code duplication. It is not unusual that Factor has a word that does exactly what you want, owing to its massive standard library. It is a good idea to lint your vocabularies from time to time, to avoid code duplication and as a good way to discover library words that you may have accidentally redefined.
Finally, there are a few utilities to inspect words. You can see the definition of a word in the help tool, but a quicker way can be
see. Or, vice versa, you may use
usage. to inspect the callers of a given word. Try
\ reverse see and
\ reverse usage..