Gforth also provides basic closures. A closure is a combination of a
quotation (see Quotations) and locals. Gforth’s closures have
locals which are filled with values at the closure’s run-time,
producing a trampoline xt. When executing that trampoline xt, the
closure’s code is executed, with access to the closure’s locals on the
locals stack. Modifications of the closure’s locals aren’t
persistent, i.e. when the closure EXIT
s, the modified values
are lost.
[{:
( – hmaddr u latest wid 0 ) gforth-experimental “start-closure”
starts a closure. Closures first declare the locals frame they are
going to use, and then the code that is executed with those locals.
Closures end like quotations with a ;]
. The locals declaration
ends depending where the closure’s locals are created. At run-time, the
closure is created as trampolin xt, and fills the values of its local
frame from the stack. At execution time of the xt, the local frame is
copied to the locals stack, and used inside the closure’s code. After
return, those values are removed from the locals stack, and not updated
in the closure itself.
:}l
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “close-brace-locals”
end a closure’s locals declaration. The closure will be allocated on the local’s stack.
:}d
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-d”
end a closure’s locals declaration. The closure will be allocated in the dictionary.
:}h
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-h”
end a closure’s locals declaration. The closure will be allocated on the heap.
:}h1
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-h-one”
end a closure’s locals declaration. The closure is deallocated
after the first execution, so this is a one-shot closure,
particularly useful in combination with send-event
(see Message queues).
:}xt
( hmaddr u latest latestnt wid 0 a-addr1 u1 ... – ) gforth-1.0 “colon-close-brace-x-t”
end a closure’s locals declaration. The closure will be allocated by
the xt on the stack, so the closure’s run-time stack effect is (
xt-alloc -- xt-closure )
.
>addr
( xt – addr ) gforth-experimental “to-addr”
convert the xt of a closure on the heap to the addr with can be
passed to free
to get rid of the closure
free-closure
( xt – ) gforth-internal “free-closure”
free a heap-allocated closure
: foo [{: a f: b d: c xt: d :}d a . b f. c d. d ;] ; 5 3.3e #1234. ' cr foo execute
foo
creates a closure in the dictionary with a single cell, a
floating point, a double, and an xt, and prints the first three values
before executing the xt on invocation.
This allows to implement Donald Knuth’s “Man or boy test” proposed in 1964 to test Algol compilers.
: A {: w^ k x1 x2 x3 xt: x4 xt: x5 | w^ B :} recursive k 0<= IF x4 x5 f+ ELSE B k x1 x2 x3 action-of x4 [{: B k x1 x2 x3 x4 :}L -1 k +! k B x1 x2 x3 x4 A ;] dup B ! execute THEN ; : man-or-boy? ( n -- ) [: 1e ;] [: -1e ;] 2dup swap [: 0e ;] A f. ;
Sometimes, closures need a permanent storage to be modified; it is even possible that more than one closure shares that permanent storage. In the example above, local variables of the outer procedure are used for this, but in some cases, the closure lives longer than the outer procedure; especially closures allocated in the dictionary or on the heap are designed to outlive their parent procedure.
For those, we have home locations, which are allocated like closures, but their code is directly executed at creation and should provide us with the addresses of the home locations.
: bar ( a b c -- aaddr baddr caddr hl-addr ) <{: w^ a w^ b w^ c :}h a b c ;> ;
This example creates a home location with three cells on the heap, and
returns the addresses of the three locations and the address of the
home location. This address can be used to free
the home
location when it is no longer needed.
<{:
( – hmaddr u latest latestnt wid 0 ) gforth-experimental “start-homelocation”
starts a home location
;>
( – ) gforth-experimental “end-homelocation”
end using a home location