What exactly does the "DOES>" word do?

705 Views Asked by At

I was messing around and trying to understand it, so I wrote a simple word to test it:

: test ." compile time" DOES> ." runtime" ;

The problem is, this word doesn't behave in a consistent way at all. Its output seems to vary depending on a number of factors, like:

  • is this the first line to be interpreted?
  • are there other words defined after it?

Also, sometimes it doesn't print anything at all.

(Using Gforth)

4

There are 4 best solutions below

0
On

Interactive play

In Gforth you can play with does> interpretively.

create foo 123 ,

foo @ . \ prints 123

does> ( addr -- ) @ . ;

foo \ prints 123

does> ( addr -- ) @ 1+ . ;

foo \ prints 124

' foo >body @ . \ prints 123

So does> just changes behavior of the last word, when this last word is defined via create. It's a mistake if you run does> when the last word was not defined via create.

Usage in practice

Usually does> is used to set a new behavior only once for a word defined via create. An ability to alter this behavior several times it just a side effect of historical implementation, and this effect is almost not used in the practice.


Alternative ways

In practice, the cases when does> is used, can be also implemented without does>.

For example, let we want to implement a word counter that creates a counter that every time returns the next value, and that is used in the following way:

1 counter x1
x1 . \ prints 1
x1 . \ prints 2
x1 . \ prints 3

An implementation via create does>

: counter ( x0 "ccc" -- ) \ Run-Time: ( -- x )
  create ,  does> ( addr -- x ) dup >r @ dup 1+ r> !
;

An implementation using a quotation

[undefined] lit, [if] : lit, ( x  -- ) postpone lit, ; [then]
[undefined] xt,  [if] : xt,  ( xt -- ) compile, ;      [then]

: counter ( x0 "ccc" -- ) \ Run-Time: ( -- x )
  align here >r , [: ( addr -- x ) dup >r @ dup 1+ r> ! ;] >r
  :  r> r> lit, xt, postpone ;
;

An implementation using a macro (code inlining) via the word ]]:

: counter ( x0 "ccc" -- ) \ Run-Time: ( -- x )
  align here >r , 
  :  r> lit, ]] dup >r @ dup 1+ r> ! [[ postpone ; 
;
3
On

ruvim's answer may be easier to understand in Gforth.

The code below defines a 'classic' defining word that creates variables initialised to the item on the stack at compile time. I hope the tracing statements in the definitions will help show what is happening

: var    \ create: n <name> -- ; does>: -- addr ; initialised VARIABLE
  create      \ create a dictionary item for <name>.
  ." HERE at compile time: " HERE .
  ,           \ place n at HERE in the dictionary
  does>       \ Push the HERE as at compile time to the stack
  ." Run time address on the stack:" dup .
;

var can now be used to define new words that have the run time action defined after DOES>.

10 var init10    \ Use var to define a new word init10
\ HERE at compile time: 135007328

init10 CR DUP . @ .
\ Run time address on the stack:135007328
\ 135007328 10   \ addr of init10, content of that address.

12 var init12    \ Use var to define a new word init12
\ HERE at compile time: 135007376

init12 CR DUP . @ .
\ Run time address on the stack:135007376
\ 135007376 12

100 init10 !    \ Store 100 in init10
\ Run time address on the stack:135007328
init10 @ .
\ Run time address on the stack:135007328  100
\ init10 now contains 100

Hopefully the answers will provide a framework to explore defining words and the action of DOES>.

0
On

A word that define words that allocate memory can be equipped with an action thru DOES> which gives the address to the memory block.

A simple example is CONSTANT wich can be defined as

: CONSTANT ( n -- ) CREATE , DOES> @ ;

0
On

This is your code:

: test ." compile time" DOES> ." runtime" ;

After entering that, I can use your word without the ambiguous behavior you are encountering:

CREATE def 12345 , test \ prints "compile time"

It prints compile time, because that's the behavior you compiled into test before DOES>. Note: this is not actually running at compile time.

DOES> ends the definition of the word, but changes it so test also modifies the last defined word so that it puts its data field address on the stack, and then runs the behaviour found after DOES>.

Using the word I created, it has the instantiated behavior you defined, following the implicit behaviour of pushing the address:

def @ . \ prints runtime 12345

Forth 2012 note: Per the definition of DOES> in Forth 2012, this would cause ambiguous behavior if the last word was not defined with CREATE. However, Gforth allows any word definition to be modified.

I hope this example helps explain why, usually, CREATE is used within the definition that uses DOES>, but it is certainly not required.