kForth uses a separate control stack instead of using the data stack,
and it permits nested :NONAME definitions. This makes it easy to define
both quotations and closures simply in Forth source.
For quotations, the definitions of [: and ;] are provided in
ans-words.4th (from the distribution package). These definitions are
: [: postpone [ :noname ; immediate
: ;] postpone ; ] postpone literal ; immediate
To open a closure which takes a single integer parameter we can define
the word corresponding to Gforth's "[N:D" as follows in kForth:
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
The closure may be terminated with ";]" just as for a quotation.
Examples of using the single parameter closure:
: test1 3 [n:d dup * ;] execute . ;
: test2 3 2 + [n:d dup * ;] execute . ;
TEST1 prints 9 when executed, and TEST2 prints 25 when executed. It's important to note the distinction between a quotation and a closure. The run-time evaluated parameter on the stack prior to the closure is bound
as a fixed value in the closure.
Since kForth performs run-time type checking when address values are required, a variant of the single parameter closure is needed for a
single address parameter:
: [a:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone a@ ; immediate
It is easy to generalize the closure to take more than one parameter
from the stack. Recent examples have shown the utility of closures for setting the compilation semantics of a word in a dual-semantics system; however, kForth is still a single-xt + immediate flag system at present.
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.
To open a closure which takes a single integer parameter we can define
the word corresponding to Gforth's "[N:D" as follows in kForth:
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
On Monday, October 17, 2022 at 7:56:24 AM UTC+2, minf...@arcor.de wrote:
[..]
IOW in Forth terms, to make a closure just make a quotation and give it a copyWhen you exit a called word, its locals go away. You'll need yet another stack.
of the locals stack frame of the word it was built in.
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?
execute . \ 13 >x execute
execute . \ 2
execute . \ 3
execute . \ 4
kForth uses a separate control stack instead of using the data stack,
and it permits nested :NONAME definitions. This makes it easy to define
both quotations and closures simply in Forth source.
For quotations, the definitions of [: and ;] are provided in
ans-words.4th (from the distribution package). These definitions are
: [: postpone [ :noname ; immediate
: ;] postpone ; ] postpone literal ; immediate
To open a closure which takes a single integer parameter we can define
the word corresponding to Gforth's "[N:D" as follows in kForth:
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
The closure may be terminated with ";]" just as for a quotation.
Krishna Myneni <krishna.myneni@ccreweb.org> writes:
To open a closure which takes a single integer parameter we can define
the word corresponding to Gforth's "[N:D" as follows in kForth:
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
This allocates only a single cell for all closures created by a single source-code location of "[N:D". I expect that this fails the
following test case:
: n+ [n:d + ;] ;
3 n+ constant 3+
5 n+ constant 5+
1 3+ execute . \ should print 4
2 5+ execute . \ should print 7
On 2022-10-16 19:54, Krishna Myneni wrote:
kForth uses a separate control stack instead of using the data stack,
and it permits nested :NONAME definitions. This makes it easy to
define both quotations and closures simply in Forth source.
For quotations, the definitions of [: and ;] are provided in
ans-words.4th (from the distribution package). These definitions are
: [: postpone [ :noname ; immediate
: ;] postpone ; ] postpone literal ; immediate
To open a closure which takes a single integer parameter we can define
the word corresponding to Gforth's "[N:D" as follows in kForth:
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
The closure may be terminated with ";]" just as for a quotation.
It means that this ";]" generates code that will always return the same
xt in run-time. But a stateless closure should have a unique xt for
every distinct bound value at the least.
Actually, instead of [n:d ... ;] you can use a more simple tool —
partial application.
: partial1 ( x xt1 -- xt2 )
\ create a partially applied definition xt2
\ by fixing the top argument for xt1
state @ >r
2>r :noname r> r> lit, compile, postpone ;
r> if ] then
;
\ NB: in your system this partial1 should properly work
\ during compilation of another definition too.
: foo ( u|n -- xt ) [: dup * + ;] partial1 ;
2 foo alias f2
3 foo alias f3
0 f2 . \ 4
1 f2 . \ 5
2 f2 . \ 6
0 f3 . \ 9
1 f3 . \ 10
2 f3 . \ 11
On 10/17/22 04:50, Ruvim wrote:
On 2022-10-16 19:54, Krishna Myneni wrote:
kForth uses a separate control stack instead of using the data stack,
and it permits nested :NONAME definitions. This makes it easy to
define both quotations and closures simply in Forth source.
For quotations, the definitions of [: and ;] are provided in
ans-words.4th (from the distribution package). These definitions are
: [: postpone [ :noname ; immediate
: ;] postpone ; ] postpone literal ; immediate
To open a closure which takes a single integer parameter we can
define the word corresponding to Gforth's "[N:D" as follows in kForth:
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
The closure may be terminated with ";]" just as for a quotation.
It means that this ";]" generates code that will always return the
same xt in run-time. But a stateless closure should have a unique xt
for every distinct bound value at the least.
Yes.
Actually, instead of [n:d ... ;] you can use a more simple tool —
partial application.
: partial1 ( x xt1 -- xt2 )
\ create a partially applied definition xt2
\ by fixing the top argument for xt1
state @ >r
2>r :noname r> r> lit, compile, postpone ;
r> if ] then
;
\ NB: in your system this partial1 should properly work
\ during compilation of another definition too.
Will try it, but the first problem needs to be fixed also.
: foo ( u|n -- xt ) [: dup * + ;] partial1 ;
2 foo alias f2
3 foo alias f3
0 f2 . \ 4
1 f2 . \ 5
2 f2 . \ 6
0 f3 . \ 9
1 f3 . \ 10
2 f3 . \ 11
On 2022-10-17 11:41, Krishna Myneni wrote:
On 10/17/22 04:50, Ruvim wrote:
On 2022-10-16 19:54, Krishna Myneni wrote:
kForth uses a separate control stack instead of using the data
stack, and it permits nested :NONAME definitions. This makes it easy
to define both quotations and closures simply in Forth source.
For quotations, the definitions of [: and ;] are provided in
ans-words.4th (from the distribution package). These definitions are
: [: postpone [ :noname ; immediate
: ;] postpone ; ] postpone literal ; immediate
To open a closure which takes a single integer parameter we can
define the word corresponding to Gforth's "[N:D" as follows in kForth: >>>>
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
The closure may be terminated with ";]" just as for a quotation.
It means that this ";]" generates code that will always return the
same xt in run-time. But a stateless closure should have a unique xt
for every distinct bound value at the least.
Yes.
Actually, instead of [n:d ... ;] you can use a more simple tool —
partial application.
: partial1 ( x xt1 -- xt2 )
\ create a partially applied definition xt2
\ by fixing the top argument for xt1
state @ >r
2>r :noname r> r> lit, compile, postpone ;
r> if ] then
;
\ NB: in your system this partial1 should properly work
\ during compilation of another definition too.
Will try it, but the first problem needs to be fixed also.
To fix the first problem, the form:
[n:d ... ;]
should be translated into:
[: ... ;] partial1
So, we need to generate some code after the quotation.
We have the following options for that:
- introduce a special ending word (instead of ";]")
- do active parsing (i.e., that "[n:d" does parsing till ";]"
- redefine ";]"
The first option is simplest for illustration.
Let it be "cl[n:d ... ]cl"
: cl[n:d postpone [: ; immediate
: ]cl postpone ;] postpone partial1 ; immediate
That's all.
The test word:
: foo ( u|n -- xt ) cl[n:d dup * + ]cl ;
works as before:
On 10/17/22 11:24, Ruvim wrote:
On 2022-10-17 11:41, Krishna Myneni wrote:
On 10/17/22 04:50, Ruvim wrote:
On 2022-10-16 19:54, Krishna Myneni wrote:
kForth uses a separate control stack instead of using the data
stack, and it permits nested :NONAME definitions. This makes it
easy to define both quotations and closures simply in Forth source.
For quotations, the definitions of [: and ;] are provided in
ans-words.4th (from the distribution package). These definitions are >>>>>
: [: postpone [ :noname ; immediate
: ;] postpone ; ] postpone literal ; immediate
To open a closure which takes a single integer parameter we can
define the word corresponding to Gforth's "[N:D" as follows in kForth: >>>>>
: [n:d
1 cells allocate drop dup
postpone literal postpone !
postpone [: postpone literal postpone @ ; immediate
The closure may be terminated with ";]" just as for a quotation.
It means that this ";]" generates code that will always return the
same xt in run-time. But a stateless closure should have a unique xt
for every distinct bound value at the least.
Yes.
Actually, instead of [n:d ... ;] you can use a more simple tool —
partial application.
: partial1 ( x xt1 -- xt2 )
\ create a partially applied definition xt2
\ by fixing the top argument for xt1
state @ >r
2>r :noname r> r> lit, compile, postpone ;
r> if ] then
;
\ NB: in your system this partial1 should properly work
\ during compilation of another definition too.
Will try it, but the first problem needs to be fixed also.
I tried your definition of partial1 (substituting POSTPONE LITERAL for "LIT,") and it works as advertised in kForth. Cast into Anton's example,
: partial1 ( x xt1 -- xt2 )
state @ >r 2>r :noname r> r> postpone literal compile, postpone ;
r> if ] then ;
ok
: n+ [: + ;] partial1 ;
ok
3 n+ constant 3+
ok
5 n+ constant 5+
ok
1 3+ execute .
4 ok
2 5+ execute .
7 ok
It is noted that the word PARTIAL1 is STATE-dependent.
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):
On Tuesday, October 18, 2022 at 8:03:25 AM UTC-5, S Jack wrote:
On Monday, October 17, 2022 at 2:47:09 AM UTC-5, Anton Ertl wrote:
I think you misunderstood closures. A closure creates a functionE.g., in Forth consider the following code (and don't think about howIn assembly a called routine can allocate stack space for local
it is implemented; these other languages use garbage collection for
this kind of stuff):
variables and can access the stack space of its ancestors should
it need to. And when the routine returns, all its locals go away.
No need for heaps nor garbage collecting. I'm guessing that
"closure" is just some high level code wanting to do the same.
--
me
(xt in Forth) which compiles into it the parameter(s) passed to it.
The function has persistence. Furthermore, each runtime invocation
of the closure creates a distinct function (xt) with persistence.
On Monday, October 17, 2022 at 2:47:09 AM UTC-5, Anton Ertl wrote:I think you misunderstood closures. A closure creates a function
E.g., in Forth consider the following code (and don't think about howIn assembly a called routine can allocate stack space for local
it is implemented; these other languages use garbage collection for
this kind of stuff):
variables and can access the stack space of its ancestors should
it need to. And when the routine returns, all its locals go away.
No need for heaps nor garbage collecting. I'm guessing that
"closure" is just some high level code wanting to do the same.
--
me
I think you misunderstood closures. A closure creates a function
On Tuesday, October 18, 2022 at 9:15:33 AM UTC-5, km361...@gmail.com wrote:
It appears to me that the essence of closure is not how the function
arrives at its parent but that once invoked by its parent it can
access its parents environment (locals).
--
me
On Tuesday, October 18, 2022 at 9:15:33 AM UTC-5, km361...@gmail.com wrote:
I think you misunderstood closures. A closure creates a functionThat may very well be the case. But going by the definition that
in the link minf just posted:
"A closure is any function which closes over the environment in which
it was defined. This means that it can access variables not in its
parameter list."
It appears to me that the essence of closure is not how the function
arrives at its parent but that once invoked by its parent it can
access its parents environment (locals).
On Tuesday, October 18, 2022 at 11:12:48 AM UTC-5, S Jack wrote:
On Tuesday, October 18, 2022 at 9:15:33 AM UTC-5, km361...@gmail.com wrote:
I think you misunderstood closures. A closure creates a functionThat may very well be the case. But going by the definition that
in the link minf just posted:
"A closure is any function which closes over the environment in which
it was defined. This means that it can access variables not in its parameter list."
It appears to me that the essence of closure is not how the function arrives at its parent but that once invoked by its parent it can
access its parents environment (locals).
I amend my statement: "I probably don't understand closures."
The link by minf is useful.
km361...@gmail.com schrieb am Dienstag, 18. Oktober 2022 um 22:54:59 UTC+2:
On Tuesday, October 18, 2022 at 11:12:48 AM UTC-5, S Jack wrote:
On Tuesday, October 18, 2022 at 9:15:33 AM UTC-5, km361...@gmail.com wrote:
I think you misunderstood closures. A closure creates a functionThat may very well be the case. But going by the definition that
in the link minf just posted:
"A closure is any function which closes over the environment in which
it was defined. This means that it can access variables not in its parameter list."
Another test case. For simplicity I use the following syntax
[[: ... ;]] means closure
[: ... ;] means quotation (should be familiar by now)
{: ... :} means locals (standard)
: CLOSTEST
{: a :} 1 to a pi 1000e f* \ f: 3141.59, local a is 1
[: {: b :} b 2* ;] \ d: xtquot f: 3141.59, local a is 1 <- this is the environment
[[: {: c :} 1 swap execute f>s a + c ;]] fdrop ; \ d: xtquot xtclos
CLOSTEST NIP 3 SWAP EXECUTE \ d: 2 3142 3
\ 1 swap execute -> 2 <- executes xtquot from the environment
\ f>s -> 2 3141 (f>s rounds towards zero)
\ a + -> 2 3142
\ c -> 2 3142 3 <- 3 had been passed to execute xtclos
But now what happens when the closure is called at a time when this
global VALUE is not properly set yet/anymore?
"minf...@arcor.de" <minf...@arcor.de> writes:
But now what happens when the closure is called at a time when thisI thought VALUEs had to be set at their time of creation, so the "yet"
global VALUE is not properly set yet/anymore?
part doesn't seem like an issue. Is there a way for them to become
unset? If not, the "anymore" part doesn't seem like an issue either.
After reading through the link, I really wonder whether Forth (in its classic form)
can have REAL closures at all.
On Wednesday, October 19, 2022 at 2:07:53 AM UTC-5, minf...@arcor.de wrote:
After reading through the link, I really wonder whether Forth (in its classic form)
can have REAL closures at all.
As closure is a concoction to deal with variables, produced by
languages typically dealing with variables, one would think that the
concept would be of little value in Forth where variables are
anathema. (I say this with some jest.)
On Wednesday, October 19, 2022 at 8:31:38 AM UTC-5, S Jack wrote:Escaping closures for two clocks:
: OUTER hex [[: . ;]] ;
DECIMAL 10 OUTER DECIMAL EXECUTE --> A
Yes or no?
"minf...@arcor.de" <minf...@arcor.de> writes:
: OUTER hex [[: . ;]] ;
DECIMAL 10 OUTER DECIMAL EXECUTE --> A
Yes or no?
The closure is only
supposed to capture locals that are in scope when it is created, not
other stuff like BASE.
From a broader perspective: https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda/36878651#36878651
On 10/18/22 09:48, minf...@arcor.de wrote:
..
From a broader perspective: https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda/36878651#36878651Based on the broader perspective, the version of the closure in my
original post might qualify as a closure when used (one or more times)
only within the containing definition.
Krishna Myneni schrieb am Donnerstag, 20. Oktober 2022 um 00:53:42 UTC+2:
On 10/18/22 09:48, minf...@arcor.de wrote:
..
From a broader perspective:Based on the broader perspective, the version of the closure in my
https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda/36878651#36878651
original post might qualify as a closure when used (one or more times)
only within the containing definition.
Yes, with Forth we can only get as close as practical to closures, but not in their pure meaning.
I'm not sure why you say that we cannot implement them in their "pure meaning." Do you mean because we are not dealing with purely symbolic expressions?
On 10/22/22 07:26, minf...@arcor.de wrote:
Krishna Myneni schrieb am Donnerstag, 20. Oktober 2022 um 00:53:42 UTC+2:
On 10/18/22 09:48, minf...@arcor.de wrote:
..
From a broader perspective:Based on the broader perspective, the version of the closure in my
https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda/36878651#36878651
original post might qualify as a closure when used (one or more times)
only within the containing definition.
Yes, with Forth we can only get as close as practical to closures, but not in their pure meaning.
I'm not sure why you say that we cannot implement them in their "pure meaning." Do you mean because we are not dealing with purely symbolic expressions?
On 10/22/22 07:26, minf...@arcor.de wrote:
Krishna Myneni schrieb am Donnerstag, 20. Oktober 2022 um 00:53:42 UTC+2:
On 10/18/22 09:48, minf...@arcor.de wrote:
..
From a broader perspective:Based on the broader perspective, the version of the closure in my
https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda/36878651#36878651
original post might qualify as a closure when used (one or more times)
only within the containing definition.
Yes, with Forth we can only get as close as practical to closures, but not in their pure meaning.
Forth closures of the type implemented by Anton and Ruvim are found to
be quite useful when we need to generate an anonymous definition at
runtime, which binds runtime-computed values.
Krishna Myneni <krishna.myneni@ccreweb.org> writes:
I'm not sure why you say that we cannot implement them in their "pure
meaning." Do you mean because we are not dealing with purely symbolic
expressions?
The methods you gave seem to allocate storage for the saved locals in
the dictionary, with no way to reclaim the storage once you are done
with the closure. That's not so great if you program in a style that
creates a lot of closures on the fly. I don't know if there are other
issues besides that. Languages that promote using lots of closures
usually reclaim the storage by garbage collection. Idk if there are
other good ways to do it. Or if you're only making a few closures, it
is probably ok to just retain their storage permanently.
On 10/23/22 19:21, Paul Rubin wrote:
Krishna Myneni <krishna...@ccreweb.org> writes:
I'm not sure why you say that we cannot implement them in their "pure
meaning." Do you mean because we are not dealing with purely symbolic
expressions?
The methods you gave seem to allocate storage for the saved locals inYes, but Ruvim's example directly includes the data within the closure definition. Either way, I don't see a straightforward way of
the dictionary, with no way to reclaim the storage once you are done
with the closure. That's not so great if you program in a style that creates a lot of closures on the fly. I don't know if there are other issues besides that. Languages that promote using lots of closures
usually reclaim the storage by garbage collection. Idk if there are
other good ways to do it. Or if you're only making a few closures, it
is probably ok to just retain their storage permanently.
deallocating both the code and data associated with closures in Forth.
Memory fo closures store in the dictionary should be reclaimable. If we consider a closure which is local to a word definition, and used within
the word only (this was the example in my original post), then, in
Forth, then code memory may be reclaimed when the word is removed from
the dictionary, e.g. through the use of a MARKER word. For the type of closure which was used to provide new compilation semantics of a word
(see my recent post on "Demonstration of Dual Semantics" using Gforth's "[n:d"), when that word is removed from the dictionary (not the one in
which the closure is defined) then the code memory of the closure can be recovered.
For now, one should probably use closures assuming each new closure
requires persistent memory.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 300 |
Nodes: | 16 (2 / 14) |
Uptime: | 55:59:11 |
Calls: | 6,712 |
Files: | 12,243 |
Messages: | 5,355,401 |