"
minf...@arcor.de" <
minf...@arcor.de> writes:
>Citing Wikipedia:
>Operationally, a closure is a record storing a function together with an environment.
>The environment is a mapping associating each free variable of the function
>(variables that are used locally, but defined in an enclosing scope) with the value
>or reference to which the name was bound when the closure was created.
>
>IOW in Forth terms, to make a closure just make a quotation and give it a copy
>of the locals stack frame of the word it was built in.
>
>Or am I missing something?
In other programming languages, a closure is when you pass a function
around that refers to a variable that is not local to it (in
particular, a local variable of one of the containing functions).
E.g., in Forth consider the following code (and don't think about how
it is implemented; these other languages use garbage collection for
this kind of stuff):
\ first example
: n+ {: n :} [: n + ;] ;
3 n+ constant 3+
2 3+ execute . \ 5
\ second example
: foo {: x :} [: to x ;] [: x ;] ;
1 foo constant x> constant >x
2 foo constant y> constant >y
x> execute . \ 1
y> execute . \ 2
3 >x execute
4 >y execute
x> execute . \ 3
y> execute . \ 4
Note that the second example produces four closures, two (X> and >X)
that refer to one instance of X, and two (Y> and >Y) that refer to a
different instance. Because these locals instances are writable, you
cannot just make a copy of them for each closure.
In 2018 I finally set out to design such a feature for Forth. Of
course I wanted to make the implementation simpler. I read up on the
topic and found that the Scheme people had invented flat-closure
conversion and assignment conversion as implementation techniques for
this stuff, and my idea was to make this explicit, and I wrote a paper
about it. I gave it to Bernd Paysan to read, and he found additional
simplifications, and implemented the result. So I had to rewrite much
of the paper, resulting in our EuroForth paper [ertl&paysan18].
In our extension as described in the paper, you pass data on the stack
(rather than through implicitly by referencing an outer local name) to
the closure, and put it into a local inside the closure (this code
works on Gforth):
: n+ ( n -- xt ) [{: n :}d n + ;] ;
3 n+ constant 3+
2 3+ execute .
The memory needed for the closure is allocated explicitly, in this
case in the dictionary (the D in :}D indicates that). Note that this
is allocated when N+ is called, not when it is compiled.
When I worked on the talk for EuroForth 2018, I realized that the
important point about closures for programming is not about locals,
but about transferring data from the closure creation time (when N+ is
called in the example above) to closure execution time. For this
point you don't need locals at all, and I mentioned that in my talk
(see the video [ertl&paysan18] or the "(eliminate locals)" part on
slide 11); at that point this was just an academic point, with no
intention of any practical application. A while later Bernd Paysan
told me that he had actually found this idea practically useful, and
had implemented the idea (with a different syntax from what I
presented on the slide):
: n+ ( n -- xt ) [n:d + ;] ;
3 n+ constant 3+
2 3+ execute .
The word [n:d indicates the start of a closure that takes one data
stack item from the stack at closure creation time (i.e., when N+ is
performed) and puts this item on the data stack at closure execution
time; the closure is stored in the dictionary. The N in [N:D
indicates that a single data-stack item is transferred (there is also
D (two cells) and F (one float)), the D indicated the dictionary
(there is also L (local stack) and H (heap, i.e., ALLOCATE)). If you
need to transfer more stack items, we don't have pure-stack words for
that; you can then use the closure syntax that puts the items in
locals.
@InProceedings{ertl&paysan18,
author = {M. Anton Ertl and Bernd Paysan},
title = {Closures --- the {Forth} way},
crossref = {euroforth18},
pages = {17--30},
url = {
http://www.complang.tuwien.ac.at/papers/ertl%26paysan.pdf},
url2 = {
http://www.euroforth.org/ef18/papers/ertl.pdf},
slides-url = {
http://www.euroforth.org/ef18/papers/ertl-slides.pdf},
video = {
https://wiki.forth-ev.de/doku.php/events:ef2018:closures},
OPTnote = {refereed},
abstract = {In Forth 200x, a quotation cannot access a local
defined outside it, and therefore cannot be
parameterized in the definition that produces its
execution token. We present Forth closures; they
lift this restriction with minimal implementation
complexity. They are based on passing parameters on
the stack when producing the execution token. The
programmer has to explicitly manage the memory of
the closure. We show a number of usage examples.
We also present the current implementation, which
takes 109~source lines of code (including some extra
features). The programmer can mechanically convert
lexical scoping (accessing a local defined outside)
into code using our closures, by applying assignment
conversion and flat-closure conversion. The result
can do everything one expects from closures,
including passing Knuth's man-or-boy test and living
beyond the end of their enclosing definitions.}
}
@Proceedings{euroforth18,
title = {34th EuroForth Conference},
booktitle = {34th EuroForth Conference},
year = {2018},
key = {EuroForth'18},
url = {
http://www.euroforth.org/ef18/papers/proceedings.pdf}
}
- anton
--
M. Anton Ertl
http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs:
http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard:
https://forth-standard.org/
EuroForth 2022:
https://euro.theforth.net