Evolution of a Computer Application - Maintaining and Extending the System

Author(s): 
John J. Wavrik

 

Since a Forth application is extensible and Forth compiles incrementally, it is neither necessary nor desirable to make modifications or extensions by revising and recompiling the entire system.

I emphasize this point because it is different from the situation that arises with conventional compiled languages.

It has been found best, once a basic system is in place, to treat it as the implementation of a language: The language should be documented and not frequently subjected to total revision. Additions and modifications can be made as extensions to the existing system.

Most Forth implementations, including Win32Forth, have a provision to "save the system". The result is an executable that combines the underlying Forth system with the extensions added by the user.  The combined system is then used as if Forth came with the user's words.

When I have used Groups32 for instruction, I have installed the executable on a file server, and the basic system is unchanged once installed. It is nevertheless possible to make changes and additions as the course progresses: Win32Forth reads a configuration file after the system is loaded. (Groups32 is saved as an executable with the configuration file designated Groups32.cfg.)

The line

INCLUDE  updates.f

is placed in the configuration file. The file "updates.f" is placed in a public directory, and the instructor has read/write privileges. All extensions, modifications, bug fixes, etc. are made in the file updates.f.

  1. Extensions
  2. Redefining Words
  3. Patching

a. Extensions

Additions to the system can be made easily in updates.f. For some classes I have added, for example, an additional package with number theory commands. Additional commands can be added easily to the menu in the commands completion interface.  First create a version of the new command that prompts for its arguments and contains help information, then install it on the menu by

>CMD <menu_name><command_name>. 

If this is done in updates.f, the new command will appear on the menu whenever the system is loaded.

b. Redefining Words

It is sometimes desirable to change the action of an existing word. If a word is redefined (i.e., a new definition is made using the same name as a previously defined word), the new word is added to the end of the dictionary, and a dictionary search will find it first. Subsequent use of the name will find the new definition, but any word compiled with the older definition will continue to use the older action. 

If >CMD is used with the new action, the command completion interface will use the new action.

Most Forth systems provide a warning if a word is re-defined.

: myword ;  
: myword ." this one prints" ;
MYWORD isn't unique

The message "isn't unique" is not an error message -- it’s just a reminder to the user that a name is being used again. If this was done unintentionally, it can be undone in many Forth systems with FORGET (usage: FORGET ), a word that will remove and all subsequent words from the dictionary.

A new word can reference an older version in its definition.

: print-one  1 . ; 
: print-one ." Here is one: " print-one ;
PRINT-ONE isn't unique
print-one Here is one: 1

Forth does have a mechanism for recursion, but self-reference is not it.  The current word is deliberately excluded from the dictionary search. In the example above, the new print-one uses the earlier version in its definition.

c. Patching

As noted already, redefining a word does not change the existing action for words already compiled.  It is sometimes necessary to change the behavior of a word in such a way that any words already compiled use the new action. This can be done in at least two ways.

1. Vectored Execution

If it is known that a word may need to have its action changed, there is a mechanism for "deferred execution". The method defines a word that executes a stored address (vector). Changing the stored address changes the behavior of the word. 

I used this feature in the permutation package to permit a switch, after the fact, between left-to-right and right-to-left multiplication:

DEFER Direction
: Left->Right ['] NOOP IS Direction ;
: Right->Left ['] SWAP IS Direction ;

The behavior of the word Direction is switched between a NOOP (no operation) and SWAP.  This switchable word is used at the start of the code for P*.

Vectored execution is also used in the "print to file" code. Win32Forth, like many other Forth systems, uses deferred execution for basic output words EMIT, TYPE, and CR. We can switch the behavior of these words. During printing to a file we install versions that not only print to the console but also send output to a file.

Vectored execution can also be used effectively in development to test several versions of a word:  The code is written with a vectored word, and alternatives can be installed "on the fly".

For clarity of code, it is important to document, at the point where a vectored word is created, the type of behavior that will be filled in.  

2. Patching a definition

The dictionary entries themselves occupy accessible memory. One can often change the behavior of an existing word (e.g., to fix a bug) by altering an existing dictionary entry directly. Details of this process depend on the implementation of Forth and are beyond the scope of this article.