melahi_ahmed@yahoo.fr (ahmed) writes:
On Sun, 9 Feb 2025 23:08:22 +0000, Anton Ertl wrote:
>
Paul Rubin <no.email@nospam.invalid> writes:
..
This is an attempt to make a counting function, like in Scheme:
>
(define (x)
((lambda (n)
(lambda ()
(set! n (+ 1 n))
n)) 0))
>
(define a (x))
...
: x ( -- xt )
here 0 , [{: addr :}d addr @ 1+ dup addr ! ;] ;
>
x alias a
...
: ctr: create 0 , does> dup @ 1+ dup rot ! ; ok
ctr: a
...
So, what is the difference between the two definitions?
One produces an xt, the other a named word; the latter is more
convenient for the shown usage).
But yes, for dictionary allocation Forth has had a way to associate
data with a single action since very early on.
If you want heap allocation (i.e., to be able to reclaim the memory
when you no longer need the counter), you can do it with closures, but
not with DOES>:
: x ( -- addr xt )
0 <{: w^ n :}H n ;> swap [{: n :}H n @ 1+ dup n ! ;] ;
x
dup execute . \ 1
dup execute . \ 2
x
dup execute . \ 1
free-closure free throw
dup execute . \ 3
free-closure free throw
Instead of using the syntax for allocating the data above, one could
also use heap allocation directly:
: x ( -- addr xt )
1 cells allocate throw 0 over ! dup [{: n :}H n @ 1+ dup n ! ;] ;
Following the textbook spirit of Paul Rubin's example, you can have
several closures working on the same data instance. E.g., let's
separate the count-up and the read-out functions (back to dictionary
allocation), and this time using pure-stack closures:
: y ( -- xt-count xt-val )
here 0 , dup [n:d 1 swap +! ;] swap [n:d @ ;] ;
y
dup execute . \ 0
over execute
dup execute . \ 1
over execute
over execute
y
dup execute . \ 0
2swap
dup execute . \ 3
2drop 2drop
You can also use DOES> for this effect, but it becomes longer and less
efficient:
: y-count ( addr "name" -- )
create ,
does> ( -- )
@ 1 swap +! ;
: y-val ( addr "name" -- )
create ,
does> ( -- u )
@ @ ;
: y ( "name1" "name2" -- )
here 0 , dup y-count y-val ;
y a-count a-val
a-val . \ 0
a-count
a-val . \ 1
a-count
a-count
y b-count b-val
b-val . \ 0
a-val . \ 3
But, as mentioned below, the textbook examples of changing data in
closures or DOES> words are rarely found in practice.
About the <{: ... ;> syntax:
The <{: ... ;> syntax becomes more useful if multiple data is located
there; you could instead define a structure, allocate that, and access
the fields, but defining a structure for a single usage is less
convenient than the syntax above; OTOH, with the syntax above you have
to pass all the addresses separately (and name them again in the
closure), while you can just pass the address of the structure to the
closure and then address the fields. So maybe the <{: ... ;> syntax is
never really useful.
The main reason why it is not used is not because alternative ways of
achieving the same thing are preferred, but because we usually don't
have uses of closures where the data changes. Not for closures, and
not for DOES>. The common case is that we have some value that we
want to associate with the code, and we do it at closure creation, and
then do not change it. I.e., something like Paul Rubin's textbook
example is a rare case in practice. And if the data is not changed,
it can just be passed as value to the closures, no home location with
an address necessary (and therefore no <{: ... ;>).
- anton
-- M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.htmlcomp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html New standard: https://forth-standard.org/EuroForth 2023 proceedings: http://www.euroforth.org/ef23/papers/EuroForth 2024 proceedings:
http://www.euroforth.org/ef24/papers/