• locals and pure-stack closures (was: GOTO and LABEL ...)

    From Anton Ertl@21:1/5 to dxforth on Wed Apr 5 07:33:35 2023
    dxforth <dxforth@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler?

    One interesting case is the pure-stack closures of Gforth: It started
    out with me wanting to provide better support for the xt-passing style
    (as used frequently in functional programming languages).

    One problem that Forth (and even more so, C) has for this style is
    that there is no simple way to pass data that is not designed into the interface. E.g., if the word you call expects an xt with the stack
    effect ( n1 -- n2 ), and you want to pass [: n + ;] as this xt, where
    n is determined at run-time, how do you pass n?

    The solution of languages with lexically-scoped locals (e.g., Scheme)
    is to pass n through the lexical-scoping mechanism, resulting in
    something like the following:

    : n+xt ( n -- xt )
    {: n :} [: n + ;] ;

    This does not work with Forth-standard [: ... ;], because n is not
    visible inside, but Scheme supports code like this (with different
    syntax). This code does not work in any Forth system I know.

    The implementation of lexical scoping is complicated, so we (mostly
    Bernd Paysan) went for a simpler-to-implement approach with
    programmer-visible flat closures (a technique that's an implementation
    detail for Scheme), where the Forth programmer performs closure
    conversion (the compiler does it in Scheme). A naive application of
    closure conversion results in:

    : n+xt ( n -- xt )
    {: n :} n [{: n :}d n + ;] ;

    Here we have a local N in N+XT and a different local N in the closure.
    The value n is passed to the closure, stored in the closure data
    structure (in the dictionary in this case, indicated by the D in
    ":}D"), and stored into the local N when the xt of the closure is
    EXECUTEd; after that the rest of the code in the closure is performed.
    This code works in development Gforth (implementation and a lot of the by Bernd Paysan).

    One obvious simplification is:

    : n+xt ( n -- xt )
    [{: n :}d n + ;] ;

    In the presentation of this work at EuroForth 2018, I mentioned that
    one could get rid of the locals here completely by just passing a
    certain number of stack items from the closure construction to the
    closure execution time. In my presentation I suggested a syntax like:

    : n+xt ( n -- xt )
    1 0 [:d + ;] ;

    where the "1 0" says that 1 cell and 0 FP-stack items are passed to
    the closure. This was just a side comment, but some time later Bernd
    Paysan implemented this idea with the following syntax:

    : n+xt ( n -- xt )
    [n:d + ;] ;

    The "n" in "[n:d]" indicates a single cell; you can also use "d"
    (double cell), or "f" (one FP-stack item). This works in development
    Gforth, and is used in some places.

    This idea seems obvious in hindsight, given the requirements, but it
    was not at all obvious at the start, and, e.g., it's not present in
    Joy (a stack-based functional language without locals). We would have
    missed this idea if we had rejected everything to do with locals from
    the start.

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to Anton Ertl on Wed Apr 5 10:04:38 2023
    On Wednesday, April 5, 2023 at 3:50:16 AM UTC-5, Anton Ertl wrote:
    dxforth <dxf...@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler?
    One interesting case is the pure-stack closures of Gforth: It started
    out with me wanting to provide better support for the xt-passing style
    (as used frequently in functional programming languages).

    One problem that Forth (and even more so, C) has for this style is
    that there is no simple way to pass data that is not designed into the interface. E.g., if the word you call expects an xt with the stack
    effect ( n1 -- n2 ), and you want to pass [: n + ;] as this xt, where
    n is determined at run-time, how do you pass n?

    The solution of languages with lexically-scoped locals (e.g., Scheme)
    is to pass n through the lexical-scoping mechanism, resulting in
    something like the following:

    In zeptoforth I have opted for the following:

    closure import \ Yeah I know these are not closures per se, because they do not involve name-binding per se
    closure-size buffer: my-xt \ This will be ( n -- n ) once DO-BIND is executed
    : do-bind { n -- } n my-xt [: { n } n + ;] bind ;
    1 do-bind
    0 my-xt execute . \ Outputs: 1 ok
    1 my-xt execute . \ Outputs: 2 ok
    2 my-xt execute . \ Outputs: 3 ok
    2 do-bind
    0 my-xt execute . \ Outputs: 2 ok
    1 my-xt execute . \ Outputs: 3 ok
    2 my-xt execute . \ Outputs: 4 ok
    \ etc.

    While BIND, and its relatives 2BIND and NBIND, do not bind names per se,
    they initialize xt's to which values are bound, being passed to the executed code on the data stack, until they are executed again with the same xt,
    where the values will be rebound.

    These of course can be used within words, as seen below:

    closure import
    create my-array here $DE c, $AD c, $BE c, $EF c,
    4 constant my-array-len
    : test-iter ( n -- )
    closure-size [: { n my-xt }
    n my-xt [: { byte n } byte n + h.2 ;] bind
    my-array my-array-len my-xt citer
    ;] with-aligned-allot
    ;
    0 test-iter \ Outputs: DEADBEEF ok
    1 test-iter \ Outputs: DFAEBFF0 ok
    -1 test-iter \ Outputs: DDACBDEE ok

    This is not the nicest way of doing things, but without using heap allocation, something I much prefer to avoid, or hard-coding placing closures in the dictionary (e.g. what if something wants to do something else with the dictionary, or what if someone wants to store a closure in a structure or object?), something I also prefer to avoid, this seems like the best way
    to do this to me.

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to Travis Bemann on Wed Apr 5 10:09:39 2023
    On Wednesday, April 5, 2023 at 12:04:41 PM UTC-5, Travis Bemann wrote:
    On Wednesday, April 5, 2023 at 3:50:16 AM UTC-5, Anton Ertl wrote:
    dxforth <dxf...@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler?
    One interesting case is the pure-stack closures of Gforth: It started
    out with me wanting to provide better support for the xt-passing style
    (as used frequently in functional programming languages).

    One problem that Forth (and even more so, C) has for this style is
    that there is no simple way to pass data that is not designed into the interface. E.g., if the word you call expects an xt with the stack
    effect ( n1 -- n2 ), and you want to pass [: n + ;] as this xt, where
    n is determined at run-time, how do you pass n?

    The solution of languages with lexically-scoped locals (e.g., Scheme)
    is to pass n through the lexical-scoping mechanism, resulting in
    something like the following:
    In zeptoforth I have opted for the following:

    closure import \ Yeah I know these are not closures per se, because they do not involve name-binding per se
    closure-size buffer: my-xt \ This will be ( n -- n ) once DO-BIND is executed
    : do-bind { n -- } n my-xt [: { n } n + ;] bind ;
    1 do-bind
    0 my-xt execute . \ Outputs: 1 ok
    1 my-xt execute . \ Outputs: 2 ok
    2 my-xt execute . \ Outputs: 3 ok
    2 do-bind
    0 my-xt execute . \ Outputs: 2 ok
    1 my-xt execute . \ Outputs: 3 ok
    2 my-xt execute . \ Outputs: 4 ok
    \ etc.

    While BIND, and its relatives 2BIND and NBIND, do not bind names per se, they initialize xt's to which values are bound, being passed to the executed code on the data stack, until they are executed again with the same xt, where the values will be rebound.

    These of course can be used within words, as seen below:

    closure import
    create my-array here $DE c, $AD c, $BE c, $EF c,
    4 constant my-array-len
    : test-iter ( n -- )
    closure-size [: { n my-xt }
    n my-xt [: { byte n } byte n + h.2 ;] bind
    my-array my-array-len my-xt citer
    ;] with-aligned-allot
    ;
    0 test-iter \ Outputs: DEADBEEF ok
    1 test-iter \ Outputs: DFAEBFF0 ok
    -1 test-iter \ Outputs: DDACBDEE ok

    This is not the nicest way of doing things, but without using heap allocation,
    something I much prefer to avoid, or hard-coding placing closures in the dictionary (e.g. what if something wants to do something else with the dictionary, or what if someone wants to store a closure in a structure or object?), something I also prefer to avoid, this seems like the best way
    to do this to me.

    Travis

    Minor correction:

    create my-array here $DE c, $AD c, $BE c, $EF c,
    4 constant my-array-len

    should be:

    create my-array here $DE c, $AD c, $BE c, $EF c,
    here swap - constant my-array-len

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From fabianorezende26@gmail.com@21:1/5 to All on Wed Apr 5 11:23:30 2023
    Em quarta-feira, 5 de abril de 2023 às 14:09:41 UTC-3, Travis Bemann escreveu:
    On Wednesday, April 5, 2023 at 12:04:41 PM UTC-5, Travis Bemann wrote:
    On Wednesday, April 5, 2023 at 3:50:16 AM UTC-5, Anton Ertl wrote:
    dxforth <dxf...@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler? One interesting case is the pure-stack closures of Gforth: It started out with me wanting to provide better support for the xt-passing style (as used frequently in functional programming languages).

    One problem that Forth (and even more so, C) has for this style is
    that there is no simple way to pass data that is not designed into the interface. E.g., if the word you call expects an xt with the stack effect ( n1 -- n2 ), and you want to pass [: n + ;] as this xt, where
    n is determined at run-time, how do you pass n?

    The solution of languages with lexically-scoped locals (e.g., Scheme)
    is to pass n through the lexical-scoping mechanism, resulting in something like the following:
    In zeptoforth I have opted for the following:

    closure import \ Yeah I know these are not closures per se, because they do not involve name-binding per se
    closure-size buffer: my-xt \ This will be ( n -- n ) once DO-BIND is executed
    : do-bind { n -- } n my-xt [: { n } n + ;] bind ;
    1 do-bind
    0 my-xt execute . \ Outputs: 1 ok
    1 my-xt execute . \ Outputs: 2 ok
    2 my-xt execute . \ Outputs: 3 ok
    2 do-bind
    0 my-xt execute . \ Outputs: 2 ok
    1 my-xt execute . \ Outputs: 3 ok
    2 my-xt execute . \ Outputs: 4 ok
    \ etc.

    While BIND, and its relatives 2BIND and NBIND, do not bind names per se, they initialize xt's to which values are bound, being passed to the executed
    code on the data stack, until they are executed again with the same xt, where the values will be rebound.

    These of course can be used within words, as seen below:

    closure import
    create my-array here $DE c, $AD c, $BE c, $EF c,
    4 constant my-array-len
    : test-iter ( n -- )
    closure-size [: { n my-xt }
    n my-xt [: { byte n } byte n + h.2 ;] bind
    my-array my-array-len my-xt citer
    ;] with-aligned-allot
    ;
    0 test-iter \ Outputs: DEADBEEF ok
    1 test-iter \ Outputs: DFAEBFF0 ok
    -1 test-iter \ Outputs: DDACBDEE ok

    This is not the nicest way of doing things, but without using heap allocation,
    something I much prefer to avoid, or hard-coding placing closures in the dictionary (e.g. what if something wants to do something else with the dictionary, or what if someone wants to store a closure in a structure or object?), something I also prefer to avoid, this seems like the best way to do this to me.

    Travis
    Minor correction:
    create my-array here $DE c, $AD c, $BE c, $EF c,
    4 constant my-array-len
    should be:
    create my-array here $DE c, $AD c, $BE c, $EF c,
    here swap - constant my-array-len
    Forth made me value local variables. You can run code "10x" faster and "10x" smaller, but at the cost of taking "10x" and making "10x" more mistakes.
    A very simple design feature that saved me a lot of pain

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to fabianor...@gmail.com on Wed Apr 5 11:38:21 2023
    On Wednesday, April 5, 2023 at 1:23:32 PM UTC-5, fabianor...@gmail.com wrote:
    Forth made me value local variables. You can run code "10x" faster and "10x" smaller, but at the cost of taking "10x" and making "10x" more mistakes.
    A very simple design feature that saved me a lot of pain

    I don't think that doing things via traditional stack operations is faster. As I mention, when
    storing local variables on the return stack, fetching a local variable is just three instructions
    and storing a local variable is a mere two instructions on ARMv6-M. The only stack
    operation that is nearly that fast is PICK which, with constant folding, can be optimized
    in many cases down to three instructions on ARMv6-M. But to do any complex operations
    on the data stack ends up requiring many stack operations such as SWAP's, ROT's,
    2SWAP's, 2DUP's, and so on, which quickly add up, especially when compiling to native
    code (unless you have a register-assigning compiler, like Mecrisp-Stellaris's, and even then
    Mecrisp-Stellaris has to evict stack entries stored in registers whenever you call a word that
    cannot be inlined). Consequently, for complex code, I would expect local variables stored
    on the return stack to be at least as fast if not faster than traditional data stack operation
    with its associated stack churn on an architecture such as ARMv6-M (I would expect even
    better performance on ARMv7-M because fetching a local variable can be reduced to
    two instructions on that architecture).

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to Travis Bemann on Wed Apr 5 13:48:52 2023
    On Wednesday, April 5, 2023 at 1:38:23 PM UTC-5, Travis Bemann wrote:
    On Wednesday, April 5, 2023 at 1:23:32 PM UTC-5, fabianor...@gmail.com wrote:
    Forth made me value local variables. You can run code "10x" faster and "10x" smaller, but at the cost of taking "10x" and making "10x" more mistakes.
    A very simple design feature that saved me a lot of pain
    I don't think that doing things via traditional stack operations is faster. As I mention, when
    storing local variables on the return stack, fetching a local variable is just three instructions
    and storing a local variable is a mere two instructions on ARMv6-M. The only stack
    operation that is nearly that fast is PICK which, with constant folding, can be optimized
    in many cases down to three instructions on ARMv6-M. But to do any complex operations
    on the data stack ends up requiring many stack operations such as SWAP's, ROT's,
    2SWAP's, 2DUP's, and so on, which quickly add up, especially when compiling to native
    code (unless you have a register-assigning compiler, like Mecrisp-Stellaris's, and even then
    Mecrisp-Stellaris has to evict stack entries stored in registers whenever you call a word that
    cannot be inlined). Consequently, for complex code, I would expect local variables stored
    on the return stack to be at least as fast if not faster than traditional data stack operation
    with its associated stack churn on an architecture such as ARMv6-M (I would expect even
    better performance on ARMv7-M because fetching a local variable can be reduced to
    two instructions on that architecture).

    I should have noted that DUP and DROP are faster than a constant PICK on ARMv6-M,
    and SWAP is as fast, but that is about it.

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Travis Bemann on Thu Apr 6 06:51:31 2023
    Travis Bemann <tabemann@gmail.com> writes:
    On Wednesday, April 5, 2023 at 3:50:16=E2=80=AFAM UTC-5, Anton Ertl wrote:
    dxforth <dxf...@gmail.com> writes:=20
    Certainly locals are easier and folks find ways of justifying them.=20
    My concern in using them would be what have I missed that's simpler?
    One interesting case is the pure-stack closures of Gforth: It started=20
    out with me wanting to provide better support for the xt-passing style=20
    (as used frequently in functional programming languages).=20
    =20
    One problem that Forth (and even more so, C) has for this style is=20
    that there is no simple way to pass data that is not designed into the=20
    interface. E.g., if the word you call expects an xt with the stack=20
    effect ( n1 -- n2 ), and you want to pass [: n + ;] as this xt, where=20
    n is determined at run-time, how do you pass n?=20
    =20
    The solution of languages with lexically-scoped locals (e.g., Scheme)=20
    is to pass n through the lexical-scoping mechanism, resulting in=20
    something like the following:=20

    In zeptoforth I have opted for the following:

    closure import \ Yeah I know these are not closures per se, because they do=
    not involve name-binding per se

    I also call them closures, because they provide the same functionality
    that closures provide in statically scoped languages.

    closure-size buffer: my-xt \ This will be ( n -- n ) once DO-BIND is execut= >ed
    : do-bind { n -- } n my-xt [: { n } n + ;] bind ;

    Anything wrong with

    : do-bind ( n -- ) my-xt [: + ;] bind ;

    ?

    1 do-bind
    0 my-xt execute . \ Outputs: 1 ok

    This assumes that the start address of the data structure written by
    BIND also serves as xt. In development Gforth the xt of an anonymous
    word ist two cells after the start of the data structure.

    This is not the nicest way of doing things, but without using heap allocati= >on,
    something I much prefer to avoid, or hard-coding placing closures in the >dictionary (e.g. what if something wants to do something else with the >dictionary, or what if someone wants to store a closure in a structure or >object?), something I also prefer to avoid, this seems like the best way
    to do this to me.

    This is the classic Forth technique of letting the caller provide a
    buffer that the callee fills. And given that here the buffer size is
    known in advance, that's not a bad solution (for an example of badness
    when the size is not known, take a look at READ-LINE). Nevertheless,
    in Gforth we took a different approach:

    Closures can be allocated in the Dictionary (D), on the heap (H), or
    on the locals stack (L), or you can use an allocation word you pass as
    xt. Given that our closures can be arbitrarily large, I cannot think
    of a comfortable interface that uses a pre-allocated buffer.

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Thu Apr 6 09:44:52 2023
    minforth <minforth@arcor.de> writes:
    Anton Ertl schrieb am Donnerstag, 6. April 2023 um 09:22:49 UTC+2:
    Closures can be allocated in the Dictionary (D), on the heap (H), or
    on the locals stack (L), or you can use an allocation word you pass as
    xt. Given that our closures can be arbitrarily large, I cannot think
    of a comfortable interface that uses a pre-allocated buffer.

    Given that closures can be called somewhere and somewhen later,
    even when the enclosing word has already terminated, I am wondering
    how the locals stack comes into play here.

    The programmer is responsible: If the closure lives longer than the
    definition containing the closure code, the programmer will not choose
    to allocate the closure on the locals stack.

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Thu Apr 6 02:30:33 2023
    Anton Ertl schrieb am Donnerstag, 6. April 2023 um 09:22:49 UTC+2:
    Closures can be allocated in the Dictionary (D), on the heap (H), or
    on the locals stack (L), or you can use an allocation word you pass as
    xt. Given that our closures can be arbitrarily large, I cannot think
    of a comfortable interface that uses a pre-allocated buffer.

    Given that closures can be called somewhere and somewhen later,
    even when the enclosing word has already terminated, I am wondering
    how the locals stack comes into play here.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Anton Ertl on Thu Apr 6 13:04:12 2023
    On 2023-04-05 07:33, Anton Ertl wrote:
    dxforth <dxforth@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler?

    One interesting case is the pure-stack closures of Gforth
    [...]

    In the presentation of this work at EuroForth 2018, I mentioned that
    one could get rid of the locals here completely by just passing a
    certain number of stack items from the closure construction to the
    closure execution time. In my presentation I suggested a syntax like:

    : n+xt ( n -- xt )
    1 0 [:d + ;] ;

    where the "1 0" says that 1 cell and 0 FP-stack items are passed to
    the closure. This was just a side comment, but some time later Bernd
    Paysan implemented this idea with the following syntax:

    : n+xt ( n -- xt )
    [n:d + ;] ;

    The "n" in "[n:d]" indicates a single cell; you can also use "d"
    (double cell), or "f" (one FP-stack item). This works in development
    Gforth, and is used in some places.

    This idea seems obvious in hindsight, given the requirements, but it
    was not at all obvious at the start, and, e.g., it's not present in
    Joy (a stack-based functional language without locals).

    We would have missed this idea if we had rejected everything
    to do with locals from the start.

    I cannot agree. As I already mentioned [1], this idea is actually the conception of partial application, which is completely independent of
    local variables. I use partial application a lot in Forth.

    Perhaps the main difference between partially applied functions and
    closures is following.
    — A partially applied function is created by binding anonymous
    *positional* parameters.
    — A closure (a lexical closure) is created by binding *named* objects
    from the lexical environment.

    Obviously, partial application can be implemented using closures. But it
    isn't necessary. And the former is far simpler than the later, because
    having locals, the same name from the lexical environment can be
    resolved to different objects at different run-time points.


    In Forth, partial application was always available, but only static in
    the dictionary [2].

    So a problem is how to create a partially applied function dynamically,
    and how to free resources of an already unneeded function (apart from
    the restricted "forget" or "marker" means).


    As a straight-forward portable solution, I can consider an approach of preliminary creating a sufficient number of unnamed definitions (a kind
    of thunk), and maintaining a list of free thunks.

    When a partially applied function is created, you exclude the top thunk
    from the list and configure it. When the function is freed, you place
    the thunk back to the list.





    [1] Re: Closures in kForth, 2022-10-17T09:50:18Z news:tij8h5$3dgsi$1@dont-email.me https://groups.google.com/g/comp.lang.forth/c/g-Je7CXe6DA/m/3lnOrgGiBQAJ

    [2] "does>" is a kind of partial application with some additional features. 「From the formal point of view, "does>" in run-time makes partial application. It partially applies the part "X" in "does> X ;" to the
    ADDR, producing a new definition, and replaces the execution semantics
    of the most recent definition by the execution semantics of this new definition.」
    Re: set-optimizer as an API for per-word optimizer, 2022-11-23T17:26:00Z news:tlll39$cu1p$1@dont-email.me https://groups.google.com/g/comp.lang.forth/c/3wPnVKmHcIM/m/NLZcBP6MAQAJ


    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to Anton Ertl on Thu Apr 6 07:36:21 2023
    On Thursday, April 6, 2023 at 2:22:49 AM UTC-5, Anton Ertl wrote:
    Travis Bemann <tabe...@gmail.com> writes:
    On Wednesday, April 5, 2023 at 3:50:16=E2=80=AFAM UTC-5, Anton Ertl wrote: >> dxforth <dxf...@gmail.com> writes:=20
    Certainly locals are easier and folks find ways of justifying them.=20
    My concern in using them would be what have I missed that's simpler?
    One interesting case is the pure-stack closures of Gforth: It started=20 >> out with me wanting to provide better support for the xt-passing style=20 >> (as used frequently in functional programming languages).=20
    =20
    One problem that Forth (and even more so, C) has for this style is=20
    that there is no simple way to pass data that is not designed into the=20 >> interface. E.g., if the word you call expects an xt with the stack=20
    effect ( n1 -- n2 ), and you want to pass [: n + ;] as this xt, where=20 >> n is determined at run-time, how do you pass n?=20
    =20
    The solution of languages with lexically-scoped locals (e.g., Scheme)=20 >> is to pass n through the lexical-scoping mechanism, resulting in=20
    something like the following:=20

    In zeptoforth I have opted for the following:

    closure import \ Yeah I know these are not closures per se, because they do=
    not involve name-binding per se
    I also call them closures, because they provide the same functionality
    that closures provide in statically scoped languages.

    Yeah, that is precisely why I named them so in zeptoforth.

    closure-size buffer: my-xt \ This will be ( n -- n ) once DO-BIND is execut= >ed
    : do-bind { n -- } n my-xt [: { n } n + ;] bind ;
    Anything wrong with

    : do-bind ( n -- ) my-xt [: + ;] bind ;

    ?

    Nope, that was just a completely gratuitous use of local variables. Hell, you could define DO-BIND as:

    : do-bind ( n -- ) my-xt ['] + bind ;

    if you so saw fit.

    1 do-bind
    0 my-xt execute . \ Outputs: 1 ok
    This assumes that the start address of the data structure written by
    BIND also serves as xt. In development Gforth the xt of an anonymous
    word ist two cells after the start of the data structure.

    Yes, the start address of a closure in zeptoforth as written by BIND is also its
    xt.

    This is not the nicest way of doing things, but without using heap allocati= >on,
    something I much prefer to avoid, or hard-coding placing closures in the >dictionary (e.g. what if something wants to do something else with the >dictionary, or what if someone wants to store a closure in a structure or >object?), something I also prefer to avoid, this seems like the best way >to do this to me.
    This is the classic Forth technique of letting the caller provide a
    buffer that the callee fills. And given that here the buffer size is
    known in advance, that's not a bad solution (for an example of badness
    when the size is not known, take a look at READ-LINE). Nevertheless,
    in Gforth we took a different approach:

    Closures can be allocated in the Dictionary (D), on the heap (H), or
    on the locals stack (L), or you can use an allocation word you pass as
    xt. Given that our closures can be arbitrarily large, I cannot think
    of a comfortable interface that uses a pre-allocated buffer.

    I have three kinds of closures, one-cell closures created with BIND
    ( x closure-addr called-xt -- ) and whose sizes are given by CLOSURE-SIZE ( -- bytes ),
    double-cell closures created with 2BIND ( d closure-addr called-xt -- ) and whose sizes
    are given by 2CLOSURE-SIZE ( -- bytes ), and arbitrary-cell closures created with NBIND
    ( xn ... x0 count closure-addr called-xt ) and whose sizes are given by NCLOSURE-SIZE
    ( count -- bytes). The existence of such *CLOSURE-SIZE words enables the user to
    allot/allocate however much space they need for such closures ahead of time. To me
    the idea to limit closures to the dictionary (which is also used like a stack for
    ephemeral storage, as done here with WITH-ALIGNED-ALLOT), a heap (note that heaps are not special in zeptoforth, they may be created like any other data structure),
    or the locals stack (which in zeptoforth is the return stack) seems rather restrictive
    to me.

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Anton Ertl on Fri Apr 7 10:51:48 2023
    On 5/04/2023 5:33 pm, Anton Ertl wrote:
    dxforth <dxforth@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler?

    One interesting case is the pure-stack closures of Gforth: It started
    out with me wanting to provide better support for the xt-passing style
    (as used frequently in functional programming languages).

    One problem that Forth (and even more so, C) has for this style is
    that there is no simple way to pass data that is not designed into the interface. E.g., if the word you call expects an xt with the stack
    effect ( n1 -- n2 ), and you want to pass [: n + ;] as this xt, where
    n is determined at run-time, how do you pass n?

    IIUC what's desired is a function that passes parameters - not through
    normal channels - but through the ether? ISTM academics who spend their
    days sorting compilers into men and boys (sheep and goats?) aren't going
    to give much thought to the likes of Forth. Has Knuth ever said what he
    thinks of Forth - or perhaps no one been game to ask?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Ruvim on Fri Apr 7 08:11:49 2023
    On 2023-04-06 13:04, Ruvim wrote:
    On 2023-04-05 07:33, Anton Ertl wrote:
    dxforth <dxforth@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler?

    One interesting case is the pure-stack closures of Gforth
    [...]

    In the presentation of this work at EuroForth 2018, I mentioned that
    one could get rid of the locals here completely by just passing a
    certain number of stack items from the closure construction to the
    closure execution time.  In my presentation I suggested a syntax like:

    : n+xt ( n -- xt )
       1 0 [:d + ;] ;

    where the "1 0" says that 1 cell and 0 FP-stack items are passed to
    the closure.  This was just a side comment, but some time later Bernd
    Paysan implemented this idea with the following syntax:

    : n+xt ( n -- xt )
       [n:d + ;] ;

    The "n" in "[n:d]" indicates a single cell; you can also use "d"
    (double cell), or "f" (one FP-stack item).  This works in development
    Gforth, and is used in some places.

    This idea seems obvious in hindsight, given the requirements, but it
    was not at all obvious at the start, and, e.g., it's not present in
    Joy (a stack-based functional language without locals).

    BTW, partial application is available in Joy via "cons". In Cat it's
    present as "papply".

    In Joy:

    DEFINE np_xt == [ + ] cons.

    1 np_xt dup . \ prints "[1 +]"
    2 swap i . \ prints "3"



    We would have missed this idea if we had rejected everything to do
    with locals from the start.

    I cannot agree. As I already mentioned [1], this idea is actually the conception of partial application, which is completely independent of
    local variables. I use partial application a lot in Forth.

    Perhaps the main difference between partially applied functions and
    closures is following.
    — A partially applied function is created by binding anonymous
    *positional* parameters.
    — A closure (a lexical closure) is created by binding *named* objects
    from the lexical environment.

    Obviously, partial application can be implemented using closures. But it isn't necessary. And the former is far simpler than the later, because
    having locals, the same name from the lexical environment can be
    resolved to different objects at different run-time points.


    A closure usually means a lexical closure.

    Without local variables in general, or without *ability* to bind the
    outer local variables, closures are trivial. I'm not sure it's still
    correct to call such functions "closures". Therefor, in Factor, Joy,
    Cat, Forth, such functions are intentionally called "quotations".

    If we don't call quotations "closures", then we rather shouldn't call
    partially applied functions "closures" too.


    Also, if the outer local variables are not bound in a function, it's
    probably confusing if this function would be called "closure".


    Well, if we still want to call partially applied functions "closures",
    they shouldn't be "lexical closures", but some other kind of closures.

    And "pure-stack closures" is not well suited either, I think.


    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Ruvim on Fri Apr 7 02:12:09 2023
    Ruvim schrieb am Freitag, 7. April 2023 um 10:11:54 UTC+2:
    On 2023-04-06 13:04, Ruvim wrote:
    On 2023-04-05 07:33, Anton Ertl wrote:
    dxforth <dxf...@gmail.com> writes:
    Certainly locals are easier and folks find ways of justifying them.
    My concern in using them would be what have I missed that's simpler?

    One interesting case is the pure-stack closures of Gforth
    [...]

    In the presentation of this work at EuroForth 2018, I mentioned that
    one could get rid of the locals here completely by just passing a
    certain number of stack items from the closure construction to the
    closure execution time. In my presentation I suggested a syntax like:

    : n+xt ( n -- xt )
    1 0 [:d + ;] ;

    where the "1 0" says that 1 cell and 0 FP-stack items are passed to
    the closure. This was just a side comment, but some time later Bernd
    Paysan implemented this idea with the following syntax:

    : n+xt ( n -- xt )
    [n:d + ;] ;

    The "n" in "[n:d]" indicates a single cell; you can also use "d"
    (double cell), or "f" (one FP-stack item). This works in development
    Gforth, and is used in some places.

    This idea seems obvious in hindsight, given the requirements, but it
    was not at all obvious at the start, and, e.g., it's not present in
    Joy (a stack-based functional language without locals).
    BTW, partial application is available in Joy via "cons". In Cat it's
    present as "papply".

    In Joy:

    DEFINE np_xt == [ + ] cons.

    1 np_xt dup . \ prints "[1 +]"
    2 swap i . \ prints "3"
    We would have missed this idea if we had rejected everything to do
    with locals from the start.

    I cannot agree. As I already mentioned [1], this idea is actually the conception of partial application, which is completely independent of local variables. I use partial application a lot in Forth.

    Perhaps the main difference between partially applied functions and closures is following.
    — A partially applied function is created by binding anonymous *positional* parameters.
    — A closure (a lexical closure) is created by binding *named* objects from the lexical environment.

    Obviously, partial application can be implemented using closures. But it isn't necessary. And the former is far simpler than the later, because having locals, the same name from the lexical environment can be
    resolved to different objects at different run-time points.
    A closure usually means a lexical closure.

    Without local variables in general, or without *ability* to bind the
    outer local variables, closures are trivial. I'm not sure it's still
    correct to call such functions "closures". Therefor, in Factor, Joy,
    Cat, Forth, such functions are intentionally called "quotations".

    If we don't call quotations "closures", then we rather shouldn't call partially applied functions "closures" too.


    Also, if the outer local variables are not bound in a function, it's probably confusing if this function would be called "closure".


    Well, if we still want to call partially applied functions "closures",
    they shouldn't be "lexical closures", but some other kind of closures.

    And "pure-stack closures" is not well suited either, I think.

    Agreed. Common understanding is based on agreed nomenclature.
    Otherwise Babylonian language confusion will bring .. nothing.

    IMO some "compliance" test cases for closures should be developed as well.
    Only for reference here's the one for quotations (200x draft):
    T{ : q1 [: 1 ;] ; q1 execute -> 1 }T
    T{ : q2 [: [: 2 ;] ;] ; q2 execute execute -> 2 }T
    T{ : q3 {: a :} [: {: a b :} b a ;] ; 1 2 3 q3 execute -> 2 1 }T
    T{ : q4 [: dup if dup 1- recurse then ;] ; 3 q4 execute .s -> 3 2 1 0 }T
    T{ : q5 [: does> drop 4 ;] 5 swap ; create x q5 execute x -> 5 4 }T
    T{ : q6 {: a :} [: {: a b :} b a ;] a 1+ ; 1 2 q6 swap execute -> 3 1 }T
    T{ 1 2 q6 q6 swap execute execute -> 4 1 }T
    T{ 1 2 3 q3 swap q6 swap execute execute -> 3 1 }T

    For Forth 'closures' perhaps starting with something like (assuming [[: ;]] syntax)
    T{ : c1 {: a :} [[: {: b :} a b + ;]] ; -> }T
    T{ 10 c1 value clo1 -> }T
    T{ 20 c1 value clo2 -> }T
    T{ 1 clo1 execute -> 11 }T
    T{ 2 clo2 execute -> 21 }T
    T{ : c2 {: a :} [[: {: b :} a b + dup to a ;]] ; -> }T
    ... etc

    Arguably such 'closures' wouldn't be lexical, only be a natural extension of Forth
    quotations. But I am still struggling with finding good applications, apart from
    declaring some dumb counters.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Ruvim on Fri Apr 7 11:51:18 2023
    Ruvim <ruvim.pinka@gmail.com> writes:
    BTW, partial application is available in Joy via "cons".

    It's unsurprising that I missed that; given the name, I think of
    Lisp's cons (which CONStructs a list cell).

    In Cat it's
    present as "papply".\

    This is also one of the names used in <https://en.wikipedia.org/wiki/Partial_application>; Cat also has
    "apply" which does what Forth calls EXECUTE. So in Forth this word
    might be called PARTIAL-EXECUTE or PEXECUTE.

    Without local variables in general, or without *ability* to bind the
    outer local variables, closures are trivial. I'm not sure it's still
    correct to call such functions "closures". Therefor, in Factor, Joy,
    Cat, Forth, such functions are intentionally called "quotations".

    If we don't call quotations "closures", then we rather shouldn't call >partially applied functions "closures" too.

    That does not follow. Quotations neither are closures nor do they
    serve the purpose of closures. What Gforth calls closures is intended
    to be used for the purpose that closures (as well as curried and partially-applied functions) are used for in Scheme; and if we have

    [n:d + ;]

    (what I call a pure-stack closure), it's not a partially-applied (or partially-EXECUTEd?) colon definition, because no partial
    application/execution has happened, and it's not a colon definition.
    It might be called a curried quotation if you are unhappy with
    "closure", but I am happy with "closure" (but a also call the
    resulting xt the xt of a closure, so I am quite loose in terminology
    here.

    Also, if the outer local variables are not bound in a function, it's
    probably confusing if this function would be called "closure".

    About as confusing as calling a colon definition a "function".

    Well, if we still want to call partially applied functions "closures",
    they shouldn't be "lexical closures", but some other kind of closures.

    "Flat closure" or "Gforth closure" is fine with me.

    And "pure-stack closures" is not well suited either, I think.

    "Pure-stack" contrasts with the closures that have locals on the
    inside:

    pure-stack closure: [n:d + ;]
    other Gforth closure: [{: n :}d n + ;]

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Ruvim on Fri Apr 7 11:20:39 2023
    Ruvim <ruvim.pinka@gmail.com> writes:
    On 2023-04-05 07:33, Anton Ertl wrote:
    We would have missed this idea if we had rejected everything
    to do with locals from the start.

    I cannot agree.

    You were not involved.

    As I already mentioned [1], this idea is actually the
    conception of partial application, which is completely independent of
    local variables.

    Or of currying; from the caller's perspective the difference is small <https://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application>.
    Plus, I had not known the term before, although it sounded familiar: <https://en.wikipedia.org/wiki/Partial_application> starts by saying:

    |Not to be confused with partial evaluation

    and so I found out that I was thinking about "partial evaluation" when
    you wrote "partial application".

    — A closure (a lexical closure) is created by binding *named* objects
    from the lexical environment.

    In Forth we do not have a lexical environment. So we use the term
    "closure" to refer to the thing that is used for the purposes that
    closures are used for in languages that have a lexical environment.
    You may prefer to call it a "partially-applied colon definition", but
    "closure" has the advantage of being much shorter.

    So a problem is how to create a partially applied function dynamically,
    and how to free resources of an already unneeded function

    It's pretty straightforward using "carnal knowledge", and as you
    demonstrate, pretty messy if you want to use currently standard means;
    so I think the better question is: What is missing from standard
    Forth? Is there something that is currently "carnal knowledge", but
    should be standardized, or should we standardize nothing at a lower
    level, and intead standardize the partial application word. (Probably
    at the present time the answer is that there is not enough common
    practice to standardize anything, but let's assume that at some point
    this will change.)

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Fri Apr 7 06:08:12 2023
    Anton Ertl schrieb am Freitag, 7. April 2023 um 14:17:54 UTC+2:
    minforth <minf...@arcor.de> writes:
    But I am still struggling with finding good applications
    Don't struggle! If the need does not come to you on its own, you
    don't need them.

    That zero investment so far suits me well. ;-)

    "Look, Ma, I have a clever solution! All I need now is a nice problem!"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Fri Apr 7 12:15:32 2023
    minforth <minforth@arcor.de> writes:
    But I am still struggling with finding good applications

    Don't struggle! If the need does not come to you on its own, you
    don't need them.

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to minforth on Fri Apr 7 09:38:06 2023
    On Friday, April 7, 2023 at 8:08:15 AM UTC-5, minforth wrote:
    Anton Ertl schrieb am Freitag, 7. April 2023 um 14:17:54 UTC+2:
    minforth <minf...@arcor.de> writes:
    But I am still struggling with finding good applications
    Don't struggle! If the need does not come to you on its own, you
    don't need them.
    That zero investment so far suits me well. ;-)

    "Look, Ma, I have a clever solution! All I need now is a nice problem!"

    The application for closures/partial application/whatever you want
    to call it in Forth that I have had is if I want to associate an address
    of a data structure or an ID associated with a peripheral with a handler
    word (e.g. an interrupt handler) without hard-coding it within the handler (case in point, what if you have a word for handling multiple different peripherals, each with their own IRQ?). Before I added support for closures/partial application/etc. what I had to do was to manually write
    words that hard-code the associated data and then call the actual
    handler; if I ever revisit that code I will change it to use closures/partial application/etc.

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Travis Bemann on Fri Apr 7 10:31:59 2023
    Travis Bemann schrieb am Freitag, 7. April 2023 um 18:38:07 UTC+2:
    On Friday, April 7, 2023 at 8:08:15 AM UTC-5, minforth wrote:
    Anton Ertl schrieb am Freitag, 7. April 2023 um 14:17:54 UTC+2:
    minforth <minf...@arcor.de> writes:
    But I am still struggling with finding good applications
    Don't struggle! If the need does not come to you on its own, you
    don't need them.
    That zero investment so far suits me well. ;-)

    "Look, Ma, I have a clever solution! All I need now is a nice problem!"
    The application for closures/partial application/whatever you want
    to call it in Forth that I have had is if I want to associate an address
    of a data structure or an ID associated with a peripheral with a handler word (e.g. an interrupt handler) without hard-coding it within the handler (case in point, what if you have a word for handling multiple different peripherals, each with their own IRQ?). Before I added support for closures/partial application/etc. what I had to do was to manually write words that hard-code the associated data and then call the actual
    handler; if I ever revisit that code I will change it to use closures/partial
    application/etc.

    I think lots of applications for closures comprise some capturing of context (data) and using that captured context later. Starting and stopping a task in "my" world for example. Things one could also achieve with OO methods.

    Although using closure xt's (aka anonymous functions) can make you code
    rather concise. But can make it also read-only, unless there is a commonly understood syntax.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to minforth on Fri Apr 7 12:38:15 2023
    On Friday, April 7, 2023 at 12:32:01 PM UTC-5, minforth wrote:
    Travis Bemann schrieb am Freitag, 7. April 2023 um 18:38:07 UTC+2:
    On Friday, April 7, 2023 at 8:08:15 AM UTC-5, minforth wrote:
    Anton Ertl schrieb am Freitag, 7. April 2023 um 14:17:54 UTC+2:
    minforth <minf...@arcor.de> writes:
    But I am still struggling with finding good applications
    Don't struggle! If the need does not come to you on its own, you
    don't need them.
    That zero investment so far suits me well. ;-)

    "Look, Ma, I have a clever solution! All I need now is a nice problem!"
    The application for closures/partial application/whatever you want
    to call it in Forth that I have had is if I want to associate an address of a data structure or an ID associated with a peripheral with a handler word (e.g. an interrupt handler) without hard-coding it within the handler (case in point, what if you have a word for handling multiple different peripherals, each with their own IRQ?). Before I added support for closures/partial application/etc. what I had to do was to manually write words that hard-code the associated data and then call the actual
    handler; if I ever revisit that code I will change it to use closures/partial
    application/etc.
    I think lots of applications for closures comprise some capturing of context (data) and using that captured context later. Starting and stopping a task in
    "my" world for example. Things one could also achieve with OO methods.

    Although using closure xt's (aka anonymous functions) can make you code rather concise. But can make it also read-only, unless there is a commonly understood syntax.

    The unfortunate thing about using closures/partial application in zeptoforth
    is that it can be rather cumbersome. For starters, you have to allot/allocate storage for them which will remain available when they are actually called.

    In the case of downwards funargs the most expedient approach is to use WITH-ALIGNED-ALLOT at the moment. However, now that I think of it,
    because this is a common use-case, I could create the following words:

    WITH-CLOSURE ( x bound-xt passed-xt -- )
    WITH-2CLOSURE ( d bound-xt passed-xt -- )
    WITH-NCLOSURE ( xn ... x0 count bound-xt passed-xt -- )

    where a closure is created temporarily in the dictionary which passes
    x, d, or xn ... x0 on the data stack and then calls bound-xt when called and
    is placed on the data stack and is valid for the scope of passed-xt, which
    is then called. These would be more convenient than the current way of
    doing things.

    In the case of upwards funargs other approaches would be necessary, which
    would be more inconvenient. The main approach would be to plan out all the upwards funargs one would need, and pre-allot/allocate space for them;
    e.g. if one had a closure which bound the address to a data structure, a convenient place to put the closure would be in the data structure itself.

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Travis Bemann on Fri Apr 7 13:39:32 2023
    Travis Bemann schrieb am Freitag, 7. April 2023 um 21:38:17 UTC+2:
    The unfortunate thing about using closures/partial application in zeptoforth is that it can be rather cumbersome. For starters, you have to allot/allocate storage for them which will remain available when they are actually called.

    IOW a closure is a data instance of upvalues with an anonymous function.
    If you don't want garbage collection, you could hide a destructor within the function. But then, why not use OO in the first place?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to minforth on Fri Apr 7 14:52:31 2023
    On Friday, April 7, 2023 at 3:39:35 PM UTC-5, minforth wrote:
    Travis Bemann schrieb am Freitag, 7. April 2023 um 21:38:17 UTC+2:
    The unfortunate thing about using closures/partial application in zeptoforth
    is that it can be rather cumbersome. For starters, you have to allot/allocate
    storage for them which will remain available when they are actually called.
    IOW a closure is a data instance of upvalues with an anonymous function.
    If you don't want garbage collection, you could hide a destructor within the function. But then, why not use OO in the first place?

    With OO (which zeptoforth supports) the obvious approach is to make the
    closure a member in the object which it passes to the handler itself, so it has the same lifetime as said object itself. Even if one is not using the OO layer per se, the same goes for storing a closure in a structure which it itself binds.
    This is less flexible, however, than the garbage-collected closure approach that is perennial in the functional programming world, which uses closures
    far more pervasively. Garbage collection, though, is not an option in zeptoforth as it is designed for embedded control; while zeptoforth does
    have support for heaps, even heaps are completely optional, having to be deliberately constructed by the user (and also there is no "the heap" - heaps are just another data structure, so the user can be more than one of them),
    the only heap that is supported "out of the box" is a small one used for storing history for the line editor. Yes, the likes of MicroPython and eLua
    use garbage-collected heaps, these hurt their real-time characteristics,
    and make them unsuitable for actual real-time operation.

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Travis Bemann@21:1/5 to Travis Bemann on Fri Apr 7 15:16:21 2023
    On Friday, April 7, 2023 at 4:52:32 PM UTC-5, Travis Bemann wrote:
    On Friday, April 7, 2023 at 3:39:35 PM UTC-5, minforth wrote:
    Travis Bemann schrieb am Freitag, 7. April 2023 um 21:38:17 UTC+2:
    The unfortunate thing about using closures/partial application in zeptoforth
    is that it can be rather cumbersome. For starters, you have to allot/allocate
    storage for them which will remain available when they are actually called.
    IOW a closure is a data instance of upvalues with an anonymous function. If you don't want garbage collection, you could hide a destructor within the
    function. But then, why not use OO in the first place?
    With OO (which zeptoforth supports) the obvious approach is to make the closure a member in the object which it passes to the handler itself, so it has
    the same lifetime as said object itself. Even if one is not using the OO layer
    per se, the same goes for storing a closure in a structure which it itself binds.
    This is less flexible, however, than the garbage-collected closure approach that is perennial in the functional programming world, which uses closures far more pervasively. Garbage collection, though, is not an option in zeptoforth as it is designed for embedded control; while zeptoforth does have support for heaps, even heaps are completely optional, having to be deliberately constructed by the user (and also there is no "the heap" - heaps
    are just another data structure, so the user can be more than one of them), the only heap that is supported "out of the box" is a small one used for storing history for the line editor. Yes, the likes of MicroPython and eLua use garbage-collected heaps, these hurt their real-time characteristics,
    and make them unsuitable for actual real-time operation.

    Correction - the user can "have" more than one heap, not "be" more than one heap. Also, there should be a "but" before "these hurt their real-time characteristics".

    Travis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Travis Bemann on Sat Apr 8 02:02:02 2023
    Travis Bemann schrieb am Freitag, 7. April 2023 um 23:52:32 UTC+2:
    ... the likes of MicroPython and eLua
    use garbage-collected heaps, these hurt their real-time characteristics,
    and make them unsuitable for actual real-time operation.

    muPython is rather flexible there:

    https://docs.micropython.org/en/latest/library/gc.html

    but it wouldn't be my choice either on slow hardware.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Anton Ertl on Sat Apr 8 13:23:45 2023
    On 2023-04-07 11:20, Anton Ertl wrote:
    Ruvim <ruvim.pinka@gmail.com> writes:
    [...]
    As I already mentioned [1], this idea is actually the
    conception of partial application, which is completely independent of
    local variables.

    Or of currying; from the caller's perspective the difference is small <https://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application>.

    This difference depends on the language. E.g., in Haskell it's small,
    since "f(a,b,c)" is equivalent to "f(a)(b)(c)".

    In Forth the difference is significant.

    Partial application itself fixes n parameters *at once* for a function.
    But currying produces a function that fixes n parameters in n *steps*.

    The process of implementing them brings better understanding.

    Let the word "pa1" partially applies xt to one parameter x. It can be
    defined as:

    : pa1 ( 1*x xt -- xt ) 2>r :noname r> r> lit, compile, postpone ; ;

    Then "pa2", "pa3", "pa4" can be defined as follows:

    : pa2 ( 2*x xt -- xt ) pa1 pa1 ;
    : pa3 ( 3*x xt -- xt ) pa2 pa1 ;
    : pa4 ( 4*x xt -- xt ) pa3 pa1 ;

    NB: identical data type identifiers (without indices) in a stack diagram
    don't mean identical parameters.

    Test cases:

    : noop ; : 'noop ['] noop ;
    : se ( i*x xt x -- j*x ) swap execute ;

    t{ 1 'noop pa1 10 se -> 10 1 }t
    t{ 2 1 'noop pa2 10 se -> 10 2 1 }t
    t{ 3 2 1 'noop pa3 10 se -> 10 3 2 1 }t

    The number 10 is placed on the stack just to test that the parameters
    are correctly taken from the stack.

    Curry (for particular number of fixed parameters) can be defined via
    partial application as follows:

    : cu0 ( xt -- xt ) ;
    : cu1 ( xt -- xt ) ['] pa1 pa1 cu0 ;
    : cu2 ( xt -- xt ) ['] pa2 pa1 cu1 ;
    : cu3 ( xt -- xt ) ['] pa3 pa1 cu2 ;
    : cu4 ( xt -- xt ) ['] pa4 pa1 cu3 ;

    Each of this word cu{n} produces a definition that fixes n parameters in
    n steps.
    Test cases:

    t{ 'noop cu1 1 se 10 se -> 10 1 }t
    t{ 'noop cu2 1 se 2 se 10 se -> 10 2 1 }t
    t{ 'noop cu3 1 se 2 se 3 se 10 se -> 10 3 2 1 }t

    NB: we apply "execute" n times to fix n parameters.

    A definition of a word "ncu ( xt u.n -- xt )" that produces xt that
    fixes arbitrary n parameters in n steps — is a puzzle for the reader (I already posted a solution with its proof in 2018).


    Partial application can be also defined via curry as follows:

    synonym e execute

    : pa1 ( 1*x xt -- xt ) cu1 e ;
    : pa2 ( 2*x xt -- xt ) cu2 e e ;
    : pa3 ( 3*x xt -- xt ) cu3 e e e ;
    : pa4 ( 4*x xt -- xt ) cu4 e e e e ;



    From the information theory point of view, partial application is a
    reduction, since the produced function "contains" less information than
    the original one. But currying is a lossless transformation — the
    produced function "contains" as much information as the original one.



    Plus, I had not known the term before, although it sounded familiar: <https://en.wikipedia.org/wiki/Partial_application> starts by saying:

    |Not to be confused with partial evaluation

    and so I found out that I was thinking about "partial evaluation" when
    you wrote "partial application".


    Oh, I see. Moreover, partial evaluation is a specific case of partial application, I think. And partial application can contain partial
    evaluation under the hood, but it's not necessary.


    [...]


    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Anton Ertl on Sat Apr 8 15:07:32 2023
    On 2023-04-07 11:20, Anton Ertl wrote:
    Ruvim <ruvim.pinka@gmail.com> writes:
    On 2023-04-05 07:33, Anton Ertl wrote:
    We would have missed this idea if we had rejected everything
    to do with locals from the start.

    I cannot agree.

    You were not involved.

    Yes, sorry I wasn't.

    Well, I awkwardly expressed my thought. I wanted to say, you probably
    would eventually come to this idea as the conception of partial
    application anyway (independently of local variables).

    [...]


    — A closure (a lexical closure) is created by binding *named* objects >>from the lexical environment.

    In Forth we do not have a lexical environment.

    Sure, we have. Formally, it depends on particular definition of
    "lexical environment" term.

    Intuitively, lexical environment is a set of information that is used to recognize lexemes (or, on which recognizing of lexemes depends on).


    So we use the term
    "closure" to refer to the thing that is used for the purposes that
    closures are used for in languages that have a lexical environment.
    You may prefer to call it a "partially-applied colon definition", but "closure" has the advantage of being much shorter.

    "colon definition" is an informal notion.

    Formally, we have "Forth definition" (not necessary a colon definition,
    and even not necessary a named definition), that is often abbreviated to
    just "definition", when it's clear from the context that we talk about a
    Forth definition.


    BTW, the notion of "anonymous definition" in Forth and "anonymous
    function" in other programming languages mean the same, I think. Then, *ordinary* named definitions in Forth and named functions in other
    languages also mean the same.

    Therefore, it's pretty clear what "partially applied function" means in
    the context of Forth.


    Concerning "execution" — it corresponds better not to "function
    application", but to "function evaluation" (in mathematical sense). Then
    I was wrong in my previous message that "partial evaluation is a
    specific case of partial application".





    Well, if we call a partially applied definition "closure", how we will
    call a real closure (which binds lexical environment including local variables)?




    So a problem is how to create a partially applied function dynamically,
    and how to free resources of an already unneeded function

    It's pretty straightforward using "carnal knowledge", and as you
    demonstrate, pretty messy if you want to use currently standard means;
    so I think the better question is: What is missing from standard
    Forth?

    Is there something that is currently "carnal knowledge", but
    should be standardized, or should we standardize nothing at a lower
    level, and intead standardize the partial application word. (Probably
    at the present time the answer is that there is not enough common
    practice to standardize anything, but let's assume that at some point
    this will change.)


    I think, we need them all — some things on a lower level and some things
    on a higher level.

    For example, there is a general need for dynamic compilation, execution
    of the resulting definitions, and then freeing them (freeing the
    resources that were taken doing compilation).

    And such means can be used to implement partial application too.


    In the same time we can consider words like:

    1apply ( x xt -- xt )
    napply ( i*x u.i xt -- xt )
    1fapply ( xt -- xt ) ( F: r -- )
    nfapply ( u.i xt -- xt ) ( F: i*r -- )
    free-definition ( xt -- )
    \ it may throw an exception if xt
    \ is not from the "apply" family words
    \ or was already freed.


    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Anton Ertl on Sat Apr 8 15:26:59 2023
    On 2023-04-07 11:20, Anton Ertl wrote:
    [...]

    So we use the term
    "closure" to refer to the thing that is used for the purposes that
    closures are used for in languages that have a lexical environment.

    In general, it's a bad argument.

    If different tools are used for the same purpose, it doesn't mean that
    each of this tools should be called by the same name, just because they
    all occasionally are used for the same purpose.


    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Ruvim on Sun Apr 9 01:42:00 2023
    Ruvim schrieb am Samstag, 8. April 2023 um 17:07:36 UTC+2:
    On 2023-04-07 11:20, Anton Ertl wrote:

    In Forth we do not have a lexical environment.
    Sure, we have. Formally, it depends on particular definition of
    "lexical environment" term.

    Intuitively, lexical environment is a set of information that is used to recognize lexemes (or, on which recognizing of lexemes depends on).

    I use a header cell containing the FNV1a hash-value of the word's/local's name. This accelerates recognizing/searching for words/locals significantly.

    Target overlays/binaries do not contain plain-text names but the hash values. With this information "lexemes" can be recognized nevertheless.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Anton Ertl on Mon Apr 10 13:37:47 2023
    On 2023-04-07 11:51, Anton Ertl wrote:
    [...]

    Without local variables in general, or without *ability* to bind the
    outer local variables, closures are trivial. I'm not sure it's still
    correct to call such functions "closures". Therefor, in Factor, Joy,
    Cat, Forth, such functions are intentionally called "quotations".

    If we don't call quotations "closures", then we rather shouldn't call
    partially applied functions "closures" too.

    That does not follow. Quotations neither are closures nor do they
    serve the purpose of closures.

    Yes. But sometimes they can be used to solve a problem for which
    closures are also used.


    What Gforth calls closures is intended
    to be used for the purpose that closures (as well as curried and partially-applied functions) are used for in Scheme; and if we have

    [n:d + ;]

    (what I call a pure-stack closure), it's not a partially-applied (or partially-EXECUTEd?) colon definition, because no partial application/execution has happened, and it's not a colon definition.

    Partial application happens when a new xt is returned by this construct.

    The construct

    [n:d + ;]

    is actually a sugar over

    [: + ;] partial1

    Where "partial1 ( x1 xt1 -- xt )" performs partial application.





    It might be called a curried quotation if you are unhappy with
    "closure", but I am happy with "closure" (but a also call the
    resulting xt the xt of a closure, so I am quite loose in terminology
    here.

    Also, if the outer local variables are not bound in a function, it's
    probably confusing if this function would be called "closure".

    About as confusing as calling a colon definition a "function".

    Well, if we still want to call partially applied functions "closures",
    they shouldn't be "lexical closures", but some other kind of closures.

    "Flat closure" or "Gforth closure" is fine with me.

    And "pure-stack closures" is not well suited either, I think.

    "Pure-stack" contrasts with the closures that have locals on the
    inside:

    pure-stack closure: [n:d + ;]
    other Gforth closure: [{: n :}d n + ;]


    Closures that are created in the same lexical environment, share the
    same external variables (their state).

    For example, see in JavaScript:

    x = (function (){
    var a ; a = 0;
    return [ ()=>{ a+=1; return a}, ()=>{ a+=10; return a} ]
    })();

    x[0]() // "1"
    x[1]() // "11"
    x[0]() // "12"
    x[0]() // "13"
    x[1]() // "23"


    Gforth "closures" does not provide this functionality. Because actually
    they are partially applied functions (partially applied definitions, if
    you like), which are defined in special syntax.


    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Ruvim on Mon Apr 10 21:55:58 2023
    Ruvim schrieb am Montag, 10. April 2023 um 15:37:50 UTC+2:
    Closures that are created in the same lexical environment, share the
    same external variables (their state).

    For example, see in JavaScript:

    x = (function (){
    var a ; a = 0;
    return [ ()=>{ a+=1; return a}, ()=>{ a+=10; return a} ]
    })();

    x[0]() // "1"
    x[1]() // "11"
    x[0]() // "12"
    x[0]() // "13"
    x[1]() // "23"


    Gforth "closures" does not provide this functionality. Because actually
    they are partially applied functions (partially applied definitions, if
    you like), which are defined in special syntax.

    "Sharing" external free variables, or getting an independent copy of them
    (the lexical environment) at time of closure creation means a huge difference. Each somewhat functional language seems to have cooked its own recipe
    for closures, and IMO Javascript has overdone it here.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Ruvim on Tue Apr 11 06:54:03 2023
    Ruvim <ruvim.pinka@gmail.com> writes:
    On 2023-04-07 11:51, Anton Ertl wrote:
    What Gforth calls closures is intended
    to be used for the purpose that closures (as well as curried and
    partially-applied functions) are used for in Scheme; and if we have

    [n:d + ;]

    (what I call a pure-stack closure), it's not a partially-applied (or
    partially-EXECUTEd?) colon definition, because no partial
    application/execution has happened, and it's not a colon definition.

    Partial application happens when a new xt is returned by this construct.

    Yes. So the construct itself can be called a curried definition, or a definition with two-stage parameter passing.

    The construct

    [n:d + ;]

    is actually a sugar over

    [: + ;] partial1

    Where "partial1 ( x1 xt1 -- xt )" performs partial application.

    Except that we don't have PARTIAL1.

    Closures that are created in the same lexical environment, share the
    same external variables (their state).

    For example, see in JavaScript:

    x = (function (){
    var a ; a = 0;
    return [ ()=>{ a+=1; return a}, ()=>{ a+=10; return a} ]
    })();

    x[0]() // "1"
    x[1]() // "11"
    x[0]() // "12"
    x[0]() // "13"
    x[1]() // "23"

    Gforth "closures" does not provide this functionality.

    [: ( -- addr )
    align here >r 0 ,
    here
    r@ [n:h 1 over +! @ ;] ,
    r> [n:h 10 over +! @ ;] ,
    ;] execute constant x

    x @ execute . \ 1
    x cell+ @ execute . \ 11
    x @ execute . \ 12
    x @ execute . \ 13
    x cell+ @ execute . \ 23

    Here I used assignment conversion, which has been described by Dybvig
    in the Scheme literature in 1987. Each invocation of the quotation
    produces a new instance of the variable in the dictionary with ",",
    and the address of this variable is then passed to the two closures,
    both of which then access the same variable. Section 5 of <http://www.euroforth.org/ef18/papers/ertl.pdf> describes the
    application of assignment conversion to Forth. The closures are
    allocated on the heap here, to make the allocation of the array in the dictionary convenient.

    The code above uses only stack stuff. A highly locals-oriented
    version is:

    [: ( -- addr )
    0 <{: w^ a :}d a ;> drop {: a :}
    here
    a [{: a :}h 1 a +! a @ ;] ,
    a [{: a :}h 10 a +! a @ ;] ,
    ;] execute constant x

    x @ execute .
    x cell+ @ execute .
    x @ execute .
    x @ execute .
    x cell+ @ execute .

    Here the <{: ... ;> syntax is used for producing home locations for
    variables that you need to share, because they are changed. It
    provides a common syntax for doing this for the different allocation
    methods; of coures for each allocation method you can do it with
    conventional means, but the syntax for the different allocation
    methods is quite different.

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Tue Apr 11 08:13:28 2023
    minforth <minforth@arcor.de> writes:
    Ruvim schrieb am Montag, 10. April 2023 um 15:37:50 UTC+2:
    Closures that are created in the same lexical environment, share the
    same external variables (their state).

    For example, see in JavaScript:

    x = (function (){
    var a ; a = 0;
    return [ ()=>{ a+=1; return a}, ()=>{ a+=10; return a} ]
    })();

    x[0]() // "1"
    x[1]() // "11"
    x[0]() // "12"
    x[0]() // "13"
    x[1]() // "23"


    Gforth "closures" does not provide this functionality. Because actually
    they are partially applied functions (partially applied definitions, if
    you like), which are defined in special syntax.

    "Sharing" external free variables, or getting an independent copy of them >(the lexical environment) at time of closure creation means a huge difference. >Each somewhat functional language seems to have cooked its own recipe
    for closures, and IMO Javascript has overdone it here.

    This kind of sharing is an old concept already present in Algol 60,
    exercised in the Man-or-Boy test (in the variables k and B) and in
    Jensen's device; and of course you can use it in Scheme. It's not
    needed in pure functional languages, because they don't change the
    values of variables; so you can just replicate the value, no need to
    worry about another user getting a stale value.

    While (some) Scheme compilers use assignment conversion as a compiler technique, we decided to leave this job to the programmer in order to
    simplify the implementation of closures. I think that this is the
    right choice:

    * The need for assignment conversion rare; I think we have only used
    it for proof-of-concept examples yet. So leaving it to the
    programmer is not very burdensome.

    * It makes the costs of things much more obvious. In the original
    (Algol 60) man-or-boy program it's not obvious that k has this extra
    cost, while in the Forth version it is quite easy to see.

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Anton Ertl on Tue Apr 11 13:34:19 2023
    On 2023-04-07 11:20, Anton Ertl wrote:
    Ruvim <ruvim.pinka@gmail.com> writes:
    [...]
    So a problem is how to create a partially applied function dynamically,
    and how to free resources of an already unneeded function

    It's pretty straightforward using "carnal knowledge", and as you
    demonstrate, pretty messy if you want to use currently standard means;

    I have implemented this idea as a PoC (a proof of concept prototype)
    [1]. It took about 50 lines of code.

    Since in Standard Forth we cannot create a new xt (new definition) in
    any particular moment, and cannot remove a particular definition, I
    create a pool of definitions (a pool of thunks [2]) in advance, which
    can be reused many times.


    This implementation is not quite efficient — I use only cons-based
    dynamic lists. A more efficient way is to use a stack to store the pool
    of idle items (which are available for hiring), and use a hash-table to
    get the data address from a thunk xt (or use create-does and ">body",
    but then I have to provide a dummy name for each definition in the pool).

    The cons-based cells and lists is a very easy tool for prototyping. It's provide simple memory management, and I even don't need to create usual
    data structures.


    Also, the linking between an xt and the corresponding data field is a
    useful thing by itself (without connection to a named definitions).

    What about a word like "bind-here ( xt1 -- xt2 )", which partially
    applies xt1 to the value of the data-space pointer? An important
    requirement is that ">body" should be applicable to xt2.

    Test cases:

    t{ ' 2@ bind-here ( xt ) 1 , 2 , execute -> 2 1 }t
    t{ [: dup . @ . ;] bind-here >body here = -> true }t

    It's a more basic tool than "create-does", and it allows to better
    optimize the code since the bound parameter cannot be changed (unlike in create-does).


    Another thing concerning dynamic partially applied functions
    (definitions) is that if you create chains of such definitions, it's
    cumbersome to manually release them.

    Probably some helper tool would be useful to define a kind of domain,
    and release all definitions (or resources in general) that are bound to
    this domain.




    [1] https://github.com/ruv/puzzle-in-forth/blob/main/extension/partial-application.fth

    [2] https://en.wikipedia.org/wiki/Thunk_(functional_programming)



    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Anton Ertl on Tue Apr 11 14:52:47 2023
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    [: ( -- addr )
    0 <{: w^ a :}d a ;> drop {: a :}
    here
    a [{: a :}h 1 a +! a @ ;] ,
    a [{: a :}h 10 a +! a @ ;] ,
    ;] execute constant x

    There are 4 definitions of a local A here, each within its own scope,
    but still humans may find it hard to see which use of A refers to
    which definition. So I have varied this so that each local gets a
    unique name:

    [: ( -- addr )
    0 <{: w^ a1 :}d a1 ;> drop {: a2 :}
    here
    a2 [{: a3 :}h 1 a3 +! a3 @ ;] ,
    a2 [{: a4 :}h 10 a4 +! a4 @ ;] ,
    ;] execute constant x

    - 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Tue Apr 11 13:40:16 2023
    Anton Ertl schrieb am Dienstag, 11. April 2023 um 10:32:56 UTC+2:
    minforth <minf...@arcor.de> writes:
    Ruvim schrieb am Montag, 10. April 2023 um 15:37:50 UTC+2:
    Closures that are created in the same lexical environment, share the
    same external variables (their state).

    For example, see in JavaScript:

    x = (function (){
    var a ; a = 0;
    return [ ()=>{ a+=1; return a}, ()=>{ a+=10; return a} ]
    })();

    x[0]() // "1"
    x[1]() // "11"
    x[0]() // "12"
    x[0]() // "13"
    x[1]() // "23"


    Gforth "closures" does not provide this functionality. Because actually
    they are partially applied functions (partially applied definitions, if
    you like), which are defined in special syntax.

    "Sharing" external free variables, or getting an independent copy of them >(the lexical environment) at time of closure creation means a huge difference.
    Each somewhat functional language seems to have cooked its own recipe
    for closures, and IMO Javascript has overdone it here.
    This kind of sharing is an old concept already present in Algol 60,
    exercised in the Man-or-Boy test (in the variables k and B) and in
    Jensen's device; and of course you can use it in Scheme. It's not
    needed in pure functional languages, because they don't change the
    values of variables; so you can just replicate the value, no need to
    worry about another user getting a stale value.

    While (some) Scheme compilers use assignment conversion as a compiler technique, we decided to leave this job to the programmer in order to simplify the implementation of closures. I think that this is the
    right choice:

    * The need for assignment conversion rare; I think we have only used
    it for proof-of-concept examples yet. So leaving it to the
    programmer is not very burdensome.

    * It makes the costs of things much more obvious. In the original
    (Algol 60) man-or-boy program it's not obvious that k has this extra
    cost, while in the Forth version it is quite easy to see.

    Thanks for your explanations! Really appreciated!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ruvim@21:1/5 to Anton Ertl on Thu Apr 13 16:26:08 2023
    (repeated message due to a problem with delivery)
    On 2023-04-11 06:54, Anton Ertl wrote:
    Ruvim <ruvim.pinka@gmail.com> writes:
    On 2023-04-07 11:51, Anton Ertl wrote:
    What Gforth calls closures is intended
    to be used for the purpose that closures (as well as curried and
    partially-applied functions) are used for in Scheme; and if we have

    [n:d + ;]

    (what I call a pure-stack closure), it's not a partially-applied (or
    partially-EXECUTEd?) colon definition, because no partial
    application/execution has happened, and it's not a colon definition.

    Partial application happens when a new xt is returned by this construct.

    Yes. So the construct itself can be called a curried definition, or a definition with two-stage parameter passing.

    I like the variant "curried definition". My rationale: when it's
    performed (i.e., at run-time), it creates a new definition using partial application, and returns the corresponding xt — and it's similar to what
    a curried function does.

    The fragment:

    [n:d + ;]

    is equivalent to:

    [: + ;] partial1

    that is equivalent to:

    [ [: + ;] curry1 compile, ]

    that is equivalent to:

    curried1(+)

    where "curried1(+)" is defined as:

    [: + ;] curry1 alias curried1(+)



    Also, a curried function returns a partially applied function, and it
    has nothing to do with lexical closures at all. And it's true for this construct too (e.g. "[n:d + ;]").





    The construct

    [n:d + ;]

    is actually a sugar over

    [: + ;] partial1

    Where "partial1 ( x1 xt1 -- xt )" performs partial application.

    Except that we don't have PARTIAL1.

    Well, this construct is a sugar conceptually, and anyway, this construct
    is equivalent to this code, regardless whether you have "partial1 (i.e.,
    the implementation details don't matter).



    Closures that are created in the same lexical environment, share the
    same external variables (their state).

    For example, see in JavaScript:

    x = (function (){
    var a ; a = 0;
    return [ ()=>{ a+=1; return a}, ()=>{ a+=10; return a} ]
    })();

    x[0]() // "1"
    x[1]() // "11"
    x[0]() // "12"
    x[0]() // "13"
    x[1]() // "23"

    Gforth "closures" does not provide this functionality.

    [: ( -- addr )
    align here >r 0 ,
    here
    r@ [n:h 1 over +! @ ;] ,
    r> [n:h 10 over +! @ ;] ,
    ;] execute constant x

    x @ execute . \ 1
    x cell+ @ execute . \ 11
    x @ execute . \ 12
    x @ execute . \ 13
    x cell+ @ execute . \ 23

    OK. My argument is not quite suitable. In your example the required
    behavior is implemented without closures.


    A closure is a function that *directly* (not by argument passing) refers
    to identifiers defined in the environment in which the function is
    defined, but not in the environment of the function call. If such a call
    is not possible, the function is not a closure.

    In a broad sense, even a function defined in a module is a closure over
    the local identifiers of that module. In a narrow sense, a closure is a
    nested function only (a function defined within another function) that
    refers to identifiers defined in an enclosing function.

    In the Forth terminology, a closure is an anonymous inner definition
    that directly mention some names defined in some of enclosing definitions.

    In your example, the inner definitions don't mention a *name* defined in
    the containing definition. So, lexical closures are not involved.



    Here I used assignment conversion, which has been described by Dybvig
    in the Scheme literature in 1987.

    Assignment conversion of a closure is one of possible ways to
    *implement* supporting of closures in a language. Another way is
    run-time code generation.

    If you are forced to do closure conversion by hand, then the language
    does not support closures.



    Each invocation of the quotation
    produces a new instance of the variable in the dictionary with ",",
    and the address of this variable is then passed to the two closures,
    both of which then access the same variable. Section 5 of <http://www.euroforth.org/ef18/papers/ertl.pdf> describes the
    application of assignment conversion to Forth.

    | In most of the rest of this paper, closure
    | refers to the data structure, and it’s
    | Forth source code representation.

    It looks like you consider only a back-end part to support closures in
    Forth.

    Without a front-end, — the ability to directly refer names defined in an enclosing definition — I cannot say that closures are supported by a
    language (in its syntax and semantics).


    The closures are
    allocated on the heap here, to make the allocation of the array in the dictionary convenient.

    The code above uses only stack stuff. A highly locals-oriented
    version is:

    [: ( -- addr )
    0 <{: w^ a :}d a ;> drop {: a :}
    here
    a [{: a :}h 1 a +! a @ ;] ,
    a [{: a :}h 10 a +! a @ ;] ,
    ;] execute constant x

    In this case too, no inner definition mentions a *name* defined in the containing definition.


    x @ execute .
    x cell+ @ execute .
    x @ execute .
    x @ execute .
    x cell+ @ execute .

    Here the <{: ... ;> syntax is used for producing home locations for
    variables that you need to share, because they are changed. It
    provides a common syntax for doing this for the different allocation
    methods; of coures for each allocation method you can do it with
    conventional means, but the syntax for the different allocation
    methods is quite different.



    --
    Ruvim

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)