6.10.10.2 User-defined defining words using create

If you want the words defined with your defining words to behave differently from words defined with standard defining words, you can write your defining word like this:

: def-word ( "name" -- )
    CREATE code1
DOES> ( ... -- ... )
    code2 ;

def-word name

This fragment defines a defining word def-word and then executes it. When def-word executes, it CREATEs a new word name, and executes the code code1. The code code2 is not executed at this time. The word name is sometimes called a child of def-word.

When you execute name, the address of the body of name is put on the data stack and code2 is executed (the address of the body of name is the address HERE returns immediately after the CREATE, i.e., the address a created word returns by default).

You can use def-word to define a set of child words that behave similarly; they all have a common run-time behaviour determined by code2. Typically, the code1 sequence builds a data area in the body of the child word. The structure of the data is common to all children of def-word, but the data values are specific – and private – to each child word. When a child word is executed, the address of its private data area is passed as a parameter on TOS to be used and manipulated18 by code2.

The two fragments of code that make up the defining words act (are executed) at two completely separate times:

Another way of understanding the behaviour of def-word and name is to say that, if you make the following definitions:

: def-word1 ( "name" -- )
    CREATE code1 ;

: action1 ( ... -- ... )
    code2 ;

def-word1 name1

Then using name1 action1 is equivalent to using name.

Another way of writing def-word is (see Quotations):

: def-word ( "name" -- ; name execution: ... -- ... )
    create code1
    [: code2 ;] set-does> ;

Gforth actually compiles the code using does> into code equivalent to the latter code. An advantage of the set-does> approach is that you can put other code behind it and you can use it inside control structures without needing workarounds. A disadvantage is that it is Gforth-specific.

A classic example is that you can define CONSTANT in this way:

: CONSTANT ( w "name" -- )
    CREATE ,
DOES> ( -- w )
    @ ;

or equivalently

: CONSTANT ( w "name" -- ; name execution: -- w )
    create ,
    ['] @ set-does> ;

When you create a constant with 5 CONSTANT five, a set of define-time actions take place; first a new word five is created, then the value 5 is laid down in the body of five with ,. When five is executed, the address of the body is put on the stack, and @ retrieves the value 5. The word five has no code of its own; it simply contains a data field and the xt of the quotation or of @.

The final example in this section is intended to remind you that space reserved in CREATEd words is data space and therefore can be both read and written by a Standard program19:

: foo ( "name" -- )
    CREATE -1 ,
DOES> ( -- )
    @ . ;

foo first-word
foo second-word

123 ' first-word >BODY !

If first-word had been a CREATEd word, we could simply have executed it to get the address of its data field. However, since it was defined to have DOES> actions, its execution semantics are to perform those DOES> actions. To get the address of its data field it’s necessary to use ' to get its xt, then >BODY to translate the xt into the address of the data field. When you execute first-word, it will display 123. When you execute second-word it will display -1.

In the examples above the stack comment after the DOES> specifies the stack effect of the defined words, not the stack effect of the following code (the following code expects the address of the body on the top of stack, which is not reflected in the stack comment). This is the convention that I use and recommend (it clashes a bit with using locals declarations for stack effect specification, though).


Footnotes

(18)

It is legitimate both to read and write to this data area.

(19)

Exercise: use this example as a starting point for your own implementation of Value and TO – if you get stuck, investigate the behaviour of ' and ['].