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.
I find it hard to imagine a situation -- programming problem --
where you need a lot of closures. And indeed a few closures is
easy enough.
I'm prepared to be convinced to think about adding any feature
that makes life easier. So convince me.
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.
Ruvim's original definition of PARTIAL1 is state-dependent, i.e. it
contains an expression of the sort, "STATE @ IF ... THEN." On a dual-semantics system it is possible to define PARTIAL1 without any
reference to STATE as follows (in Gforth):
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> ( -- xt1 x )
postpone literal
compile,
postpone ;
;
compsem:
['] partial1 compile,
]
;
On 2022-10-23 21:52, Krishna Myneni wrote:
[...]
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.
Ruvim's original definition of PARTIAL1 is state-dependent, i.e. it
contains an expression of the sort, "STATE @ IF ... THEN." On a
dual-semantics system it is possible to define PARTIAL1 without any
reference to STATE as follows (in Gforth):
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> ( -- xt1 x )
postpone literal
compile,
postpone ;
;
compsem:
['] partial1 compile,
]
;
Don't want to upset you, but it does not work correctly in Gforth.
Moreover, in Gforth, when you execute the xt that "name>compile"
returns, you are not guaranteed that the compilation semantics (i.e.,
the first behavior variant from above in the case of "partial1") will be performed regardless of the STATE.
A test case:
: p1 [ s" partial1" find-name name>compile execute 123 . ] ;
\ the code in the square brackets should append
\ the execution semantics of "partial1" to "p1",
\ and then print "123".
...
NB: your code doesn't violate the current specifications for
"set-compsem" and "name>compile".
The implementation of "name>compile" in Gforth just effectively imposes
a restriction on programs — a program should perform xt from
"name>compile" only in compilation state to perform the corresponding compilation semantics (in the general case).
This restriction is
similar to one that the popular implementation for "postpone" imposes.
And, as you can see, the dual-xt approach has not allowed to avoid this problem.
On 10/28/22 06:49, Ruvim wrote:..
On 2022-10-23 21:52, Krishna Myneni wrote:
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> ( -- xt1 x )
postpone literal
compile,
postpone ;
;
compsem:
['] partial1 compile,
]
;
... but it does not work correctly in Gforth.
Yes, there is something wrong with the definition. It is not a problem
with Gforth or a problem with NAME>COMPILE.
...
A test case:
: p1 [ s" partial1" find-name name>compile execute 123 . ] ;
\ the code in the square brackets should append
\ the execution semantics of "partial1" to "p1",
\ and then print "123".
...
If we look at the compiled definition for P1 in Gforth it shows the following:
see p1
: p1
[COMPILE] partial1 #123 . ] ; ok
This shows that NAME>COMPILE is indeed doing what it should do. It is performing the compilation semantics regardless of STATE -- it is just
not the behavior needed for your test case.
On 10/28/22 06:49, Ruvim wrote:[...]
Moreover, in Gforth, when you execute the xt that "name>compile"
returns, you are not guaranteed that the compilation semantics (i.e.,
the first behavior variant from above in the case of "partial1") will
be performed regardless of the STATE.
Yes, Gforth is executing the specified compilation semantics of PARTIAL1
in interpretation state for your example below. Note that the
compilation semantics I specified with COMPSEM: includes a return to compilation state.
A test case:
: p1 [ s" partial1" find-name name>compile execute 123 . ] ;
\ the code in the square brackets should append
\ the execution semantics of "partial1" to "p1",
\ and then print "123".
...
If we look at the compiled definition for P1 in Gforth it shows the following:
see p1
: p1
[COMPILE] partial1 #123 . ] ; ok
This shows that NAME>COMPILE is indeed doing what it should do. It is performing the compilation semantics regardless of STATE -- it is just
not the behavior needed for your test case.
NB: your code doesn't violate the current specifications for
"set-compsem" and "name>compile".
The implementation of "name>compile" in Gforth just effectively
imposes a restriction on programs — a program should perform xt from
"name>compile" only in compilation state to perform the corresponding
compilation semantics (in the general case).
Yes, it appears that this is the case for the example shown.
This restriction is similar to one that the popular implementation
for "postpone" imposes.
And, as you can see, the dual-xt approach has not allowed to avoid
this problem.
Not with my example. Is it a fundamental limitation or is my definition
of PARTIAL1 just not the compatible one? I'm not sure.
On 10/28/22 10:30, Ruvim wrote:
The problem stems from your original definition of PARTIAL1 :
: 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
;
There was no need for you to bring STATE into this definition. The
following works just as well for all subsequent definitions using
PARTIAL1, including your closures.
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> postpone literal compile, postpone ;
; ok
On 10/28/22 10:30, Ruvim wrote:[...]
The problem stems from your original definition of PARTIAL1 :
: 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
;
There was no need for you to bring STATE into this definition. The
following works just as well for all subsequent definitions using
PARTIAL1, including your closures.
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> postpone literal compile, postpone ;
;
On 2022-10-28 16:39, Krishna Myneni wrote:
A test case
: [bar] 123 ['] . partial1 compile, ; immediate
: baz [bar] 456 . ;
baz \ should print "123 456"
This test will not work on some standard system, since it has an environmental dependency on creation definitions while compiling another definition. kForth complies to this dependency.
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> postpone literal compile, postpone ;
;
It does not work in another test (see above).
On 10/29/22 03:19, Ruvim wrote:
On 2022-10-28 16:39, Krishna Myneni wrote:
A test case..
: [bar] 123 ['] . partial1 compile, ; immediate
: baz [bar] 456 . ;
baz \ should print "123 456"
This test will not work on some standard system, since it has an
environmental dependency on creation definitions while compiling
another definition. kForth complies to this dependency.
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> postpone literal compile, postpone ;
;
It does not work in another test (see above).
The simpler definition of PARTIAL1 works fine under kForth.
Your test,
: [bar] 123 ['] . partial1 compile, ; immediate
can be simplified to be
: bar 123 ['] partial1 compile, ;
: baz [ bar ] 456 . ;
baz
123 456 ok
There is no need for all of the contortions of EXECUTE-INTERPRETIVELY
and the STATE dependence of PARTIAL1 if a system allows
: NAME ... [ :NONAME ... ; ] ... ;
On 2022-10-29 11:28, Krishna Myneni wrote:
On 10/29/22 03:19, Ruvim wrote:
On 2022-10-28 16:39, Krishna Myneni wrote:..
A test case
: [bar] 123 ['] . partial1 compile, ; immediate
: baz [bar] 456 . ;
baz \ should print "123 456"
This test will not work on some standard system, since it has an
environmental dependency on creation definitions while compiling
another definition. kForth complies to this dependency.
: partial1 ( x xt1 -- xt2 )
2>r :noname r> r> postpone literal compile, postpone ;
;
It does not work in another test (see above).
The simpler definition of PARTIAL1 works fine under kForth.
It does not work in the above test in kForth:
: baz [bar] 456 . ;
Line 9: VM Error(-276): End of definition with no beginning
But my more complex definition of "partial1" works.
Your test,
: [bar] 123 ['] . partial1 compile, ; immediate
can be simplified to be
: bar 123 ['] partial1 compile, ;
: baz [ bar ] 456 . ;
baz
123 456 ok
A test purpose is not to solve a practical task, but to check how a
system or program behaves in some edge cases.
The word "partial1", as it was initially defined by me, has a
restriction in a standard program: it cannot be performed if the current definition exists (due to 3.4.5).
It's OK if you can live with this restriction. But if a system already implements the corresponding capabilities under the hood, I would prefer
to avoid this restriction, than document/explain it to users.
BTW, it's possible to define "partial1" without this restriction in a standard-compliant way, but it will be significant larger and slightly
less efficient in run-time.
There is no need for all of the contortions of EXECUTE-INTERPRETIVELY
and the STATE dependence of PARTIAL1 if a system allows
: NAME ... [ :NONAME ... ; ] ... ;
Well, it looks like you admit that the dual-xt approach does not provide
any special means to define a more advanced "partial1".
On 10/29/22 07:29, Ruvim wrote:[...]
[...]The word "partial1", as it was initially defined by me, has a
restriction in a standard program: it cannot be performed if the
current definition exists (due to 3.4.5).
It's OK if you can live with this restriction. But if a system already
implements the corresponding capabilities under the hood, I would
prefer to avoid this restriction, than document/explain it to users.
There is no need for all of the contortions of EXECUTE-INTERPRETIVELY
and the STATE dependence of PARTIAL1 if a system allows
: NAME ... [ :NONAME ... ; ] ... ;
Well, it looks like you admit that the dual-xt approach does not
provide any special means to define a more advanced "partial1".
All I am saying is that the simple definition of PARTIAL1 which has no reference to STATE suits our purpose at hand, e.g. for closures. It does
so regardless of whether or not a system adheres to 3.4.5 of the
standard. The simple definition of PARTIAL1 will not pass your most
recent test.
I simply point out that your test of [BAR] is no drawback for systems
which do violate 3.4.5 and allow execution of :NONAME while the
compilation of the current definition is suspended.
You are adding an
extraordinary amount of complexity to allow for PARTIAL1 to be used in
all possible corner cases -- this is simply not a practical approach
because such complexity will be avoided by programmers.
On 2022-10-29 16:03, Krishna Myneni wrote:
On 10/29/22 07:29, Ruvim wrote:[...]
[...]The word "partial1", as it was initially defined by me, has a
restriction in a standard program: it cannot be performed if the
current definition exists (due to 3.4.5).
It's OK if you can live with this restriction. But if a system
already implements the corresponding capabilities under the hood, I
would prefer to avoid this restriction, than document/explain it to
users.
There is no need for all of the contortions of
EXECUTE-INTERPRETIVELY and the STATE dependence of PARTIAL1 if a
system allows
: NAME ... [ :NONAME ... ; ] ... ;
Well, it looks like you admit that the dual-xt approach does not
provide any special means to define a more advanced "partial1".
All I am saying is that the simple definition of PARTIAL1 which has no
reference to STATE suits our purpose at hand, e.g. for closures. It
does so regardless of whether or not a system adheres to 3.4.5 of the
standard. The simple definition of PARTIAL1 will not pass your most
recent test.
I simply point out that your test of [BAR] is no drawback for systems
which do violate 3.4.5 and allow execution of :NONAME while the
compilation of the current definition is suspended.
Just a side note.
The section "3.4.5" does not restrict a system, but only a program.
During compilation of a definition, a system may use data space at it's
own discretion.
And if your system provides an advanced "partial1" — it does not violate "3.4.5".
[...]
You are adding an extraordinary amount of complexity to allow for
PARTIAL1 to be used in all possible corner cases -- this is simply not
a practical approach because such complexity will be avoided by
programmers.
I can't agree with that, but agree with Anton's formulation: "As a
system implementor, you may be tempted to cut corners, but that means
you also cut the corner cases, and users of your system will get
unpleasant surprises"[1]
On 11/2/22 14:11, Ruvim wrote:
On 2022-10-29 16:03, Krishna Myneni wrote:
On 10/29/22 07:29, Ruvim wrote:[...]
There is no need for all of the contortions of
EXECUTE-INTERPRETIVELY and the STATE dependence of PARTIAL1 if a
system allows
: NAME ... [ :NONAME ... ; ] ... ;
Well, it looks like you admit that the dual-xt approach does not
provide any special means to define a more advanced "partial1".
All I am saying is that the simple definition of PARTIAL1 which has
no reference to STATE suits our purpose at hand, e.g. for closures.
It does so regardless of whether or not a system adheres to 3.4.5 of
the standard. The simple definition of PARTIAL1 will not pass your
most recent test.
I simply point out that your test of [BAR] is no drawback for systems
which do violate 3.4.5 and allow execution of :NONAME while the
compilation of the current definition is suspended.
Just a side note.
The section "3.4.5" does not restrict a system, but only a program.
During compilation of a definition, a system may use data space at
it's own discretion.
And if your system provides an advanced "partial1" — it does not
violate "3.4.5".
I never claimed that a system in which a "first class" PARTIAL1 may be written will violate 3.4.5.
My claim is that a system which permits a
:NONAME definition nested within a definition can support an advanced PARTIAL1
-- the two are different claims.
[...]
You are adding an extraordinary amount of complexity to allow for
PARTIAL1 to be used in all possible corner cases -- this is simply
not a practical approach because such complexity will be avoided by
programmers.
I can't agree with that, but agree with Anton's formulation: "As a
system implementor, you may be tempted to cut corners, but that means
you also cut the corner cases, and users of your system will get
unpleasant surprises"[1]
My argument is not against writing a first class PARTIAL1 which works as expected using POSTPONE '(tick) NAME>COMPILE etc.
My issue is
understanding what may be structurally wrong with the design of a Forth system which makes writing a first class PARTIAL1 a complex task. Dual-semantics systems go a long way towards simplifying the definition
of first class words. It looks like implementing a first class PARTIAL1
in a dual-semantics systems may still be complex. So what's the
underlying problem, or, to put it another way, what is required of the
Forth system design to enable the simple definition of such a word ?
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 300 |
Nodes: | 16 (2 / 14) |
Uptime: | 47:54:20 |
Calls: | 6,710 |
Calls today: | 3 |
Files: | 12,243 |
Messages: | 5,354,635 |
Posted today: | 1 |