The defining word Defer
allows you to define a word by name
without defining its behaviour; the definition of its behaviour is
deferred. Here are two situation where this can be useful:
In the following example, foo
always invokes the version of
greet
that prints “Good morning
” whilst bar
always invokes the version that prints “Hello
”. There is no way
of getting foo
to use the later version without re-ordering the
source code and recompiling it.
: greet ." Good morning" ; : foo ... greet ... ; : greet ." Hello" ; : bar ... greet ... ;
This problem can be solved by defining greet
as a Defer
red
word. The behaviour of a Defer
red word can be defined and
redefined at any time by using IS
to associate the xt of a
previously-defined word with it. The previous example becomes:
Defer greet ( -- ) : foo ... greet ... ; : bar ... greet ... ; : greet1 ( -- ) ." Good morning" ; : greet2 ( -- ) ." Hello" ; ' greet2 IS greet \ make greet behave like greet2
Programming style note:
You should write a stack comment for every deferred word, and put only XTs into deferred words that conform to this stack effect. Otherwise it’s too difficult to use the deferred word.
One thing to note is that IS
has special compilation semantics,
such that it parses the name at compile time (like TO
):
: set-greet ( xt -- ) IS greet ; ' greet1 set-greet
Defer
( "name" – ) core-ext “Defer”
Define a deferred word name; you have to set it to an xt before
executing it.
name execution: execute the most recent xt that
name has been set to.
Is name
run-time: ( xt –
) Set name to execute xt.
Action-of name
run-time: ( – xt ) Xt is currently assigned to name.
IS
( xt ... "name" – ) core-ext “IS”
Name is a defer-flavoured word, ... is optional additional addressing information, e.g., for a defer-flavoured field. At run-time, perform the is name semantics: change name (with the same additional addressing information) to execute xt.
You can extract the xt of a defer
red word with action-of
:
action-of greet ( xt ) >name id.
action-of
( interpretation "name" ... – xt; compilation "name" – ; run-time ... – xt ) core-ext “action-of”
Name is a defer-flavoured word, ... is optional additional addressing information, e.g., for a defer-flavoured field. At run-time, perform the action-of name semantics: Push the xt, that name (possibly with additional addressing data on the stack) executes.
One usage for deferred words is the definition of a an action (e.g., initialization) in several pieces, each piece in a different source file dealing with the matters of that source file. This can be done with
defer myspeech ( -- ) :noname cr ." <central message>" ; is myspeech \ and in every source file where you want to add a piece, something like :noname ( -- ) cr ." <introduction>" [ action-of myspeech compile, ] cr ." <conclusion>" ; is myspeech
The [ action-of myspeech compile, ]
calls the previous content
of myspeech
. Gforth offers the words :is
and
defers
to express the last definition more conveniently:
:is myspeech ( -- ) cr ." <introduction>" defers myspeech cr ." <conclusion>" ;
:is
( "name" – ) gforth-experimental “:is”
define a noname that is assigned to the deffered word name
at ;
.
defers
( compilation "name" – ; run-time ... – ... ) gforth-0.2 “defers”
Compiles the present contents of the deferred word name into the current definition. I.e., this produces static binding as if name was not deferred.
Another usage is to change a deferred word temporarily, and later
change it back. Gforth provides words for supporting this usage. The
use of preserve
is shown in this example:
: smalltalk ( -- ) greet ." Isn't the weather nice?" ; \ here GREET perfroms GREET2 : when-in-rome ( xt -- ) [: ." Buon Giorno!" ;] is greet execute preserve greet \ Equivalent to: ['] greet2 is greet ; ' greet1 is greet greet \ "Good Morning" ' smalltalk when-in-rome \ "Buon Giorno! Isn't the weather nice?" greet \ "Hello"
preserve
( compilation "name" – ; run-time – ) gforth-1.0 “preserve”
Name has to be a defer-flavoured word that does not consume
additional stack items for addressing (i.e., not a
defer-flavoured field). Preserve name
changes
name at run-time to execute the same XT that it had at
compile time. I.e., Preserve name
is equivalent to
[ action-of name ] literal is name
.
Preserve
is only appropriate when you want to restore the
deferred word to a fixed xt. If you want to change a deferred
temporarily and then restore its old run-time value, use
wrap-xt
:
: when-in-rome2 ( xt -- ) [: ." Buon Giorno!" ;] ['] greet rot wrap-xt ; ' greet1 is greet greet \ "Good Morning" ' smalltalk when-in-rome2 \ "Buon Giorno! Isn't the weather nice?" greet \ "Good Morning"
wrap-xt
( ... xt1 xt2 xt3 – ... ) gforth-1.0 “wrap-xt”
Set deferred word xt2 to xt1 and execute xt3. Restore afterwards.
For implementing words like wrap-xt
to which you pass the xt of
a deferred word, you cannot use is
and action-of
, which
consume a name from the input stream. Instead, you use the words
defer!
and defer@
.
defer!
( xt xt-deferred – ) core-ext “defer-store”
xt-deferred belongs to a word defined with defer
, it
is changed to execute xt on execution.
If xt-deferred
belongs to another defer-flavoured word (e.g., a
defer-flavoured field), the location associated with
... xt-deferred is changed to execut xt.
If
xt-deferred is the xt of a word that is not
defer-flavoured, throw -21 (Unsupported operation).
defer@
( ... xt-deferred – xt ) core-ext “defer-fetch”
If xt-deferred belongs to a word defined with defer
,
xt represents the word currently associated with the
deferred word xt-deferred.
If xt-deferred belongs to
another defer-flavoured word (e.g., a defer-flavoured field),
xt is the word associated with the location indicated by
... xt-deferred (e.g., for a defer-flavoured field ...
is the structure address).
If xt-deferred is the xt of a
word that is not defer-flavoured, throw -21 (Unsupported
operation).
A deferred word can only inherit execution semantics from the xt (because that is all that an xt can represent – for more discussion of this see Tokens for Words); by default it will have default interpretation and compilation semantics deriving from this execution semantics. However, you can change the interpretation and compilation semantics of the deferred word in the usual ways:
: bar .... ; immediate Defer fred immediate Defer jim ' bar IS jim \ jim has default semantics ' bar IS fred \ fred is immediate