• Re: Back & Forth - Co-routines

    From albert@spenarnc.xs4all.nl@21:1/5 to no.email@nospam.invalid on Tue Feb 4 13:26:26 2025
    In article <874j1aycdt.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:
    albert@spenarnc.xs4all.nl writes:
    I maintain that if you are not in a recursive call for
    a function with locals, and you try to catch the same function
    call, everything is fine.

    The thing about exceptions is that they occur unexpectedly.
    Yeah right.

    Think about it precisely.
    You are in fun , recursively called from fun. A throw occurs in
    this invocation but not from the external invocation.
    You did not catch it in the external fun (otherwise all would be under control). Can that cause problems by no restoring the variable?

    your recursive function prints something, and that works the first few
    times, but then the printer runs out of paper and there is an i/o
    exception. It's not the recursive function's job to handle this. The >exception throws to some outer level handler that asks the user to fix
    the problem.

    The printer runs out of paper during a recursive fibonacci call where you
    print physically all the iteration stuff on a separate page.
    You add new paper to the printer and you want to continue.
    That is not a good example.
    You call 100 'fib catch (don't mind the paper spill, but yes
    the printer is guaranteed to run out of paper).
    The problem is that you cannot recover! The catch makes no sense,
    because the throw is made from in inner fib call.
    Or do you can propose to adorn all recursive calls
    : fib ...
    'fib catch ( enormous recovery for all cases including printer errors )
    ...
    'fib catch ( enormous recovery for all cases including printer errors )
    ..
    ;


    Adding (LOCAL) to a Forth interpreter should normally not be too
    difficulot, if you control the interpreter implementation. It's the
    right way to do stuff like this. Why mess around with all that awful
    stack juggling for a half-working and woefully slow solution?

    That is not the point. We are discussing whether or not the
    present solution is possible.
    It is an academic discussion, because I don't use locals at all,
    if they are absolutely necessary I use my enhanced `[ instead
    of any style of locals

    : root [ variable a variable b variable c ]
    c ! b ! a !
    \ now insert the famous quadratic root formula
    [ hide a hide b hide c ] \ Prevent "not unique messages"
    ;

    Remark that is doesn't introduce any unfamiliar syntax,
    only does away with "Forth shall not nested definitions"
    (Says who?)
    It is perfectly clear that this doesn't work where root
    call itself recursively. But it doesn't call itself, so fine.

    Groetjes Albert
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Tue Feb 11 10:21:57 2025
    Unfortunately, everything is very non-standard, as there is
    no multitasking wordset (for historical reasons?).
    A pity really, as co-operative multitasking existed very
    early on in Forth.
    Then it would be easier to have better discussions about
    coroutines (the original topic of this thread) or ownership
    of closure objects and different variants of GC.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From HenryHH@21:1/5 to albert@spenarnc.xs4all.nl on Thu Feb 6 06:47:59 2025
    On Tue, 4 Feb 2025 12:26:26 +0000, albert@spenarnc.xs4all.nl wrote:

    ... if they are absolutely necessary I use my enhanced `[ instead
    of any style of locals

    : root [ variable a variable b variable c ]
    c ! b ! a !
    \ now insert the famous quadratic root formula
    [ hide a hide b hide c ] \ Prevent "not unique messages"
    ;

    Hello Albert,

    why is an enhanced [ necessary for this?
    Couldn't you simply write:

    variable a variable b variable c

    : root ( a b c -- root)
    c ! b ! a !
    \ now insert the famous quadratic root formula
    ;

    hide a hide b hide c \ Prevent "not unique messages"

    Henry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to albert@spenarnc.xs4all.nl on Thu Feb 6 09:20:16 2025
    On Tue, 4 Feb 2025 12:26:26 +0000, albert@spenarnc.xs4all.nl wrote:
    Remark that is doesn't introduce any unfamiliar syntax,
    only does away with "Forth shall not nested definitions"
    (Says who?)

    Given that a Forth system supports quotations and xt-locals,
    nested definitions are easy to implement. BTW xt-locals exist
    f.ex. in gforth. They hold xt's and when called execute the xt
    instead of pushing it to the stack as normal locals would do.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to no.email@nospam.invalid on Wed Feb 12 13:49:48 2025
    In article <87wmdwunwk.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:
    minforth@gmx.net (minforth) writes:
    Unfortunately, everything is very non-standard, as there is
    no multitasking wordset (for historical reasons?).
    A pity really, as co-operative multitasking existed very
    early on in Forth.

    I had thought there wasn't enough agreement about the wordset to
    standardize it, and the same thing happened for cross development. But
    at least for multitasking, there is reasonable shared understanding
    about how it should work, at least in the cooperative case.

    Then it would be easier to have better discussions about coroutines
    (the original topic of this thread) or ownership of closure objects
    and different variants of GC.

    I think these fancy closures are mostly of interest to language geeks
    and not so much in the old-fashioned Forth spirit. Coroutines don't
    seem that important if you have multitasking. Anton's GC is great and I >think cooperative multitasking wouldn't affect it much, if you don't
    mind the collection pauses. It would simply block during collection.

    Look at python: they have decorators. In Forth this looks like:
    ' aap ' noot decorate
    Execute `aap (e.g. .S) before each invocation of `noot

    This works nicely with coroutines. In ciforth it is actually implemented.
    (also undecorated of course)

    In ciforth the canonical example is
    "
    { &i EMIT .S ;: &o EMIT .S } 'somethingbuggy decorated
    "
    The `;: ( `CO ) takes care that the second part is executed after each invocation of `somethingbuggy.
    Note that this is ideal for Heisenbug's, where you cannot insert testoutput because the bug disappears as soon as memory is shifted.

    Downplaying coroutines is an indication that you have not realised
    the potential.

    [I don't care much about closures, maybe the same applies to me
    about closures.]

    Groetjes Albert
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to dxf on Fri Jan 31 08:22:41 2025
    dxf <dxforth@gmail.com> writes:
    Not sure if previously mentioned but here's another version of LOCAL

    : ;: >r ;

    : LOCAL ( x adr -- )
    -rot dup @ over 2>r ! ;: 2r> ! ;

    variable A variable B 8 a ! 7 b !

    : divide ( a b -- ) b local a local
    a @ b @ / . cr ;

    This kind of implementation leads to the same behaviour as dynamic
    scoping. As long as you don't have quotations or other kinds of
    nested definitions, or if you only access the locals of the innermost definition (as in the quotations in the next standard draft), there is
    no difference from static scoping. But if you have nested definitions
    and access to outer locals, there is a difference, and Knuth's
    man-or-boy test shows the difference. One usually wants static
    scoping, but there are also cases where dynamic scoping is desired.

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to minforth on Thu Feb 6 12:11:28 2025
    In article <3c3bdb056696f15c43fa512b5366002d@www.novabbs.com>,
    minforth <minforth@gmx.net> wrote:
    On Tue, 4 Feb 2025 12:26:26 +0000, albert@spenarnc.xs4all.nl wrote:
    Remark that is doesn't introduce any unfamiliar syntax,
    only does away with "Forth shall not nested definitions"
    (Says who?)

    Given that a Forth system supports quotations and xt-locals,
    nested definitions are easy to implement. BTW xt-locals exist
    f.ex. in gforth. They hold xt's and when called execute the xt
    instead of pushing it to the stack as normal locals would do.

    Right. I've shown that it is easy, that is nothing new.
    What also isn't new that gforth insists on introducing another
    special syntax, words and idioms that are unnecessary.
    With my enhanced [ ] pair, you can introduce whatever local
    widgets, in particular objects, you choose.

    The goal of language design is not to overload with features,
    but attain expressiveness with the least amount of concepts.

    Groetjes Albert
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to HenryHH on Thu Feb 6 12:05:07 2025
    In article <73a87ff17586b02ae110516bfb76956e@www.novabbs.com>,
    HenryHH <hohl@isartext.de> wrote:
    On Tue, 4 Feb 2025 12:26:26 +0000, albert@spenarnc.xs4all.nl wrote:

    ... if they are absolutely necessary I use my enhanced `[ instead
    of any style of locals

    : root [ variable a variable b variable c ]
    c ! b ! a !
    \ now insert the famous quadratic root formula
    [ hide a hide b hide c ] \ Prevent "not unique messages"
    ;

    Hello Albert,

    why is an enhanced [ necessary for this?
    Couldn't you simply write:

    variable a variable b variable c

    : root ( a b c -- root)
    c ! b ! a !
    \ now insert the famous quadratic root formula
    ;

    hide a hide b hide c \ Prevent "not unique messages"

    You could, but now it doesn't look like a local variable,
    isn't it?
    You remark serves to show that local values (why do they call
    them local variables) are largely superfluous.

    P.S.
    Maybe
    [ variable a variable b variable c
    : D b @ DUP * a @ b @ 4 * - ; ]
    ..
    [ hide a .... hide D ]
    ..
    is more convincing?


    Henry

    Groetjes Albert
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to albert@spenarnc.xs4all.nl on Thu Feb 6 12:36:39 2025
    On Thu, 6 Feb 2025 11:11:28 +0000, albert@spenarnc.xs4all.nl wrote:

    With my enhanced [ ] pair, you can introduce whatever local
    widgets, in particular objects, you choose.

    The goal of language design is not to overload with features

    But haven't you overloaded the old [ ] with new features now? ;-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Thu Feb 6 13:59:21 2025
    On Thu, 6 Feb 2025 12:57:12 +0000, Anton Ertl wrote:

    minforth@gmx.net (minforth) writes:
    On Tue, 4 Feb 2025 12:26:26 +0000, albert@spenarnc.xs4all.nl wrote:
    Remark that is doesn't introduce any unfamiliar syntax,
    only does away with "Forth shall not nested definitions"
    (Says who?)

    Given that a Forth system supports quotations and xt-locals,
    nested definitions are easy to implement.

    You mean:

    : foo [: ." xyz" ;] {: xt: bar :} bar ;

    Sure, you then get a local that behaves similar to a nested
    definition, but I don't think anyone would want to do that.

    Yes, that's the mechanism. Actually I use it with some syntactic
    sugar for better readability:

    : foo
    <: bar ." xyz" ;>
    bar
    ;

    I find this quite handy, since upvalues (locals within foo's
    context) are accessible from within bar.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Thu Feb 6 12:57:12 2025
    minforth@gmx.net (minforth) writes:
    On Tue, 4 Feb 2025 12:26:26 +0000, albert@spenarnc.xs4all.nl wrote:
    Remark that is doesn't introduce any unfamiliar syntax,
    only does away with "Forth shall not nested definitions"
    (Says who?)

    Given that a Forth system supports quotations and xt-locals,
    nested definitions are easy to implement.

    You mean:

    : foo [: ." xyz" ;] {: xt: bar :} bar ;

    Sure, you then get a local that behaves similar to a nested
    definition, but I don't think anyone would want to do that.

    And it uses proper locals as part of the mechanism, whereas many
    contributions in the thread seem to have the goal of doing something
    that may require less implementation effort, but also does not do
    everything proper locals do (e.g., work with exceptions), and then try
    to Jedi away the cases that do not work.

    BTW xt-locals exist
    f.ex. in gforth. They hold xt's and when called execute the xt
    instead of pushing it to the stack as normal locals would do.

    We call them defer-flavoured, and the more usual locals
    value-flavoured. In the example above the "xt:" means that the next
    name is that of a defer-flavoured local.

    When Bernd Paysan introduced defer-flavoured locals, I was sceptical, especially given the experience with variable-flavoured locals which
    have been in Gforth since 1994 and are barely used. But it turns out
    that when a local contains an xt, using a defer-flavoured local is
    often more appropriate than a value-flavoured local. This is: you can
    use either (use l EXECUTE when l is value-flavoured and you want to
    execute it, or use ACTION-OF l when l is defer-flavoured and you want
    its value), but in many cases you want to execute an xt you pass,
    sometimes multiple times.

    An example is the generation of code for 2-stage division by
    constants:

    : lit/, {: divisor xt: stage1 xt: stage2 -- :}
    next-section staged/-size small-allot previous-section {: addr :}
    divisor addr stage1 ]] addr stage2 [[ ;

    The first line allocates memory for storing the inverse of the divisor
    in ADDR. The second line first converts from the divisor to the
    inverse in stage 1 of the division when the division is compiled, and
    compiles code for performing stage 2 of the division (multiplying the
    dividend (available only at run-time) with the inverse). It also
    demonstrates that when using a defer-flavoured local inside ]]...[[,
    the xt in the local is COMPILE,d, not EXECUTEd.

    With value-flavoured locals that would have been:

    : lit/, {: divisor stage1 stage2 -- :}
    next-section staged/-size small-allot previous-section {: addr :}
    divisor addr stage1 execute ]] addr [[ stage2 compile, ;

    While the mechanism is more obvious here, the intent is more obvious
    in the version with defer-flavoured locals (at least if you understand
    the code). And of course the orthodox traditional Forther would have
    written this without locals at all:

    : lit/, ( divisor stage1 stage2 -- )
    >r >r >r next-section staged/-size small-allot previous-section ( addr )
    r> dup r> execute ( addr )
    postpone literal r> compile, ;

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Thu Feb 6 15:46:23 2025
    There is indeed a restriction in standard ยง3.4:

    "A program shall not attempt to nest compilation of definitions.
    During the compilation of the current definition, a program
    shall not execute any defining word, :NONAME, or any definition
    that allocates dictionary data space."

    However because local names are not compiled into dictionary data
    space, but use their own transient dictionary entries, which
    disappear after compilation, this restriction as of $3.4 does not
    apply to embedded functions emulated by xt-locals, aka
    defer-flavoured locals.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Thu Feb 6 17:46:40 2025
    I don't think so. This is what happens now:

    MinForth 3.6 (32 bit)
    # : n+ ( n -- xt ) {: n :} [: n + ;] ; ok
    # 5 n+ constant 5+ ok
    # 3 5+ execute . 8 ok
    # 7 5+ execute . 12 ok
    #

    I toyed with the idea of "passing" the man-or-boy test
    but did not pursue it last year due to health problems. :-(

    At least writing to upvalues works:

    # : m+ {: m :} <: inc 1 +to m ;> inc inc m . ; ok
    # 2 m+ 4 ok
    #

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Thu Feb 6 17:06:59 2025
    minforth@gmx.net (minforth) writes:
    : foo
    <: bar ." xyz" ;>
    bar
    ;

    I find this quite handy, since upvalues (locals within foo's
    context) are accessible from within bar.

    Do you implement proper static scoping? I.e., does your system pass
    the man-or-boy test. What about returning xts that reference outer
    locals, e.g.:

    : n+ ( n -- xt ) {: n :} [: n + ;] ;
    5 n+ constant 5+
    3 5+ execute .
    7 5+ execute .

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to dxf on Thu Feb 6 17:20:51 2025
    dxf <dxforth@gmail.com> writes:
    On 7/02/2025 12:59 am, minforth wrote:
    On Thu, 6 Feb 2025 12:57:12 +0000, Anton Ertl wrote:
    AFAIR 200x nested definitions were justified on the grounds named
    definitions were neither needed nor wanted

    Really? There's a proposal to eliminate named definitions? That's
    news to me.

    There are cases where you need to pass an xt to some word, and where
    the xt does not merit a name, e.g.,

    : (where) ( "name" -- ) \ gforth-internal
    (') [: over = ;] forwheres
    drop -1 where-index ! ;

    Of course, one could do that without quotations:

    : (where-helper) over = ;

    : (where) ( "name" -- ) \ gforth-internal
    (') ['] (where-helper) forwheres
    drop -1 where-index ! ;

    But it's more convenient and readable with quotations.

    and access to external
    locals not necessary.

    It's more that anything proper is pretty hard to implement and there
    was not enough demand for that nor existing practice that anybody
    wanted to propose for standardization.

    However, the topic came up repeatedly in discussions, so in 2018 I sat
    down to investigate the issue. After a while I had a paper design,
    but had my doubts that it is worth implementing it. I let Bernd
    Paysan read the paper, and he came up with further simplifications and implemented the result; the changes were so deep that I had to mostly
    rewrite the paper, resulting in our EuroForth 2018 paper
    [ertl&paysan18]. And Bernd embraced using the resulting closures.

    I still had my doubts about whether one really needs that, as I had
    not found a short example that showed a clear advantage over the
    alternatives. I asked Niklaus Wirth, who had kept access to outer
    locals in his languages for many decades, but his answer was that he
    finally removed that feature from Oberon 07 in 2013, and he did not
    provide such an example, either.

    Finally, such a usage was found by Bernd Paysan: He had implemented a
    variant of the actor model
    <https://gforth.org/manual/Message-queues.html> (inspired by Heinz
    Schnitter's Open Network Forth):

    One task sends a message consisting of an xt to another task, and the
    other task then executes it. But sending just an xt without any data
    is not very useful, so one could also send integers, strings, etc.
    The sender would send the data and then the xt, and the receiving task
    would push the data on the stack, and then execute the xt. However,
    several tasks can send messages to one task at the same time, so there
    was some additional twist to avoid mixing the parts of a message of
    one task with the parts of a message of another task. With closures
    that all became unnecessary: The data is part of the passed xt.

    Another development was pure-stack closures.

    But none of this existed when quotations were accepted for
    standardization.

    @InProceedings{ertl&paysan18,
    author = {M. Anton Ertl and Bernd Paysan},
    title = {Closures --- the {Forth} way},
    crossref = {euroforth18},
    pages = {17--30},
    url = {https://www.complang.tuwien.ac.at/papers/ertl%26paysan.pdf},
    url2 = {http://www.euroforth.org/ef18/papers/ertl.pdf},
    slides-url = {http://www.euroforth.org/ef18/papers/ertl-slides.pdf},
    video = {https://wiki.forth-ev.de/doku.php/events:ef2018:closures},
    OPTnote = {refereed},
    abstract = {In Forth 200x, a quotation cannot access a local
    defined outside it, and therefore cannot be
    parameterized in the definition that produces its
    execution token. We present Forth closures; they
    lift this restriction with minimal implementation
    complexity. They are based on passing parameters on
    the stack when producing the execution token. The
    programmer has to explicitly manage the memory of
    the closure. We show a number of usage examples.
    We also present the current implementation, which
    takes 109~source lines of code (including some extra
    features). The programmer can mechanically convert
    lexical scoping (accessing a local defined outside)
    into code using our closures, by applying assignment
    conversion and flat-closure conversion. The result
    can do everything one expects from closures,
    including passing Knuth's man-or-boy test and living
    beyond the end of their enclosing definitions.}
    }

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Thu Feb 6 19:15:11 2025
    Another aspect:

    Once I had read/write access to outer locals from within
    quotations (or nested functions) I found that I was using
    it more and more for code organisation i.e. encapsulation of
    factored code segments.

    In 'normal' Forth, factorization tends to clutter up the
    current compilation wordlist.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to dxforth@gmail.com on Fri Jan 31 11:47:34 2025
    In article <49736c0ec0e34ca5d67f4e0d6e8cbe2a080e38ab@i2pn2.org>,
    dxf <dxforth@gmail.com> wrote:
    On 31/01/2025 4:42 am, Hans Bezemer wrote:
    The lot of you have contributed to this episode - by discussing the previous episode on local variables.

    I thank you for your comments - and decided it was worth its own episode: https://youtu.be/FH4tWf9vPrA

    More than worth! ;-)

    Thanks Hans!

    Not sure if previously mentioned but here's another version of LOCAL

    : ;: >r ;

    : LOCAL ( x adr -- )
    -rot dup @ over 2>r ! ;: 2r> ! ;

    variable A variable B 8 a ! 7 b !

    : divide ( a b -- ) b local a local
    a @ b @ / . cr ;

    15 3 divide a ? b ?

    I hate TUCK but this code could benefit from RTUCK

    : RTUCK R> >R >R ;
    Obviously this must be low level.

    Groetjes Albert



    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to All on Fri Jan 31 12:45:11 2025
    Hi,
    Thanks for this video and this implementation of locals.

    But what about the speed of execution?

    I've written this test ( calculte the membership function, fuzzy logic)
    using:
    --stack juggling, the word: tri_mf1
    --global variables, the word: tri_mf2
    --local variables, (gforth), the word: tri_mf3
    --local variables, using this method presented here, the word: tri_mf3

    the program is here after (using gforth):

    : tri_mf1 ( x a b c -- mf) \ stack juggling
    >r >r ( x a ) ( r: c b)
    2dup < if rdrop rdrop 2drop 0 exit then

    ( x a) ( r: c b)
    over ( x a x ) ( r: c b)
    r@ ( x a x b) ( r: c b)
    <
    >r
    2dup >=
    r> and ( x a t) ( r: c b)

    if ( x a) ( r: c b)
    tuck ( a x a)
    - 100 ( a x-a 100) ( r: c b)
    rot ( x-a 100 a ) ( r: c b)
    r> ( x-a 100 a b) ( r: c)
    swap - ( x-a 100 b-a) ( r: c)
    */ rdrop exit
    then

    ( x a ) ( r: c b)
    drop ( x) ( r: c b)
    r@ 2dup ( x b x b) ( r: c b )
    >= ( x b t) ( r: c b)

    rdrop ( x b t ) ( r: c)
    >r ( x b ) ( r: c t)
    over ( x b x ) ( r: c t)
    r> ( x b x t) ( r: c)
    r@ ( x b x t c ) ( r: c)
    swap ( x b x c t) ( r: c)
    >r ( x b x c) ( r: c t)
    < r> and ( x b t) ( r: c)

    if ( x b) ( r: c)
    r@ ( x b c) ( r: c)
    rot - ( b c-x) ( r: c)
    100 ( b c-x 100) ( r: c)
    rot ( c-x 100 b ) ( r: c)
    r> swap - */ exit then
    2drop rdrop
    0
    ;

    variable a
    variable b
    variable c

    : tri_mf2 ( x a b c -- mf) \ global variables
    c ! b ! a !
    dup a @ < if drop 0 exit then
    dup a @ >= over b @ < and if a @ - 100 b @ a @ - */ exit then
    dup b @ >= over c @ < and if c @ swap - 100 c @ b @ - */ exit then
    drop 0

    ;

    : tri_mf3 ( x a b c -- mf) { a b c -- } \ locals ร  la gforth
    dup a < if drop 0 exit then
    dup a >= over b < and if a - 100 b a - */ exit then
    dup b >= over c < and if c swap - 100 c b - */ exit then
    drop 0
    ;


    : ;: >r ;
    : local r> swap dup >r @ >r ;: r> r> ! ;

    variable a
    variable b
    variable c

    : tri_mf4 ( x a b c -- mf) \ locals new
    a local b local c local
    c ! b ! a !
    dup a @ < if drop 0 exit then
    dup a @ >= over b @ < and if a @ - 100 b @ a @ - */ exit then
    dup b @ >= over c @ < and if c @ swap - 100 c @ b @ - */ exit then
    drop 0
    ;

    for the test I use:
    defer tri_mf

    : test 0 do 20 -50 0 50 tri_mf drop loop ;

    The results:

    ' tri_mf1 is tri_mf ok
    timer-reset 1000000 test .elapsed 94.866300ms ok
    ' tri_mf2 is tri_mf ok
    timer-reset 1000000 test .elapsed 96.399100ms ok
    ' tri_mf3 is tri_mf ok
    timer-reset 1000000 test .elapsed 83.403500ms ok
    ' tri_mf4 is tri_mf ok
    timer-reset 1000000 test .elapsed 211.670300ms ok

    I see that the proposed method is slower than the others

    Any explainations?

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to Hans Bezemer on Fri Jan 31 15:16:29 2025
    On Fri, 31 Jan 2025 14:55:05 +0000, Hans Bezemer wrote:

    On 31-01-2025 13:45, ahmed wrote:
    Hi,
    Thanks for this video and this implementation of locals.
    You're welcome.

    But what about the speed of execution?

    Well, that will depend a lot. I mean - you're not very honest if you use
    the built-in primitives of a Forth compiler. If you want to be honest,
    use the reference implementation of Forth 200x. Because primitives
    always win from high level code.

    I measured a size penalty of 4 times and a performance penalty of 10
    times when executing my example DIVIDE word. But then again - 4tH
    doesn't support locals natively. So that is to be expected.

    That is also the reason why I didn't include these findings. It would be dishonest to compare a high level implementation where others offer a implementation using primitives.

    For instance, I'm sure you understand that this benchmark will come out
    quite differently if I run it in 4tH.

    As I said - you can convenience your way out of everything. And no
    matter how fast your Forth, in the end raw assembly will beat
    everything, including C.

    So next time you want to make a point, yes, your native regular
    expression engine *WILL* probably beat even the neatest and most
    cleverly designed high level Forth matching routine.

    I see that the proposed method is slower than the others

    Any explainations?

    Yeah, I understand why you like locals, because your stack juggling
    skills need some polishing. But I got a video on that: https://youtu.be/gfE8arB3uWk

    First the preliminaries:

    : ;then postpone exit postpone then ; immediate
    : >zero dup xor ;
    : spin swap rot ;

    These are 4tH-ese, but I'm attached to them. ;-)
    ( n1 n2 n3 n4 -- n5)
    : calc - >r - 100 r> spin */ ;

    : tri_mf4
    >r rot r> swap >r spin ( c b a R: x )
    dup r@ >= if drop drop >zero rdrop ;then
    over r@ >= over r@ < and if dup r> swap calc ;then drop
    over r@ >= over r@ < and if over r> calc ;then
    rdrop drop >zero
    ;

    If you need more speed, inline CALC.
    BTW, replace "RDROP" with "R> DROP" if you need to.

    Hans Bezemer


    I didn't do this comparison for anything but just to test the
    possibility to integrate it (use it) in my forth programs which need
    speed (fuzzy logic, neural networks).
    I'm not arguing anything about forth or 4th, I'm just a user.

    Thanks

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to Hans Bezemer on Fri Jan 31 15:35:40 2025
    On Fri, 31 Jan 2025 14:55:05 +0000, Hans Bezemer wrote:

    On 31-01-2025 13:45, ahmed wrote:
    Hi,
    Thanks for this video and this implementation of locals.
    You're welcome.
    ..
    Any explainations?

    Yeah, I understand why you like locals, because your stack juggling
    skills need some polishing. But I got a video on that: https://youtu.be/gfE8arB3uWk


    I'm still learning and I don't prefer locals or any other method. I just
    use what it comes handy. And yes, I like stack juggling, but I'm not
    very skilled for.

    First the preliminaries:

    : ;then postpone exit postpone then ; immediate
    : >zero dup xor ;
    : spin swap rot ;

    These are 4tH-ese, but I'm attached to them. ;-)
    ( n1 n2 n3 n4 -- n5)
    : calc - >r - 100 r> spin */ ;

    : tri_mf4
    >r rot r> swap >r spin ( c b a R: x )
    dup r@ >= if drop drop >zero rdrop ;then
    over r@ >= over r@ < and if dup r> swap calc ;then drop
    over r@ >= over r@ < and if over r> calc ;then
    rdrop drop >zero
    ;

    This one (tri_mf5) gives better result and thanks for it.
    I find:
    ' tri_mf1 is tri_mf timer-reset 1000000 test .elapsed 88.759600ms ok
    ' tri_mf2 is tri_mf timer-reset 1000000 test .elapsed 100.902500ms ok
    ' tri_mf3 is tri_mf timer-reset 1000000 test .elapsed 86.124300ms ok
    ' tri_mf4 is tri_mf timer-reset 1000000 test .elapsed 209.688300ms ok
    ' tri_mf5 is tri_mf timer-reset 1000000 test .elapsed 96.897100ms ok

    If you need more speed, inline CALC.
    BTW, replace "RDROP" with "R> DROP" if you need to.

    Hans Bezemer


    Thanks for your reply and the way to speedup the method.
    I'll use it, be sure.

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to Hans Bezemer on Fri Jan 31 16:30:35 2025
    On Fri, 31 Jan 2025 14:55:05 +0000, Hans Bezemer wrote:
    First the preliminaries:

    : ;then postpone exit postpone then ; immediate
    : >zero dup xor ;
    : spin swap rot ;

    These are 4tH-ese, but I'm attached to them. ;-)
    ( n1 n2 n3 n4 -- n5)
    : calc - >r - 100 r> spin */ ;

    : tri_mf4
    >r rot r> swap >r spin ( c b a R: x )
    dup r@ >= if drop drop >zero rdrop ;then
    over r@ >= over r@ < and if dup r> swap calc ;then drop
    over r@ >= over r@ < and if over r> calc ;then
    rdrop drop >zero
    ;


    I studied this version you've prposed: tri_mf4.
    I find it amazing, comprehensible (redibility) and very clever (compared
    to mine tri_mf1 in the previous post).
    Thanks a lot for it.

    Hans Bezemer

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to Hans Bezemer on Fri Jan 31 16:14:27 2025
    On Fri, 31 Jan 2025 15:43:11 +0000, Hans Bezemer wrote:

    On 31-01-2025 16:16, ahmed wrote:
    I didn't do this comparison for anything but just to test the
    possibility to integrate it (use it) in my forth programs which need
    speed (fuzzy logic, neural networks).
    I'm not arguing anything about forth or 4th, I'm just a user.

    "But what about the speed of execution?"
    Well, you wanted an explanation - and you got one: primitives will
    always win from fancy, high level implementations. That's why you have
    to test your specific implementation if the documentation isn't clear on
    how certain features are implemented.

    Thanks, for this explanation.

    ..

    From a user perspective, the story is very simple - pick the one that
    fits you. Who am I to argue with what you need? I don't know you and I
    don't know the task you want to fulfill.

    That's what I'm doing.

    If I have any beef it's why do you need a 50 line (high level) reference implementation if you can implement the same functionality with one
    single cleverly designed line (which is much more Forth like).

    Yes, you are right.

    And yeah - don't use locals at all. Bad habit. Gee - coming from me,
    that's a big surprise.

    Hans Bezemer

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to ahmed on Fri Jan 31 14:06:00 2025
    melahi_ahmed@yahoo.fr (ahmed) writes:
    : tri_mf3 ( x a b c -- mf) { a b c -- } \ locals ร  la gforth
    dup a < if drop 0 exit then
    dup a >= over b < and if a - 100 b a - */ exit then
    dup b >= over c < and if c swap - 100 c b - */ exit then
    drop 0
    ;

    This seems more in the locals spirit:

    : blend { a x b -- n } 100 b x - b a - */ ;
    : tri_mf3.1 { x a b c -- mf }
    a x <= x b < AND IF b x a blend EXIT THEN
    b x <= x c < AND IF b x c blend EXIT THEN
    0 ;

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to Paul Rubin on Fri Jan 31 23:05:15 2025
    On Fri, 31 Jan 2025 22:06:00 +0000, Paul Rubin wrote:

    melahi_ahmed@yahoo.fr (ahmed) writes:
    : tri_mf3 ( x a b c -- mf) { a b c -- } \ locals ร  la gforth
    dup a < if drop 0 exit then
    dup a >= over b < and if a - 100 b a - */ exit then
    dup b >= over c < and if c swap - 100 c b - */ exit then
    drop 0
    ;

    This seems more in the locals spirit:

    : blend { a x b -- n } 100 b x - b a - */ ;
    : tri_mf3.1 { x a b c -- mf }
    a x <= x b < AND IF b x a blend EXIT THEN
    b x <= x c < AND IF b x c blend EXIT THEN
    0 ;

    Yes, thanks.
    I know that with a little bit of thinking one can get good solutions.

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to All on Fri Jan 31 23:21:19 2025
    Here is another one:

    : blend { b x a -- } 100 x a - b a - */ ;
    : tri_mf3.2 { x a b c -- }
    b x a blend b x c blend min 0 max ;

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to dxf on Sat Feb 1 07:50:49 2025
    dxf <dxforth@gmail.com> writes:
    If I use locals I'm more likely to
    use the ANS notation. I notice Forth Inc does too - perhaps why they were so >adverse to conceding to { } .

    The reason why Forth, Inc. argued against { } is that they support an
    existing code base that uses { } for comments; they use { } comments extensively in SwiftForth, and their customers use it, too. They
    voted for {: :}, so they obviously don't have a problem with the
    ordering of locals in {: :} (which is the same as for { }).

    Using WHERE LOCALS| in SwiftForth x64-Linux 4.0.0-RC89 only brings up
    the definition of LOCALS|, but no uses. "WHERE {:" brings up the
    definition and 5 uses of "{:", all with more than one local; so they
    obviously do not have a problem with the ordering of locals in {: :}.
    Can you elaborate on what you have noticed?

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Sat Feb 1 07:26:11 2025
    Paul Rubin <no.email@nospam.invalid> writes:
    This seems more in the locals spirit:

    : blend { a x b -- n } 100 b x - b a - */ ;
    : tri_mf3.1 { x a b c -- mf }
    a x <= x b < AND IF b x a blend EXIT THEN
    b x <= x c < AND IF b x c blend EXIT THEN
    0 ;

    Forth has a word WITHIN that should come in handy here:

    : tri_mf3.1 { x a b c -- mf }
    x a b within IF b x a blend EXIT THEN
    x b c within IF b x c blend EXIT THEN
    0 ;

    Another alternative, assuming a<=b<=c:

    : tri_mf3.1 { x a b c -- mf }
    case
    x a < ?of 0 endof
    x b < ?of b x a blend endof
    x c < ?of b x c blend endof
    0 0
    endcase ;

    The disadvantage in the latter case is that the above and below cases
    are separate, needing another branch and possibly increasing the
    number of mispredictions. This can be addressed with:

    [undefined] select [if]
    : select ( u1 u2 f -- u )
    if swap then nip ;
    [then]

    : tri_mf3.1 { x a b c -- mf }
    x a c within if
    b x x b < a c select blend
    else
    0
    then ;

    In Gforth (development) SELECT is a primitive which hopefully does not
    branch; in that case you have only one branch here.

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to Anton Ertl on Sat Feb 1 08:22:01 2025
    On Sat, 1 Feb 2025 7:26:11 +0000, Anton Ertl wrote:

    Paul Rubin <no.email@nospam.invalid> writes:
    This seems more in the locals spirit:

    : blend { a x b -- n } 100 b x - b a - */ ;
    : tri_mf3.1 { x a b c -- mf }
    a x <= x b < AND IF b x a blend EXIT THEN
    b x <= x c < AND IF b x c blend EXIT THEN
    0 ;

    Forth has a word WITHIN that should come in handy here:

    : tri_mf3.1 { x a b c -- mf }
    x a b within IF b x a blend EXIT THEN
    x b c within IF b x c blend EXIT THEN
    0 ;

    Yes, it works fine. Thanks.

    Another alternative, assuming a<=b<=c:

    : tri_mf3.1 { x a b c -- mf }
    case
    x a < ?of 0 endof
    x b < ?of b x a blend endof
    x c < ?of b x c blend endof
    0 0
    endcase ;

    It also works fine without problems.



    The disadvantage in the latter case is that the above and below cases
    are separate, needing another branch and possibly increasing the
    number of mispredictions. This can be addressed with:

    [undefined] select [if]
    : select ( u1 u2 f -- u )
    if swap then nip ;
    [then]

    : tri_mf3.1 { x a b c -- mf }
    x a c within if
    b x x b < a c select blend
    else
    0
    then ;

    This latter gives wrong results:

    -100 -50 0 50 tri_mf3.1 . 0 ok \ this is true
    -50 -50 0 50 tri_mf3.1 . -4900 ok \ false, must be 0
    -10 -50 0 50 tri_mf3.1 . -900 ok \ false, must be 80
    0 -50 0 50 tri_mf3.1 . \ here division by zero
    *the terminal*:47:12: error: Division by zero
    0 -50 0 50 >>>tri_mf3.1<<< .

    I think it must be:
    (the true/false flag must on tos for select as you defined it)

    : tri_mf3.1 { x a b c -- mf }
    x a c within if
    b x a c x b < select blend
    else
    0
    then ;

    and this one works fine.

    -100 -50 0 50 tri_mf3.1 . 0 ok
    -50 -50 0 50 tri_mf3.1 . 0 ok
    -10 -50 0 50 tri_mf3.1 . 80 ok
    0 -50 0 50 tri_mf3.1 . 100 ok
    10 -50 0 50 tri_mf3.1 . 80 ok
    50 -50 0 50 tri_mf3.1 . 0 ok
    100 -50 0 50 tri_mf3.1 . 0 ok


    In Gforth (development) SELECT is a primitive which hopefully does not branch; in that case you have only one branch here.

    - anton


    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to minforth on Fri Feb 7 12:03:34 2025
    In article <3955434636b2a293c6a9c6d726ff6eae@www.novabbs.com>,
    minforth <minforth@gmx.net> wrote:
    On Thu, 6 Feb 2025 12:57:12 +0000, Anton Ertl wrote:

    minforth@gmx.net (minforth) writes:
    On Tue, 4 Feb 2025 12:26:26 +0000, albert@spenarnc.xs4all.nl wrote:
    Remark that is doesn't introduce any unfamiliar syntax,
    only does away with "Forth shall not nested definitions"
    (Says who?)

    Given that a Forth system supports quotations and xt-locals,
    nested definitions are easy to implement.

    You mean:

    : foo [: ." xyz" ;] {: xt: bar :} bar ;

    Sure, you then get a local that behaves similar to a nested
    definition, but I don't think anyone would want to do that.

    Yes, that's the mechanism. Actually I use it with some syntactic
    sugar for better readability:

    : foo
    <: bar ." xyz" ;>
    bar
    ;
    You mean
    : foo
    [ : bar ." xyz" ; ]
    bar
    ;

    I find this quite handy, since upvalues (locals within foo's
    context) are accessible from within bar.

    Groetjes Albert
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to minforth on Fri Feb 7 11:50:17 2025
    In article <392b236c4183716c5db29d3d8ae07e33@www.novabbs.com>,
    minforth <minforth@gmx.net> wrote:
    On Thu, 6 Feb 2025 11:11:28 +0000, albert@spenarnc.xs4all.nl wrote:

    With my enhanced [ ] pair, you can introduce whatever local
    widgets, in particular objects, you choose.

    The goal of language design is not to overload with features

    But haven't you overloaded the old [ ] with new features now? ;-)

    Or have I eliminated an unexpecte restriction?

    Groetjes Albert
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to minforth on Fri Feb 7 11:56:08 2025
    In article <53de9d9c27593c926215da4fb41e682f@www.novabbs.com>,
    minforth <minforth@gmx.net> wrote:
    There is indeed a restriction in standard ยง3.4:

    "A program shall not attempt to nest compilation of definitions.
    During the compilation of the current definition, a program
    shall not execute any defining word, :NONAME, or any definition
    that allocates dictionary data space."

    However because local names are not compiled into dictionary data
    space, but use their own transient dictionary entries, which
    disappear after compilation, this restriction as of $3.4 does not
    apply to embedded functions emulated by xt-locals, aka
    defer-flavoured locals.

    This is hypocritical. You want local, i.e. nested words, it is
    forbidden by the standards, now you implement it differently and
    maintain you abide by the standard.

    Groetjes Albert
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Fri Feb 7 10:20:40 2025
    Where does this self-limiting backwardness come from?

    Virtually all modern embedded devices communicate with
    their outside world, and multitasking comes naturally.
    On the software side, closures can be a very helpful
    tool for communication and task management.

    So virtually all modern programming languages support
    closures (albeit with subtle nuances). I think a modern
    Forth should not be closed to modern programming concepts.
    DOS is finally history.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to albert@spenarnc.xs4all.nl on Fri Feb 7 12:21:35 2025
    On Fri, 7 Feb 2025 11:03:34 +0000, albert@spenarnc.xs4all.nl wrote:
    Yes, that's the mechanism. Actually I use it with some syntactic
    sugar for better readability:

    : foo
    <: bar ." xyz" ;>
    bar
    ;

    You mean
    : foo
    [ : bar ." xyz" ; ]
    bar
    ;

    No I don't. My
    <: bar creates a local
    whereas your
    [ : bar seems to create a dictionary entry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From sjack@21:1/5 to minforth on Fri Feb 7 17:50:02 2025
    minforth <minforth@gmx.net> wrote:

    However because local names are not compiled into dictionary data
    space, but use their own transient dictionary entries, which


    -- The goal then is nesting a definition to space outside of the
    -- dictionary. Using what's at hand, Toad version of Wil Baden's local
    -- macros:
    : foo
    [ '." xyz"' /mm: bar ]
    mm bar
    ;

    -- executing the nested code from the parent word
    i. foo --> xyz

    -- executing the code after the parent word has terminated.
    i. mm bar --> xyz

    -- Can also pass value to the local definition
    : foo
    '+ mmx: bardat' eval
    %'mm bardat ." xyz: " .' /mm: bar% eval
    'mm bar' eval
    ;

    i. 40 2 foo --> xyz: 42
    i. mm bar --> xyz: 42

    --
    me

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Anton Ertl on Fri Feb 7 18:09:57 2025
    On Thu, 6 Feb 2025 17:06:59 +0000, Anton Ertl wrote:

    minforth@gmx.net (minforth) writes:
    : foo
    <: bar ." xyz" ;>
    bar
    ;

    I find this quite handy, since upvalues (locals within foo's
    context) are accessible from within bar.

    Do you implement proper static scoping? I.e., does your system pass
    the man-or-boy test. What about returning xts that reference outer
    locals, e.g.:

    : n+ ( n -- xt ) {: n :} [: n + ;] ;
    5 n+ constant 5+
    3 5+ execute .
    7 5+ execute .

    As I said, I don't have proper scoping, as needed for closures
    (current MinForth only captures the outer context once, so it
    does not work with multiple instances)

    However, does this work in gforth?

    : n+ ( n -- xt ) {: n :} [: n + ;] ;
    5 n+ constant 5+
    10 n+ constant 10+ ( 2nd instance )
    2 5+ . ( 7 )
    2 10+ . ( 12 )
    3 5+ . ( 8 )

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to dxforth@gmail.com on Sat Feb 1 13:30:25 2025
    In article <a33bc1882ad45b21b93e051c9be958821dcfb27b@i2pn2.org>,
    dxf <dxforth@gmail.com> wrote:
    On 1/02/2025 6:50 pm, Anton Ertl wrote:
    dxf <dxforth@gmail.com> writes:
    If I use locals I'm more likely to
    use the ANS notation. I notice Forth Inc does too - perhaps why they were so
    adverse to conceding to { } .

    The reason why Forth, Inc. argued against { } is that they support an
    existing code base that uses { } for comments; they use { } comments
    extensively in SwiftForth, and their customers use it, too. They
    voted for {: :}, so they obviously don't have a problem with the
    ordering of locals in {: :} (which is the same as for { }).

    IIRC FI was pressed hard for { } but they wouldn't budge. It was odd
    since a single character to delimit a comment was inherently problematic.

    You got that right. In a language like Forth or assembler, the only good solution is a single character that runs until the end of line, such as
    ; and \ .
    DOC ENDDOC is used to comment pages of code, so that you cannot see it is commented out bleh!

    If your editor isn't capable to turn

    : XXGCD 2DUP SWAP XGCD ( A B D GCD )
    DUP 0< IF NEGATE SWAP NEGATE SWAP THEN
    DUP >R OVER >R ( R: GCD D )
    >R * R> SWAP - / ( A B D GCD -- C )
    R> R> ;

    into

    \ : XXGCD 2DUP SWAP XGCD ( A B D GCD )
    \ DUP 0< IF NEGATE SWAP NEGATE SWAP THEN
    \ DUP >R OVER >R ( R: GCD D )
    \ >R * R> SWAP - / ( A B D GCD -- C )
    \ R> R> ;

    and reverse it, get your money back.

    I find it hard to believe FI customers wouldn't have jumped at the chance
    to get a proper comment scheme and nicer looking locals syntax. As it is
    now they're stuck with two lesser things.

    Not hard to believe.
    Changing millions of lines of code, for a dubious advantage.


    Using WHERE LOCALS| in SwiftForth x64-Linux 4.0.0-RC89 only brings up
    the definition of LOCALS|, but no uses. "WHERE {:" brings up the
    definition and 5 uses of "{:", all with more than one local; so they
    obviously do not have a problem with the ordering of locals in {: :}.
    Can you elaborate on what you have noticed?

    Interesting since...

    SwiftForth i386-Win32 3.11.9-RC1 01-Sep-2022

    85 matches for LOCAL| (a few false positives in that)
    0 matches for {: :} (despite being implemented)

    LOCAL| apparently fit the bill. It still is standard. Why change?
    If you have a large code base, uniformity is an advantage.

    Groetjes Albert.
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From mhx@21:1/5 to ahmed on Sat Feb 1 15:31:01 2025
    On Fri, 31 Jan 2025 12:45:11 +0000, ahmed wrote:

    Hi,
    Thanks for this video and this implementation of locals.

    But what about the speed of execution?
    [..]
    The results:

    ' tri_mf1 is tri_mf ok
    timer-reset 1000000 test .elapsed 94.866300ms ok
    ' tri_mf2 is tri_mf ok
    timer-reset 1000000 test .elapsed 96.399100ms ok
    ' tri_mf3 is tri_mf ok
    timer-reset 1000000 test .elapsed 83.403500ms ok
    ' tri_mf4 is tri_mf ok
    timer-reset 1000000 test .elapsed 211.670300ms ok

    I see that the proposed method is slower than the others

    Any explainations?

    I had to modify ;: and LOCALE aka LOCAL for iForth
    ( tail-call optimization is forced off with `[ -OPT ]` ).

    The loops here are 1000 x larger (timing goes from ms to seconds)
    so that they can be properly compared with your results. However,
    the outcome is qualitatively the same: tri-mf4 is 3x worse than
    the others. PARAM| is fastest, but it can only shine when
    there are less than 3 stack parameters.

    : ;: >r [ -OPT ] ;
    : locale r> swap dup >r @ >r ;: r> r> ! [ -OPT ] ;

    : tri_mf4 ( x a b c -- mf) \ locals new
    c locale b locale a locale
    c ! b ! a !
    dup a @ < if drop 0 exit then
    dup a @ >= over b @ < and if a @ - 100 b @ a @ - */ exit then
    dup b @ >= over c @ < and if c @ swap - 100 c @ b @ - */ exit then
    drop 0
    ;

    defer tri_mf

    : test 0 do 20 -50 0 50 tri_mf drop loop ;

    : DO-TEST
    CR ." \ The results:"
    CR ." \ tri_mf1: " ['] tri_mf1 [is] tri_mf
    timer-reset #1000000000 test .elapsed ." ( 94.866 s)"
    CR ." \ tri_mf2: " ['] tri_mf2 [is] tri_mf
    timer-reset #1000000000 test .elapsed ." ( 96.399 s)"
    CR ." \ tri_mf3: " ['] tri_mf3 [is] tri_mf
    timer-reset #1000000000 test .elapsed ." ( 83.403 s)"
    CR ." \ tri_mf4: " ['] tri_mf4 [is] tri_mf
    timer-reset #1000000000 test .elapsed ." ( 211.670 s)" ;

    FORTH> DO-TEST
    \ The results:
    \ tri_mf1: 3.273 seconds elapsed. ( 94.866 s)
    \ tri_mf2: 2.727 seconds elapsed. ( 96.399 s)
    \ tri_mf3: 2.910 seconds elapsed. ( 83.403 s)
    \ tri_mf4: 8.006 seconds elapsed. ( 211.670 s) ok

    The explanation: execution speed is by no means the main goal
    of Forth. Who cares about a factor of 30 or 40?

    -marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to dxf on Sat Feb 1 17:30:24 2025
    dxf <dxforth@gmail.com> writes:
    Interesting since...

    SwiftForth i386-Win32 3.11.9-RC1 01-Sep-2022

    85 matches for LOCAL| (a few false positives in that)
    0 matches for {: :} (despite being implemented)

    Apparently your search for {: is broken. Here's what I get:

    [/usr/local/src/SwiftForth-3.11.0:155305] rg -i -c 'locals[|]' unsupported/fslib/library/integral.f:4
    unsupported/fslib/library/polrat.f:4
    unsupported/fslib/library/svd.f:9
    unsupported/fslib/library/gaussj.f:13
    unsupported/fslib/library/amoeba.f:2
    unsupported/fslib/library/levmarq.f:7
    lib/options/rstruct.f:4
    lib/options/win32/sprintf.f:1
    lib/options/win32/screensaver.f:1
    lib/options/win32/gditools.f:6
    lib/options/win32/gdiext.f:2
    lib/options/win32/turtle.f:1
    unsupported/contrib/formattext.f:2
    lib/options/win32/boxed.f:9
    lib/samples/win32/rndlines.f:1
    lib/samples/win32/scribble.f:1
    lib/samples/win32/tetris/guitetris.f:1
    lib/samples/win32/showfonts.f:1
    lib/samples/win32/mdi.f:1
    src/ide/exception.f:1
    src/ide/strings.f:2
    src/ide/localvariables.f:4
    src/ide/win32/editor.f:1
    src/ide/win32/winmgmt.f:3
    src/ide/win32/tty/repaint.f:1
    src/ide/win32/tty/tty.f:1
    lib/samples/macos/mac/cocoa/cocoastarter.f:1 lib/samples/macos/mac/doc/CHANGES.txt:1

    [/usr/local/src/SwiftForth-3.11.0:155306] rg -i -c '[{]:'
    src/ide/aswoop.f:3
    src/ide/localvariables.f:4

    [/usr/local/src/SwiftForth-3.11.0:155307] + /usr/local/src/SwiftForth-4.0.0-RC89 /usr/local/src/SwiftForth-3.11.0 /usr/local/src/SwiftForth-3.11.0

    [/usr/local/src/SwiftForth-4.0.0-RC89:155308] rg -i -c 'locals[|]' src/ide/locals.f:5
    src/arch/i386/ide/exception.f:1
    src/arch/x64/ide/exception.f:1
    unsupported/contrib/formattext.f:2 unsupported/forth-2012-tests/localstest.fth:1 unsupported/fslib/library/integral.f:4
    unsupported/fslib/library/polrat.f:4
    unsupported/fslib/library/svd.f:9
    unsupported/fslib/library/gaussj.f:13
    unsupported/fslib/library/levmarq.f:7
    unsupported/fslib/library/amoeba.f:2

    [/usr/local/src/SwiftForth-4.0.0-RC89:155309] rg -i -c '[{]:' unsupported/forth-2012-tests/localstest.fth:38
    src/ide/aswoop.f:3
    src/ide/strings.f:2
    src/ide/locals.f:4
    src/ide/imports.f:2
    src/arch/x64/decode.f:1

    Apparently the use of LOCALS| is connected mostly with win32 files,
    which do not come with 4.0.0-RC89 in the version I use. And most of
    the remaining uses of LOCALS| are in unsupported libraries, in
    particular the Forth Scientific Library. The tool I used for this is
    ripgrep, if anybody is wondering.

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to minforth on Sat Feb 8 11:06:27 2025
    minforth@gmx.net (minforth) writes:
    However, does this work in gforth?

    : n+ ( n -- xt ) {: n :} [: n + ;] ;
    5 n+ constant 5+
    10 n+ constant 10+ ( 2nd instance )
    2 5+ . ( 7 )
    2 10+ . ( 12 )
    3 5+ . ( 8 )

    : n+ ( n -- xt ) [{: n :}d n + ;] ;
    5 n+ constant 5+-xt
    10 n+ constant 10+-xt
    2 5+-xt execute . \ output: 7
    2 10+-xt execute . \ output: 12
    3 5+-xt execute . \ output: 8

    The :}D means that the closure data is stored in the dictionary; there
    is also :}L (for locals, deallocated when the surrounding definition
    is exited), :}H (heap, deallocated with FREE-CLOSURE), and :}H1 (heap, deallocated right after the first (and only) execution).

    For the common cases of passing one cell or one double, or one FP
    value to a closure, there are also pure-stack closures:

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

    works just like the locals-using N+ above. We have several numbers
    [T:A where T is the type (N for cell, D for double, F for float), and
    A is the allocation (L for local, D for dictionary, H for heap,
    currently no H1).

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Sat Feb 8 12:31:45 2025
    Thanks, that makes sense. Each call to the outer function
    creates a data record for (the contexts of) each of its inner
    closure(s). The context records need to be managed somehow.

    Personally, I do not need more than the limited functionality
    that MinForth now provides. But I have a good idea how to
    extend it to full closures whenever the need arises in the
    future. That is, perhaps never... ;-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to minforth on Sat Feb 8 23:07:11 2025
    minforth@gmx.net (minforth) writes:
    Thanks, that makes sense. Each call to the outer function
    creates a data record for (the contexts of) each of its inner
    closure(s). The context records need to be managed somehow.

    Most languages with closures also have garbage collection, or anyway scope-controlled deallocation like in C++. It may not be obvious, but
    closures are sort of the same thing as OOP, just viewed from a different
    angle. Storage for the internal data of OOP instances has to be managed
    in about the same way.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Paul Rubin on Sun Feb 9 08:18:41 2025
    On Sun, 9 Feb 2025 7:07:11 +0000, Paul Rubin wrote:

    minforth@gmx.net (minforth) writes:
    Thanks, that makes sense. Each call to the outer function
    creates a data record for (the contexts of) each of its inner
    closure(s). The context records need to be managed somehow.

    Most languages with closures also have garbage collection, or anyway scope-controlled deallocation like in C++. It may not be obvious, but closures are sort of the same thing as OOP, just viewed from a different angle. Storage for the internal data of OOP instances has to be managed
    in about the same way.

    I found a good article that starts with an anecdote about Anton :-) https://kidneybone.com/c2/wiki/ClosuresAndObjectsAreEquivalent

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to ruvim.pinka@gmail.com on Sun Feb 2 12:13:54 2025
    In article <vnkvvq$2a5o$1@dont-email.me>, Ruvim <ruvim.pinka@gmail.com> wrote: >On 2025-01-31 11:33, dxf wrote:

    Not sure if previously mentioned but here's another version of LOCAL

    : ;: >r ;

    : LOCAL ( x adr -- )
    r> -rot dup @ over 2>r ! ;: 2r> ! ;

    variable A variable B 8 a ! 7 b !

    : divide ( a b -- ) b local a local
    a @ b @ / . cr ;

    15 3 divide a ? b ?



    This approach does not work well with catch/throw. Because `throw` must >restore the values of all "local" variables that are used in the
    definitions whose execution is being terminated. And this is difficult
    to implement.


    See also 13.3.3.1, item c, ><https://forth-standard.org/standard/locals#subsubsection.13.3.3.1>

    | ABORT shall release all local storage resources,
    | and CATCH / THROW (if implemented) shall release
    | such resources for all definitions whose execution
    | is being terminated.

    Nice catch!

    However, this is highly artificial. You have to have a recursive routine in this
    vein:

    RECURSIVE
    : fun .. fun ... fun . 'fun CATCH .. fun ... ;

    otherwise the global VARIABLE's can be ignored.

    I would be interested to see a remotely plausible example of this.
    Mixing recursion and exception is ill advised by iq<160.

    Groetjes Albert




    --
    Ruvim

    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to albert@spenarnc.xs4all.nl on Sun Feb 2 13:26:22 2025
    albert@spenarnc.xs4all.nl writes:
    I would be interested to see a remotely plausible example of this.
    Mixing recursion and exception is ill advised by iq<160.

    The recursive function calls something that throws an exception, and it
    should be ok to think about those things separately.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Sun Feb 9 15:54:29 2025
    Paul Rubin <no.email@nospam.invalid> writes:
    It may not be obvious, but
    closures are sort of the same thing as OOP, just viewed from a different >angle.

    At least that's what the fans of languages claim that have only one or
    the other. And Forth has had ;CODE and DOES> very early on, and those
    who don't have objects or closures claim that DOES> is sort of the
    same as OOP and closures, just viewed from a different angle.

    Yet we found it fruitful to add object-oriented extensions in various
    Forth systems, and we find it fruitful to add closures in Gforth.

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Anton Ertl on Sun Feb 9 09:55:29 2025
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    The :}D means that the closure data is stored in the dictionary; there
    is also :}L (for locals, deallocated when the surrounding definition
    is exited), :}H (heap, deallocated with FREE-CLOSURE), and :}H1 (heap, deallocated right after the first (and only) execution).

    This is pretty cool, but it looks like quotations within the closure
    aren't allowed to access the closure's locals, using them as OOP-like
    state. In the current Gforth git snapshot:

    : x [{: n :}d [: n 1+ dup to n ;] ;]h 0 execute ;

    gives:

    *the terminal*:26:30: error: Unsupported operation
    : x [{: n :}d [: n 1+ dup to >>>n<<< ;] ;]h 0 execute ;

    This is an attempt to make a counting function, like in Scheme:

    (define (x)
    ((lambda (n)
    (lambda ()
    (set! n (+ 1 n))
    n)) 0))

    (define a (x))

    (a) ; 1
    (a) ; 2, etc.

    It would be interesting if your conservative gc could be made reliable
    and included with gforth, and then another suffix could be added to put
    closure locals in the gc'd heap. "Reliable" = scan the return and
    locals stacks, via suitable extensions added to GC. Also in a threaded
    program I guess it would have to stop any threads that shared a GC'd
    heap during collection of that heap.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Paul Rubin on Sun Feb 9 19:15:29 2025
    On Sun, 9 Feb 2025 17:55:29 +0000, Paul Rubin wrote:

    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    The :}D means that the closure data is stored in the dictionary; there
    is also :}L (for locals, deallocated when the surrounding definition
    is exited), :}H (heap, deallocated with FREE-CLOSURE), and :}H1 (heap,
    deallocated right after the first (and only) execution).

    This is pretty cool, but it looks like quotations within the closure
    aren't allowed to access the closure's locals, using them as OOP-like
    state. In the current Gforth git snapshot:

    : x [{: n :}d [: n 1+ dup to n ;] ;]h 0 execute ;

    gives:

    *the terminal*:26:30: error: Unsupported operation
    : x [{: n :}d [: n 1+ dup to >>>n<<< ;] ;]h 0 execute ;

    This is an attempt to make a counting function, like in Scheme:

    (define (x)
    ((lambda (n)
    (lambda ()
    (set! n (+ 1 n))
    n)) 0))

    (define a (x))

    (a) ; 1
    (a) ; 2, etc.

    FWIW a single quotation-based counter in another Forth:

    MinForth 3.6 (32 bit)
    # defer ctr ok
    # : init { n } [: n 1+ dup to n ;] ; ok
    # 4 init is ctr ok
    # ctr . 5 ok
    # ctr . 6 ok
    # ctr . 7 ok
    #

    Generalisation would of course require closures and memory
    management after use.

    IOW read/write access to locals of the parent function opens
    up new possiblities in Forth - perhaps also an idea for gforth.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to minforth on Sun Feb 9 14:30:28 2025
    minforth@gmx.net (minforth) writes:
    FWIW a single quotation-based counter in another Forth:

    MinForth 3.6 (32 bit)
    # defer ctr ok
    # : init { n } [: n 1+ dup to n ;] ; ok
    # 4 init is ctr ok
    # ctr . 5 ok

    Questions:

    1) where does the storage cell for n live, after init has returned?

    2) what if you make more than one counter?

    3) why did you use defer instead of something like CONSTANT?

    thanks

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to Paul Rubin on Sun Feb 9 23:08:22 2025
    Paul Rubin <no.email@nospam.invalid> writes:
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    The :}D means that the closure data is stored in the dictionary; there
    is also :}L (for locals, deallocated when the surrounding definition
    is exited), :}H (heap, deallocated with FREE-CLOSURE), and :}H1 (heap,
    deallocated right after the first (and only) execution).

    This is pretty cool, but it looks like quotations within the closure
    aren't allowed to access the closure's locals

    Correct. You need to use a closure (and perform closure conversion
    and assignment conversion manually, if needed).

    This is an attempt to make a counting function, like in Scheme:

    (define (x)
    ((lambda (n)
    (lambda ()
    (set! n (+ 1 n))
    n)) 0))

    (define a (x))

    (a) ; 1
    (a) ; 2, etc.

    : x ( -- xt )
    here 0 , [{: addr :}d addr @ 1+ dup addr ! ;] ;

    x alias a
    x alias b
    a . \ 1
    a . \ 2
    b . \ 1
    a . \ 3

    Given that you are using dictionary allocation, traditional Forth
    allocation for the mutable data is fine. There is also a syntax for
    allocating data in other locations, but you don't need it with
    dictionary allocation and traditional dictionary allocation is usually
    shorter. With that syntax the equivalent would be:

    : x ( -- xt )
    0 <{: w^ n :}d n ;> drop [{: n :}d n @ 1+ dup n ! ;] ;

    The other issue is that the value-flavoured local N or ADDR in the
    closure cannot be changed in a way that takes effect outside the
    closure. So you give an address to it, and use @, ! etc. to work on
    that (assignment conversion).

    It would be interesting if your conservative gc could be made reliable
    and included with gforth, and then another suffix could be added to put >closure locals in the gc'd heap.

    Yes. There is :}xt for passing an xt that performs the allocation.

    See <https://gforth.org/manual/Closures.html> for all of these topics.
    Or read the paper:

    @InProceedings{ertl&paysan18,
    author = {M. Anton Ertl and Bernd Paysan},
    title = {Closures --- the {Forth} way},
    crossref = {euroforth18},
    pages = {17--30},
    url = {https://www.complang.tuwien.ac.at/papers/ertl%26paysan.pdf},
    url2 = {http://www.euroforth.org/ef18/papers/ertl.pdf},
    slides-url = {http://www.euroforth.org/ef18/papers/ertl-slides.pdf},
    video = {https://wiki.forth-ev.de/doku.php/events:ef2018:closures},
    OPTnote = {refereed},
    abstract = {In Forth 200x, a quotation cannot access a local
    defined outside it, and therefore cannot be
    parameterized in the definition that produces its
    execution token. We present Forth closures; they
    lift this restriction with minimal implementation
    complexity. They are based on passing parameters on
    the stack when producing the execution token. The
    programmer has to explicitly manage the memory of
    the closure. We show a number of usage examples.
    We also present the current implementation, which
    takes 109~source lines of code (including some extra
    features). The programmer can mechanically convert
    lexical scoping (accessing a local defined outside)
    into code using our closures, by applying assignment
    conversion and flat-closure conversion. The result
    can do everything one expects from closures,
    including passing Knuth's man-or-boy test and living
    beyond the end of their enclosing definitions.}
    }

    Also in a threaded
    program I guess it would have to stop any threads that shared a GC'd
    heap during collection of that heap.

    That's a tough one. My current thinking is along the lines of a
    per-thread allocator and garbage-collector, with no heap-allocated
    data passed between threads. Then thread-unaware GCs are good enough.

    - 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 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Paul Rubin on Mon Feb 10 01:30:01 2025
    On Sun, 9 Feb 2025 22:30:28 +0000, Paul Rubin wrote:

    minforth@gmx.net (minforth) writes:
    FWIW a single quotation-based counter in another Forth:

    MinForth 3.6 (32 bit)
    # defer ctr ok
    # : init { n } [: n 1+ dup to n ;] ; ok
    # 4 init is ctr ok
    # ctr . 5 ok

    Questions:

    1) where does the storage cell for n live, after init has returned?

    2) what if you make more than one counter?

    3) why did you use defer instead of something like CONSTANT?

    thanks

    1) Briefly, details omitted: a copy of the locals stack frame of the
    outer function is inlined. Before execution of the quotation, this
    copy is inserted into the quotation's locals stack frame.

    2) Won't work because there is only one copy per quotation

    3) Convenience

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to Anton Ertl on Mon Feb 10 06:14:52 2025
    On Sun, 9 Feb 2025 23:08:22 +0000, Anton Ertl wrote:

    Paul Rubin <no.email@nospam.invalid> writes:
    ..
    This is an attempt to make a counting function, like in Scheme:

    (define (x)
    ((lambda (n)
    (lambda ()
    (set! n (+ 1 n))
    n)) 0))

    (define a (x))

    (a) ; 1
    (a) ; 2, etc.

    : x ( -- xt )
    here 0 , [{: addr :}d addr @ 1+ dup addr ! ;] ;

    x alias a
    x alias b
    a . \ 1
    a . \ 2
    b . \ 1
    a . \ 3

    Hi,
    But I can do it like this:
    : ctr: create 0 , does> dup @ 1+ dup rot ! ; ok
    ctr: a
    ctr: b

    a . 1 ok
    b . 1 ok
    a . 2 ok
    b . 2 ok
    b . 3 ok

    So, what is the difference between the two definitions?

    - anton

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to ahmed on Mon Feb 10 07:19:05 2025
    melahi_ahmed@yahoo.fr (ahmed) writes:
    On Sun, 9 Feb 2025 23:08:22 +0000, Anton Ertl wrote:

    Paul Rubin <no.email@nospam.invalid> writes:
    ..
    This is an attempt to make a counting function, like in Scheme:

    (define (x)
    ((lambda (n)
    (lambda ()
    (set! n (+ 1 n))
    n)) 0))

    (define a (x))
    ...
    : x ( -- xt )
    here 0 , [{: addr :}d addr @ 1+ dup addr ! ;] ;

    x alias a
    ...
    : ctr: create 0 , does> dup @ 1+ dup rot ! ; ok
    ctr: a
    ...
    So, what is the difference between the two definitions?

    One produces an xt, the other a named word; the latter is more
    convenient for the shown usage).

    But yes, for dictionary allocation Forth has had a way to associate
    data with a single action since very early on.

    If you want heap allocation (i.e., to be able to reclaim the memory
    when you no longer need the counter), you can do it with closures, but
    not with DOES>:

    : x ( -- addr xt )
    0 <{: w^ n :}H n ;> swap [{: n :}H n @ 1+ dup n ! ;] ;
    x
    dup execute . \ 1
    dup execute . \ 2
    x
    dup execute . \ 1
    free-closure free throw
    dup execute . \ 3
    free-closure free throw

    Instead of using the syntax for allocating the data above, one could
    also use heap allocation directly:

    : x ( -- addr xt )
    1 cells allocate throw 0 over ! dup [{: n :}H n @ 1+ dup n ! ;] ;

    Following the textbook spirit of Paul Rubin's example, you can have
    several closures working on the same data instance. E.g., let's
    separate the count-up and the read-out functions (back to dictionary allocation), and this time using pure-stack closures:

    : y ( -- xt-count xt-val )
    here 0 , dup [n:d 1 swap +! ;] swap [n:d @ ;] ;
    y
    dup execute . \ 0
    over execute
    dup execute . \ 1
    over execute
    over execute
    y
    dup execute . \ 0
    2swap
    dup execute . \ 3
    2drop 2drop

    You can also use DOES> for this effect, but it becomes longer and less efficient:

    : y-count ( addr "name" -- )
    create ,
    does> ( -- )
    @ 1 swap +! ;

    : y-val ( addr "name" -- )
    create ,
    does> ( -- u )
    @ @ ;

    : y ( "name1" "name2" -- )
    here 0 , dup y-count y-val ;

    y a-count a-val
    a-val . \ 0
    a-count
    a-val . \ 1
    a-count
    a-count
    y b-count b-val
    b-val . \ 0
    a-val . \ 3

    But, as mentioned below, the textbook examples of changing data in
    closures or DOES> words are rarely found in practice.

    About the <{: ... ;> syntax:

    The <{: ... ;> syntax becomes more useful if multiple data is located
    there; you could instead define a structure, allocate that, and access
    the fields, but defining a structure for a single usage is less
    convenient than the syntax above; OTOH, with the syntax above you have
    to pass all the addresses separately (and name them again in the
    closure), while you can just pass the address of the structure to the
    closure and then address the fields. So maybe the <{: ... ;> syntax is
    never really useful.

    The main reason why it is not used is not because alternative ways of
    achieving the same thing are preferred, but because we usually don't
    have uses of closures where the data changes. Not for closures, and
    not for DOES>. The common case is that we have some value that we
    want to associate with the code, and we do it at closure creation, and
    then do not change it. I.e., something like Paul Rubin's textbook
    example is a rare case in practice. And if the data is not changed,
    it can just be passed as value to the closures, no home location with
    an address necessary (and therefore no <{: ... ;>).

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023 proceedings: http://www.euroforth.org/ef23/papers/
    EuroForth 2024 proceedings: http://www.euroforth.org/ef24/papers/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to All on Mon Feb 10 08:37:37 2025
    Another use case (which I use a lot): higher oder functions
    like the classic map/filter/reduce and domain-specific ones.
    I have no idea how that could be done with CREATE .. DOES>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From ahmed@21:1/5 to All on Mon Feb 10 08:34:41 2025
    Thanks!
    It is a little bit complicated to me (for now).
    I'll study it and figure out how to use it.

    Thanks again.

    Ahmed

    --

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From mhx@21:1/5 to All on Mon Feb 10 09:20:00 2025
    If you want heap allocation (i.e., to be able to reclaim the memory
    when you no longer need the counter), you can do it with closures, but
    not with DOES>:

    You can just FORGET the word.

    -marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to albert@spenarnc.xs4all.nl on Mon Feb 3 12:57:50 2025
    albert@spenarnc.xs4all.nl writes:
    I maintain that if you are not in a recursive call for
    a function with locals, and you try to catch the same function
    call, everything is fine.

    The thing about exceptions is that they occur unexpectedly. Example:
    your recursive function prints something, and that works the first few
    times, but then the printer runs out of paper and there is an i/o
    exception. It's not the recursive function's job to handle this. The exception throws to some outer level handler that asks the user to fix
    the problem.

    Adding (LOCAL) to a Forth interpreter should normally not be too
    difficulot, if you control the interpreter implementation. It's the
    right way to do stuff like this. Why mess around with all that awful
    stack juggling for a half-working and woefully slow solution?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to ruvim.pinka@gmail.com on Mon Feb 3 13:19:15 2025
    In article <vnq10p$162l3$1@dont-email.me>,
    Ruvim <ruvim.pinka@gmail.com> wrote:
    On 2025-02-02 15:13, albert@spenarnc.xs4all.nl wrote:


    Recursion is not necessary. It is enough to use the same-name "local" >variables in different functions, some of which throw exceptions, and
    other catch exceptions.

    An artificial example:


    : local ( x2 addr1 -- ; R: nest-sys1 -- x1 addr1 nest-sys.xt nest-sys1 )
    \ This definition assumes that nest-sys size is 1 cell,
    \ and xt is a subtype of nest-sys
    r> ( x2 addr nest-sys1 )
    over dup @ >r >r [: 2r> ! ;] >r
    ( x2 addr1 nest-sys1 ) >r !
    ;


    : idiv ( n1 n2\0 -- n3 | n1 0 -- never )
    dup if / exit then -10 throw
    ;

    variable a
    variable b

    : foo ( n1 n2 -- )
    b local a local
    a @ b @ idiv
    ." idiv result is " . cr
    ;
    : bar ( u1 -- u1 )
    a local
    100 a @ ['] foo catch if 2drop then
    a @
    ;

    0 bar .
    \ this must print 0, but will print 10


    You share the same global storage for local's in different
    words.
    Try this with naming the local in bar `` c ''.

    I maintain that if you are not in a recursive call for
    a function with locals, and you try to catch the same function
    call, everything is fine.
    The challenge I posed remains, find a realistic scenarion that
    leads to problems.

    Groetjes Albrt

    --
    Ruvim
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to mhx on Mon Feb 10 14:45:12 2025
    In article <75f81945380192813776e3b0dd2ddec5@www.novabbs.com>,
    mhx <mhx@iae.nl> wrote:
    If you want heap allocation (i.e., to be able to reclaim the memory
    when you no longer need the counter), you can do it with closures, but
    not with DOES>:

    You can just FORGET the word.

    If you forego heap allocation, that is.
    If you combine heap allocation and FORGET/MARKERS and closures/DOES>
    it can be very tricky.


    -marcel
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From albert@spenarnc.xs4all.nl@21:1/5 to Anton Ertl on Mon Feb 10 14:40:05 2025
    In article <2025Feb10.081905@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    melahi_ahmed@yahoo.fr (ahmed) writes:
    On Sun, 9 Feb 2025 23:08:22 +0000, Anton Ertl wrote:

    Paul Rubin <no.email@nospam.invalid> writes:
    ..
    This is an attempt to make a counting function, like in Scheme:

    (define (x)
    ((lambda (n)
    (lambda ()
    (set! n (+ 1 n))
    n)) 0))

    (define a (x))
    ...
    : x ( -- xt )
    here 0 , [{: addr :}d addr @ 1+ dup addr ! ;] ;

    x alias a
    ...
    : ctr: create 0 , does> dup @ 1+ dup rot ! ; ok
    ctr: a
    ...
    So, what is the difference between the two definitions?

    One produces an xt, the other a named word; the latter is more
    convenient for the shown usage).

    But yes, for dictionary allocation Forth has had a way to associate
    data with a single action since very early on.

    Make that
    "
    Forth has had a way to associate
    data with a single action since very early on.
    "
    or more precise
    " Forth recipes combine code and data"

    Using underlying more primitive facilities in a Forth, one can define
    a recipe ("xt") that has no name, is not linked in a wordlist and sits
    in ALLOCATEd space and perform an action like x.
    (Admittedly, in a Forth that separates code and data it is more involved)

    This is a simple example to get a word `test floating in allocated space.
    (In ciforth example)
    ALLOC moves a word to the heap freeing the a freshly generated object
    that runs from ( addr --) to HERE. This doesn't switch DP, and assumes relocatable code.
    UNLINK-LATEST decouples the latest word from the dictionary.

    WANT >ALLOC UNLINK-LATEST
    INIT-ALLOC \ Use 1/4 of the dictionary space for heap.

    : test "hello" TYPE ;
    ' test UNLINK-LATEST \ decouple
    ALLOC \ move to heap
    WORDS \ to show that test has vanished
    .S \ The xt
    EXECUTE
    hello OK

    In the above example reclaiming memory is not harder than with
    any dynamically allocated memory.

    [This doesn't show how the data is coupled, but conveys hopefully
    a part of the idea.]

    <SNIP>

    But, as mentioned below, the textbook examples of changing data in
    closures or DOES> words are rarely found in practice.

    I totally agree on this. In 400+ euler problems I was never inclined
    to use this technique (if this counts for something ..)

    <SNIP>

    - anton
    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to mhx on Mon Feb 10 09:21:46 2025
    mhx@iae.nl (mhx) writes:
    You can just FORGET the word.

    That also forgets any words that were defined later, I thought.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to Anton Ertl on Mon Feb 10 11:21:59 2025
    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    : x ( -- xt )
    here 0 , [{: addr :}d addr @ 1+ dup addr ! ;] ;

    Nice ;).

    : x ( -- xt )
    0 <{: w^ n :}d n ;> drop [{: n :}d n @ 1+ dup n ! ;] ;

    Why do you use <{: ... :}> instead of something like [{: ... :} ... ;]w ?

    another suffix could be added to put closure locals in the gc'd heap.
    Yes. There is :}xt for passing an xt that performs the allocation.
    See <https://gforth.org/manual/Closures.html> for all of these topics.

    Ah nice, I did see the mention of :}xt in the docs, but didn't connect
    that with garbage collection. It seems worth a mention. I saw the
    paper when it came out but should look at it again. I would say having
    to pass the allocator to the xt is a bit messy though there is probably
    a simple way to wrap it automatically.

    Also in a threaded program I guess it would have to stop any threads
    that shared a GC'd heap during collection of that heap.

    That's a tough one. My current thinking is along the lines of a
    per-thread allocator and garbage-collector, with no heap-allocated
    data passed between threads. Then thread-unaware GCs are good enough.

    Ergh, that adds a new layer of discipline required of the program,
    avoiding passing gc'd data between threads. For example, the :}h1
    allocator could leak memory you pass the xt to another thread and the
    receiver never calls it. So you might prefer to use the gc allocator,
    but then the message passing scheme breaks the heap-per-thread
    invariant.

    Erlang handles this by serializing and copying the closure from the
    sender's heap to the receiver's during message passing. Maybe gforth
    could get a library function that does something similar. Python and
    GHC don't attempt separate heaps per thread, and it's common in them to
    pass shared heap data between threads using message queues. The main convention you have to follow in Python is that any mutating objects
    must be owned by a single thread and not accessed by any others.
    Attempting to e.g. protect them with locks turns into a big mess.

    OTOH, if Gforth is going to use a copying approach, maybe it should go
    the full Erlang route and use multiple processes instead of Posix threads.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Rubin@21:1/5 to minforth on Tue Feb 11 14:17:31 2025
    minforth@gmx.net (minforth) writes:
    Unfortunately, everything is very non-standard, as there is
    no multitasking wordset (for historical reasons?).
    A pity really, as co-operative multitasking existed very
    early on in Forth.

    I had thought there wasn't enough agreement about the wordset to
    standardize it, and the same thing happened for cross development. But
    at least for multitasking, there is reasonable shared understanding
    about how it should work, at least in the cooperative case.

    Then it would be easier to have better discussions about coroutines
    (the original topic of this thread) or ownership of closure objects
    and different variants of GC.

    I think these fancy closures are mostly of interest to language geeks
    and not so much in the old-fashioned Forth spirit. Coroutines don't
    seem that important if you have multitasking. Anton's GC is great and I
    think cooperative multitasking wouldn't affect it much, if you don't
    mind the collection pauses. It would simply block during collection.

    The GC hazard we're discussing is when there are real preemptive threads
    and maybe multiple cores. This may also be reaching outside of Forth's traditional areas of effectiveness. Software transactional memory (STM)
    is another topic that should probably come up again. We haven't heard
    from Andrew Haley in a while though.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Paul Rubin on Wed Feb 12 22:30:18 2025
    On Tue, 11 Feb 2025 22:17:31 +0000, Paul Rubin wrote:

    minforth@gmx.net (minforth) writes:
    Unfortunately, everything is very non-standard, as there is
    no multitasking wordset (for historical reasons?).
    A pity really, as co-operative multitasking existed very
    early on in Forth.

    I had thought there wasn't enough agreement about the wordset to
    standardize it

    Likely dead or too little support: https://forth-standard.org/proposals/multi-tasking-proposal

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From sjack@21:1/5 to Ruvim on Sat Mar 22 15:51:18 2025
    Ruvim <ruvim.pinka@gmail.com> wrote:

    An artificial example:

    While wondering down roads less taken, I took a path where
    bacForth CUT: and -CUT were used in lieu of CATCH THROW .


    ( ENTER is the same as ';:' )
    : LOCAL ( x adr -- )
    r> -rot dup @ over 2>r ! ENTER 2r> ! ;

    \ prefixed '-' to indicate this word does a cut
    : -idiv ( n1 n2\0 -- n3 | n1 0 -- true )
    dup if /
    else 2drop true -CUT
    then
    ;

    : ?idiv ( n1 n2 -- n1/n2 false | <nothing> )
    CUT: -idiv ." Valid: " false
    ;

    42 variable a
    42 variable b

    : ?/ ( u1 u2 -- u1/u2 )
    a local b local
    b @ a @ ?idiv
    if ." Bogus! " 666 then .
    ;

    i. 100 5 ?/ --> Valid: 20
    i. a ? b ? --> 42 42
    i. 100 0 ?/ --> Bogus! 666
    i. a ? b ? --> 42 42

    forget ?/

    : ?/ ( u1 u2 -- u1/u2 )
    a local b local
    b @ a @ ' ?idiv ENTER
    if ." Bogus! " 666 then .
    ;

    i. 100 5 ?/ --> Valid: 20
    i. a ? b ? --> 42 42
    i. 100 0 ?/ --> Bogus! 666
    i. a ? b ? --> 42 42

    forget ?/

    : ?/ ( u1 u2 -- u1/u2 )
    a local b local
    b @ a @ (: cut: -idiv ." Valid: " false ;) ENTER
    if ." Bogus! " 666 then .
    ;

    i. 100 5 ?/ --> Valid: 20
    i. a ? b ? --> 42 42
    i. 100 0 ?/ --> Bogus! 666
    i. a ? b ? --> 42 42

    +echo
    -fin-

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From sjack@21:1/5 to dxf on Sun Mar 23 16:47:09 2025
    dxf <dxforth@gmail.com> wrote:

    Does anyone have a reference/details for this anywhere as I can't seem to find anything?

    Sufficient BACK TRACKING code can be found in the following document;
    I don't have a link and I won't post the code because of copyright
    mark. The discussion is in Russian but the code summary is (simple) Forth.
    The paper is about abstract iterator:
    AMONG <??????> EACH <????????> ITERATE
    but BACK TRACKING is part of the implementation and its code is shown in summary.
    The Cyrillic here is KOI8-R


    ?????????? ???????????? ???????? ? ??????? (???????????)
    ?.?.?????????

    ?????-????????????? ???????? ??????????? ? ????????????? ???
    199178, ?????-?????????, 14-? ????? ?.?., ?.39
    ?-mail: mlg@iias.spb.su

    ... Skip abstract in Russian ...

    Enhancing the capabilities of backtracking
    M.L.Gassanenko

    Abstract:
    The technique of backtracking enables one to create abstract iterator
    modules, which are very convenient, but there's a severe restriction on
    the use of backtracking in the body of loops controlled by these
    iterators. This paper introduces a loop-like control structure that
    eliminates these restrictions. A backtrackable procedure is used in it
    as an iterator that controls repeating execution of a backtrackable
    body; control is transferred to the iterator when the loop body succeeds
    (and not when it fails). Compatibility with cut statements and
    employment in rapid prototyping are discussed. A Forth implementation is presented.

    --
    me

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From sjack@21:1/5 to Hans Bezemer on Sun Mar 23 23:34:55 2025
    Hans Bezemer <the.beez.speaks@gmail.com> wrote:

    But I'm gonna spoil it for you: BacForth has an extra "L" stack. CUT:
    and -CUT use it. You might wanna use this one if you REALLY want it: https://sourceforge.net/p/forth-4th/code/HEAD/tree/trunk/4th.src/lib/stack.4th

    Yes, the L-stack is just a stack and what I use; however, in later evolution the L-stack became a virtual stack, a variable used in conjunction with
    the return stack. I never implemented it, the simple stack was good
    enough for me.

    --
    me

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