6.11.11 Deferred Words

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 Deferred word. The behaviour of a Deferred 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 deferred 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