A local instance that is written and read must exist at only one location, its home location. The address of this home location is only read and can be duplicated and passed around. A textbook example might look like this in a hypothetical Gforth with lexical-scoping and explicit dictionary allocation:
\ does not work : counter ( -- xt-inc xt-val ) 0 {: n :}d [:d n 1+ to n ;] [:d n ;] ; \ for usage example see below
Instead, you allocate the home location, and pass its address around:
: counter ( -- xt-inc xt-val ) align here {: np :} 0 , \ home location np [{: np :}d 1 np +! ;] np [{: np :}d np @ ;] ; \ usage example counter \ first instance dup execute . \ prints 0 over execute over execute dup execute . \ prints 2 counter \ second instance over execute dup execute . \ prints 1 2swap \ work on first instance again dup execute . \ prints 2
This introduction of a home location is called assignment conversion in the programming language implementation literature.
You can also use pure-stack closures in this case:
: counter ( -- xt-inc xt-val ) align here {: np :} 0 , \ home location np [n:d 1 swap +! ;] np [n:d @ ;] ; \ same usage
Instead of dictionary allocation you can also allocate
on the
heap. For local allocation of the home location you can use
variable-flavoured locals (see Gforth locals), but of course then
the closures must not be used after leaving the definition in which
the home location is defined. E.g.
: counter-example ( -- ) 0 {: w^ np :} \ home location np [n:d 1 swap +! ;] np [n:d @ ;] dup execute cr . over execute over execute dup execute cr . 2drop ; counter-example \ prints 0 and 2
There is actually rarely a reason to use home locations at all,
because what the textbook examples do with closures and writable
locals can be done in Gforth more directly with structs
(see Structures) or objects (see Object-oriented Forth), or in
the counter example, simply with create
:
: counter ( "name" -- ) create 0 , ; : counter-inc ( addr -- ) 1 swap +! ; : counter-val ( addr -- ) @ ; \ usage example counter a a counter-val . \ prints 0 a counter-inc a counter-inc a counter-val . \ prints 2 counter b b counter-inc b counter-val . \ prints 1 a counter-val . \ prints 2
Still, for dictionary and heap allocation Gforth has a home-location
definition syntax based on the locals-definition syntax. Here’s a
heap-allocation version of counter
using closures and the
locals-like home-location syntax:
: counter ( -- handle xt-inc xt-val ) 0 <{: w^ np :}h np [n:h 1 swap +! ;] np [n:h @ ;] ;> -rot ; \ usage example counter \ first instance dup execute . \ prints 0 over execute over execute dup execute . \ prints 2 counter \ second instance over execute dup execute . \ prints 1 free-closure free-closure free throw \ back to first instance dup execute . \ prints 2 free-closure free-clouse free throw
Here <{:
starts a locals scope (similar to a closure itself),
then you define (variable-flavoured) locals. :}h
(or
:}d
) finishes the locals definition. Now (and up to ;>
)
you can use the names of the defined locals. Finally, ;>
ends
the scope and pushes the start address of the allocated home-location
block (also when using :}d
for dictionary allocation), for
free
ing the home-location block later.
We have produced no uses of <{:
and ;>
in the first 6
years that they were present in (development) Gforth. We think that
the reason is that one prefers structs or objects for modifiable data.
Therefore, we intend to remove these words in the future. If you want
to see them preserved, contact us and make a case for them.
<{:
( compilation – colon-sys ; run-time – ) gforth-obsolete “start-homelocation”
Starts defining a home location block.
;>
( compilation colon-sys – ; run-time – addr ) gforth-obsolete “end-homelocation”
Ends defining a home location; addr is the start address of the home-location block (used for deallocation).