Learning the Tools
Factor handbook » Guided tour of Factor

Prev:The Object System
Next:Metaprogramming


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..