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.
A deferred word can be used to improve the statistics-gathering example
from User-defined Defining Words; rather than edit the
application’s source code to change every :
to a my:
, do
this:
: real: : ; \ retain access to the original defer : \ redefine as a deferred word ' my: IS : \ use special version of : \ \ load application here \ ' real: IS : \ go back to the original
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
In situations where IS
does not fit, use defer!
instead.
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
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.
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).
IS
( value "name" – ) core-ext “IS”
changes the defer
red word name to execute value
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).
action-of
( interpretation "name" – xt; compilation "name" – ; run-time – xt ) core-ext “action-of”
Xt is the XT that is currently assigned to name.
:is
( "name" – ) gforth-experimental “:is”
define a noname that is assigned to the deffered word name
at ;
.
Definitions of these Forth-2012 words in Forth-94 are provided in compat/defer.fs. In addition, Gforth provides:
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.
wrap-xt
( xt1 xt2 xt: xt3 – ... ) gforth-1.0 “wrap-xt”
Set deferred word xt2 to xt1 and execute xt3. Restore afterwards.
preserve
( "name" – ) gforth-1.0 “preserve”
emit code that reverts a deferred word to the state at compilation