<< Home | About Forth | About TurboForth | Download | Language Reference | Resources | Tutorials | YouTube >>
Deferred words in Forth are words that exist, but don't do anything yet. Before a word can be compiled into a definition, it has to exist. Sometimes, you want the word to exist without any code. There are a few reasons for doing this:
DEFER and its partner word, IS allows you to do the above very easily. Consider the following:
This creates a word in the dictionary that can execute some code. What code? Well, that's the cool part. We can attach the code that we want this word to execute later on. Or, we can even change it. We can even change it in a running program if we want to.
Let's create a little program that either counts up or down. We'll get the same program to count up or down without having to compile it. Here's the code:
If you type in the above program (you'll need to load DEFER/IS from block 73 on the utilities disk) you'll see that it compiles just fine, but if you try and run it (by typing Counter) then you get an error: Deferred action not set. What does this mean? Well, clearly, deferring something means leaving something until later. In the case of deferred words in Forth, you're creating a word, but you are deferring the run-time behaviour of that word until later. So, before we can run the above program, we need to associate UpDown with some code, or another word, so that it can actually do something when executed. We do that with the word ' (which is pronounced tick).
When we 'tick' a word, we get back its execution token. This is simply an address (at least, it is in TurboForth). If we supply that address to EXECUTE (via the stack) then we can execute a word via its execution token. Try this:
' WORDS EXECUTE
Here, ' (tick) looked up the address of WORDS and placed it on the stack. EXECUTE took that address, and executed the code at that address, which causes WORDS to execute (obviously).
We use the same techinque to associate a deferred word with some code to execute.
Let's expand the little example program:
Now, type the following:
' CountUp IS UpDown
And then execute the program by typing Counter.
Ta dah! Our program is counting upwards. Of course, normally, we'd have to define CountUp before we defined Counter, but we created a deferred word (UpDown) that satisfied the compiler (i.e. the UpDown existed when the compiler encountered it, so the compiler was happy). We associated the action of UpDown later on, when we knew what we wanted it to do.
Stop the program by pressing the space bar. Now type the following:
' CountDown IS UpDown
Now type Counter again. Now we're counting down.
You can probably see now, that it is possible to write code using a deferred word and change its action later. This is great when you're de-bugging. Perhaps you're working on a word, and you haven't got it quite right yet. You create a deferred word, compile it into your program, then you create test words (TEST1, TEST2, TEST3) and associate your deferred word with the test words and re-run your program. This saves you the hassle of having to re-compile your program every time you make a change.
It's even possible to change the action of a deferred word at run-time, while your program is running. This is done with the word DEFER!.
The stack signature of DEFER! (pronounced "defer store") is as follows:
DEFER! ( xt1 xt2 -- )
It takes the execution token xt1 (found with tick, or its run-time counter-part ['] (also pronounced tick - just to confuse you) and stores it in the execution token of the deferred word xt2 (also obtained with ' or [']). For example:
' CountUp ' UpDown DEFER!
Let's modify the test program above such that we can change the action of the deferred word (UpDown) at run-time...
Note that in this version of the program, we have moved CountUp and CountDown to the top. This is because they are ticked (with [']) in the definition of Counter, and of course, a word must already exist in order to obtain its execution token!
The real work in this program is in the CASE...ENDCASE structure. Notice that if the U key is detected (for "up") then the execution token of CountUp is written into the deferred word UpDown via DEFER!, causing the program to count upwards. Similarly, if D is pressed (for "down") then the execution token of CountDown is written into the deferred word UpDown via DEFER!.
The definition of both IS and DEFER! as contained on block 73 of the utilities disk is intelligent. All deferred words, when created, are "standard" words (i.e. they are not immediate). If you subsequently associate an immediate word with your deferred word, the deferred word will be changed to immediate also. If you subsequently associate the same deferred word with a non-immediate word, the deferred word will be changed such that it is not immediate.
And there endeth the lesson on deferred words.
Below is the code found on block 73 of the utilities disk:
Here's some tests that you can execute in TurboForth to test that deferred words are operating correctly. The following test assign deferred words to both immediate and non-immediate words, and checks that the deferred word changes to immediate/non-immediate as appropriate.
14 August 2013