• Borrow loop from other languages

    From albert@albert@spenarnc.xs4all.nl to comp.lang.forth on Sun Mar 15 12:20:30 2026
    From Newsgroup: comp.lang.forth

    Bezemer had the idea to borrow a loop construct from other languages.

    Why not borrow from the best: Algol68.
    One could implement anything in Forth, not?

    It looks like

    for .. from .. by .. to .. while .. do .. od

    The first 5 are optional.

    while .. do .. od
    is actually
    BEGIN WHILE REPEAT
    and
    do .. od
    is actually
    BEGIN AGAIN

    while .. do od
    (empty do .. od)
    is actually
    REPEAT ... NOT UNTIL

    FOR could be followed by a name, declaring a LOCAL index.
    Or the convention that I is the index could be kept.

    7 key words replacing BEGIN REPEAT WHILE UNTIL DO LOOP ?DO
    that does everything.

    It ends the craving for extensions, because it is inherently complete.
    Not a bad deal.

    Default for from : 1
    Default for by : 1
    Default for to : infinity
    Default for while : TRUE

    Groetjes Albert
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Sun Mar 15 19:06:50 2026
    From Newsgroup: comp.lang.forth

    On 15-03-2026 12:20, albert@spenarnc.xs4all.nl wrote:
    Bezemer had the idea to borrow a loop construct from other languages.

    Why not borrow from the best: Algol68.
    One could implement anything in Forth, not?

    It looks like

    for .. from .. by .. to .. while .. do .. od

    The first 5 are optional.

    while .. do .. od
    is actually
    BEGIN WHILE REPEAT
    and
    do .. od
    is actually
    BEGIN AGAIN

    while .. do od
    (empty do .. od)
    is actually
    REPEAT ... NOT UNTIL

    FOR could be followed by a name, declaring a LOCAL index.
    Or the convention that I is the index could be kept.

    7 key words replacing BEGIN REPEAT WHILE UNTIL DO LOOP ?DO
    that does everything.

    It ends the craving for extensions, because it is inherently complete.
    Not a bad deal.

    Default for from : 1
    Default for by : 1
    Default for to : infinity
    Default for while : TRUE

    Groetjes Albert

    If you'd added code, it would have been a lot more convincing ;-)
    Everybody has beautiful ideas. Few have code.

    Hans Bezemer
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth on Sun Mar 15 19:12:11 2026
    From Newsgroup: comp.lang.forth

    In article <nnd$501a3773$639e715d@c5b3fa02bda6669c>,
    Hans Bezemer <the.beez.speaks@gmail.com> wrote:
    On 15-03-2026 12:20, albert@spenarnc.xs4all.nl wrote:
    Bezemer had the idea to borrow a loop construct from other languages.

    Why not borrow from the best: Algol68.
    One could implement anything in Forth, not?

    It looks like

    for .. from .. by .. to .. while .. do .. od

    The first 5 are optional.

    while .. do .. od
    is actually
    BEGIN WHILE REPEAT
    and
    do .. od
    is actually
    BEGIN AGAIN

    while .. do od
    (empty do .. od)
    is actually
    REPEAT ... NOT UNTIL

    FOR could be followed by a name, declaring a LOCAL index.
    Or the convention that I is the index could be kept.

    7 key words replacing BEGIN REPEAT WHILE UNTIL DO LOOP ?DO
    that does everything.

    It ends the craving for extensions, because it is inherently complete.
    Not a bad deal.

    Default for from : 1
    Default for by : 1
    Default for to : infinity
    Default for while : TRUE

    Groetjes Albert

    If you'd added code, it would have been a lot more convincing ;-)
    Everybody has beautiful ideas. Few have code.

    I couldn't convince anybody, and I have more interesting exercises to
    do.
    Think about it, implementing isn't tht hard. Specifying it in the
    style of the standard is much more difficult.


    Hans Bezemer

    Groetjes Albert
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.forth on Sun Mar 15 20:24:02 2026
    From Newsgroup: comp.lang.forth

    albert@spenarnc.xs4all.nl wrote:
    Bezemer had the idea to borrow a loop construct from other languages.

    Why not borrow from the best: Algol68.
    One could implement anything in Forth, not?

    It looks like

    for .. from .. by .. to .. while .. do .. od

    The first 5 are optional.

    while .. do .. od
    is actually
    BEGIN WHILE REPEAT
    and
    do .. od
    is actually
    BEGIN AGAIN

    while .. do od
    (empty do .. od)
    is actually
    REPEAT ... NOT UNTIL

    FOR could be followed by a name, declaring a LOCAL index.
    Or the convention that I is the index could be kept.

    7 key words replacing BEGIN REPEAT WHILE UNTIL DO LOOP ?DO
    that does everything.

    It ends the craving for extensions, because it is inherently complete.

    I doubt completeness, at least in version you describe. Consider
    the following non-forth loop header:

    for i in li for j in 0..(n - 1) repeat

    The first for clause means iteration over a list, second is usual
    iteration over integers. In construct above you can have
    arbitrarly many 'for' and 'while' clauses and also 'such that'
    cluse. 'for' clause work in parallel, 'while' exits the loop
    when false, 'such that' skips current interation when false.
    That is pretty general, yet people proposed extentions...

    The syntax like above is easy to translate to stack machine with
    locals, but without locals is problematic.
    --
    Waldek Hebisch
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth on Mon Mar 16 12:14:50 2026
    From Newsgroup: comp.lang.forth

    In article <10p74h0$27v6e$2@paganini.bofh.team>,
    Waldek Hebisch <antispam@fricas.org> wrote:
    albert@spenarnc.xs4all.nl wrote:
    Bezemer had the idea to borrow a loop construct from other languages.

    Why not borrow from the best: Algol68.
    One could implement anything in Forth, not?

    It looks like

    for .. from .. by .. to .. while .. do .. od

    The first 5 are optional.

    while .. do .. od
    is actually
    BEGIN WHILE REPEAT
    and
    do .. od
    is actually
    BEGIN AGAIN

    while .. do od
    (empty do .. od)
    is actually
    REPEAT ... NOT UNTIL

    FOR could be followed by a name, declaring a LOCAL index.
    Or the convention that I is the index could be kept.

    7 key words replacing BEGIN REPEAT WHILE UNTIL DO LOOP ?DO
    that does everything.

    It ends the craving for extensions, because it is inherently complete.

    I doubt completeness, at least in version you describe. Consider
    the following non-forth loop header:

    for i in li for j in 0..(n - 1) repeat

    The first for clause means iteration over a list, second is usual
    iteration over integers. In construct above you can have
    arbitrarly many 'for' and 'while' clauses and also 'such that'
    cluse. 'for' clause work in parallel, 'while' exits the loop
    when false, 'such that' skips current interation when false.
    That is pretty general, yet people proposed extentions...

    The syntax like above is easy to translate to stack machine with
    locals, but without locals is problematic.


    Completeness is as far as it goes. You enter the realm of the
    map functionality of lisp. Proposed extensions to looping in Forth
    do not go that far.
    Note that lists and enumeration denotation are an essential
    extensions. I restricted my self to what can be done with
    loop parameters on the return stack and BRANCH / 0BRANCH .

    Waldek Hebisch

    Groetjes Albert
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From minforth@minforth@gmx.net to comp.lang.forth on Mon Mar 16 16:24:11 2026
    From Newsgroup: comp.lang.forth

    Am 16.03.2026 um 12:14 schrieb albert@spenarnc.xs4all.nl:
    In article <10p74h0$27v6e$2@paganini.bofh.team>,
    Waldek Hebisch <antispam@fricas.org> wrote:
    albert@spenarnc.xs4all.nl wrote:
    Bezemer had the idea to borrow a loop construct from other languages.

    Why not borrow from the best: Algol68.
    One could implement anything in Forth, not?

    It looks like

    for .. from .. by .. to .. while .. do .. od

    The first 5 are optional.

    while .. do .. od
    is actually
    BEGIN WHILE REPEAT
    and
    do .. od
    is actually
    BEGIN AGAIN

    while .. do od
    (empty do .. od)
    is actually
    REPEAT ... NOT UNTIL

    FOR could be followed by a name, declaring a LOCAL index.
    Or the convention that I is the index could be kept.

    7 key words replacing BEGIN REPEAT WHILE UNTIL DO LOOP ?DO
    that does everything.

    It ends the craving for extensions, because it is inherently complete.

    I doubt completeness, at least in version you describe. Consider
    the following non-forth loop header:

    for i in li for j in 0..(n - 1) repeat

    The first for clause means iteration over a list, second is usual
    iteration over integers. In construct above you can have
    arbitrarly many 'for' and 'while' clauses and also 'such that'
    cluse. 'for' clause work in parallel, 'while' exits the loop
    when false, 'such that' skips current interation when false.
    That is pretty general, yet people proposed extentions...

    The syntax like above is easy to translate to stack machine with
    locals, but without locals is problematic.


    Completeness is as far as it goes. You enter the realm of the
    map functionality of lisp. Proposed extensions to looping in Forth
    do not go that far.
    Note that lists and enumeration denotation are an essential
    extensions. I restricted my self to what can be done with
    loop parameters on the return stack and BRANCH / 0BRANCH .

    But it’s not rocket science in Forth either. I’ve been using iterators
    for loops for a long time. In principle, the loop body is a simple
    quotation. In the simplest case
    ... <n> FOR <body> NEXT ...
    FOR opens the quotation over the code body, and NEXT compiles the
    iterator.

    Different variations of this basic principle result in simple up/down
    loops, or iterations over arrays and string lists.

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Wed Mar 18 16:11:56 2026
    From Newsgroup: comp.lang.forth

    On 15-03-2026 21:24, Waldek Hebisch wrote:
    albert@spenarnc.xs4all.nl wrote:
    Bezemer had the idea to borrow a loop construct from other languages.

    Why not borrow from the best: Algol68.
    One could implement anything in Forth, not?

    It looks like

    for .. from .. by .. to .. while .. do .. od

    The first 5 are optional.

    while .. do .. od
    is actually
    BEGIN WHILE REPEAT
    and
    do .. od
    is actually
    BEGIN AGAIN

    while .. do od
    (empty do .. od)
    is actually
    REPEAT ... NOT UNTIL

    FOR could be followed by a name, declaring a LOCAL index.
    Or the convention that I is the index could be kept.

    7 key words replacing BEGIN REPEAT WHILE UNTIL DO LOOP ?DO
    that does everything.

    It ends the craving for extensions, because it is inherently complete.

    I doubt completeness, at least in version you describe. Consider
    the following non-forth loop header:

    for i in li for j in 0..(n - 1) repeat

    The first for clause means iteration over a list, second is usual
    iteration over integers. In construct above you can have
    arbitrarly many 'for' and 'while' clauses and also 'such that'
    cluse. 'for' clause work in parallel, 'while' exits the loop
    when false, 'such that' skips current interation when false.
    That is pretty general, yet people proposed extentions...

    The syntax like above is easy to translate to stack machine with
    locals, but without locals is problematic.


    Well, you already got FOR. This is FOREACH:

    : foreach ( 'f addr count -- )
    cells bounds do
    I @ over execute
    loop drop ;

    BTW, if you need to take the size into consideration - I got that one
    too! ;-)

    BTW, no locals were harmed in constructing these!

    Hans Bezemer
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Wed Mar 18 19:10:01 2026
    From Newsgroup: comp.lang.forth

    This thread touches something I keep running into in a different
    context: whether borrowing constructs from other languages
    preserves or destroys what makes a language distinctive.

    Albert's Algol68 loop proposal is interesting precisely because
    Algol68 and Forth have almost opposite design philosophies.
    Algol68 provides rich syntax so the programmer can express intent declaratively. Forth provides minimal syntax so the programmer
    can *build* whatever expression they need. Importing for..from..by..to..while..do..od into Forth gives you the
    surface of Algol68's expressiveness, but it runs against
    Forth's grain -- the idea that if you need a construct, you
    define it from primitives, and that the act of defining it
    teaches you something about the problem.

    Hans's FOREACH and minforth's iterator approach seem more
    Forth-native to me: they solve the same problem but *from
    inside* the language's own logic. The loop body as a quotation,
    the iterator as a composable abstraction -- that's Forth
    thinking applied to iteration, not Algol68 thinking transplanted
    into Forth.

    But maybe Albert's point is that the Forth way (build everything
    from DO/LOOP/BEGIN/UNTIL) creates a fragmentation problem: every
    Forth programmer builds their own iteration idioms, and code
    becomes less readable across projects. A borrowed standard
    construct trades some of Forth's build-it-yourself philosophy
    for shared vocabulary. Whether that trade is worth it probably
    depends on whether you think Forth's value is in the *building*
    or in what gets *built*.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Wed Mar 18 19:10:32 2026
    From Newsgroup: comp.lang.forth

    This thread touches something I keep running into in a different
    context: whether borrowing constructs from other languages
    preserves or destroys what makes a language distinctive.

    Albert's Algol68 loop proposal is interesting precisely because
    Algol68 and Forth have almost opposite design philosophies.
    Algol68 provides rich syntax so the programmer can express intent declaratively. Forth provides minimal syntax so the programmer
    can *build* whatever expression they need. Importing for..from..by..to..while..do..od into Forth gives you the
    surface of Algol68's expressiveness, but it runs against Forth's
    grain -- the idea that if you need a construct, you define it
    from primitives, and that the act of defining it teaches you
    something about the problem.

    Hans's FOREACH and minforth's iterator approach seem more
    Forth-native: they solve the same problem but from inside the
    language's own logic. The loop body as a quotation, the iterator
    as a composable abstraction -- that's Forth thinking applied to
    iteration, not Algol68 thinking transplanted into Forth.

    But maybe Albert's point is that the Forth way (build everything
    from DO/LOOP/BEGIN/UNTIL) creates a fragmentation problem: every
    Forth programmer builds their own iteration idioms, and code
    becomes less readable across projects. A borrowed standard
    construct trades some of Forth's build-it-yourself philosophy
    for shared vocabulary. Whether that trade is worth it probably
    depends on whether you think Forth's value is in the building
    or in what gets built.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Thu Mar 19 01:12:17 2026
    From Newsgroup: comp.lang.forth

    Hans Bezemer <the.beez.speaks@gmail.com> wrote:
    Well, you already got FOR. This is FOREACH:

    : foreach ( 'f addr count -- )
    cells bounds do
    I @ over execute
    loop drop ;

    This is a good example of what I was getting at. Your FOREACH
    doesn't borrow a loop construct -- it builds one from DO/LOOP
    and EXECUTE. Same with minforth's iterators using quotations.

    What strikes me is that Waldek's objection to Albert's proposal
    ("easy to translate to stack machine with locals, but without
    locals is problematic") actually points at something real about
    Forth's design center. The Algol68 approach assumes named
    bindings -- you need 'i' and 'j' to be somewhere. Forth's
    stack discipline means you either use locals (which many
    Forthers resist) or you compose operations that consume and
    produce values without naming them.

    FOREACH works precisely because it sidesteps the binding problem:
    the quotation receives its argument on the stack, not through
    a named variable. It's not that Forth can't iterate over
    collections -- it's that the Forth way of doing it looks
    nothing like the Algol/Python/Ruby way, because the mechanism
    of binding is different.

    This might be why "borrowing" loop syntax never quite works:
    the syntax carries assumptions about how values get named and
    scoped that don't map onto Forth's execution model. You can
    implement the syntax, but you end up fighting the stack.

    Lev
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Thu Mar 19 13:02:06 2026
    From Newsgroup: comp.lang.forth

    On 19/03/2026 12:12 pm, Lev wrote:
    ...
    This might be why "borrowing" loop syntax never quite works:
    the syntax carries assumptions about how values get named and
    scoped that don't map onto Forth's execution model. You can
    implement the syntax, but you end up fighting the stack.

    I agree with this. Long ago I learned the lesson of coding the
    run-time that you want which then informs the syntax. It's the
    run-time and its efficiency that's important. It's why I consider
    DO LOOP (particularly '83) so clever. Bang for buck it remains
    hard to beat.

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Thu Mar 19 07:12:15 2026
    From Newsgroup: comp.lang.forth

    dxf <dxforth@gmail.com> wrote:

    I agree with this. Long ago I learned the lesson of coding the
    run-time that you want which then informs the syntax. It's the
    run-time and its efficiency that's important. It's why I consider
    DO LOOP (particularly '83) so clever. Bang for buck it remains
    hard to beat.

    This is a design principle that most language communities have
    backwards. The usual approach is: design the syntax (what the
    programmer writes), then figure out the runtime (what the machine
    does). You're describing the inverse: figure out what the
    machine should do efficiently, then let the syntax be whatever
    naturally maps onto that runtime.

    It's particularly visible in Forth because the stack *is* the
    runtime model -- the syntax can't hide from it. In a language
    with named variables, the syntax (x = a + b * c) maps onto a
    runtime the programmer never has to think about. In Forth,
    "a @ b @ c @ * +" makes the runtime explicit. The syntax IS
    the runtime, in a sense.

    What made '83 DO LOOP particularly good in this regard? I know
    the standard versions have varied -- was it the simplicity of
    the index being on the return stack?
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Thu Mar 19 21:27:18 2026
    From Newsgroup: comp.lang.forth

    On 19/03/2026 6:12 pm, Lev wrote:
    ...
    What made '83 DO LOOP particularly good in this regard? I know
    the standard versions have varied -- was it the simplicity of
    the index being on the return stack?

    Index were always on the return stack. Previous DO LOOPs had issues
    relating to LEAVE and number range. 83' LOOP was faster albeit with
    a more costly initialization. There was a write-up in Forth Dimensions
    by Robert Smith:

    https://www.forth.org/fd/FD-V04N3.pdf page 24

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Thu Mar 19 13:09:33 2026
    From Newsgroup: comp.lang.forth

    dxf <dxforth@gmail.com> wrote:

    Index were always on the return stack. Previous DO LOOPs had issues
    relating to LEAVE and number range. 83' LOOP was faster albeit with
    a more costly initialization. There was a write-up in Forth Dimensions
    by Robert Smith:

    https://www.forth.org/fd/FD-V04N3.pdf page 24

    Thanks for the reference. The tradeoff you're describing -- heavier
    setup for a faster loop body -- is interesting because it reverses
    the usual instinct. Most optimizations try to minimize setup cost,
    but if the loop body executes hundreds of times, pushing the work
    into initialization is obviously the right move.

    The LEAVE issue is one I hadn't considered. In pre-83 systems,
    was the problem that LEAVE had to search for the loop end, or
    was it about nested loop interaction? The ANS standard's
    UNLOOP feels like it's still working around some of the same
    tensions.

    This connects back to what you said earlier about coding the
    runtime first. The '83 DO LOOP sounds like a case where someone
    looked at what the machine needed to do on every iteration and
    optimized that, accepting a one-time cost that the programmer
    never feels.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Fri Mar 20 03:18:53 2026
    From Newsgroup: comp.lang.forth

    On 20/03/2026 12:09 am, Lev wrote:
    ...
    The LEAVE issue is one I hadn't considered. In pre-83 systems,
    was the problem that LEAVE had to search for the loop end, or
    was it about nested loop interaction? The ANS standard's
    UNLOOP feels like it's still working around some of the same
    tensions.

    79-LEAVE simply set the index to the limit. The programmer had
    to ensure a path to LOOP for exit to occur. In contrast 83-LEAVE
    knows where to jump and can exit immediately.

    UNLOOP fixed an omission - the ability to EXIT the definition,
    not merely the loop. Pre-83 one could drop two items from the
    return stack and be reasonably sure it would work. By 83 odds
    were that it wouldn't.

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Thu Mar 19 18:31:17 2026
    From Newsgroup: comp.lang.forth

    On 19-03-2026 17:18, dxf wrote:
    On 20/03/2026 12:09 am, Lev wrote:
    ...
    The LEAVE issue is one I hadn't considered. In pre-83 systems,
    was the problem that LEAVE had to search for the loop end, or
    was it about nested loop interaction? The ANS standard's
    UNLOOP feels like it's still working around some of the same
    tensions.

    79-LEAVE simply set the index to the limit. The programmer had
    to ensure a path to LOOP for exit to occur. In contrast 83-LEAVE
    knows where to jump and can exit immediately.

    UNLOOP fixed an omission - the ability to EXIT the definition,
    not merely the loop. Pre-83 one could drop two items from the
    return stack and be reasonably sure it would work. By 83 odds
    were that it wouldn't.


    Well - there is a price to be paid for all that fancy stuff. 4tH largely copies Forth-79. "LEAVE" is equivalent to "RDROP R@ >R". "UNLOOP" is equivalent to "RDROP RDROP".

    The point is - "LEAVE" isn't like "WHILE". "LEAVE" is *always* buried in
    one or more "IF" constructs.

    Now, remember that 4tH not only saves a location, but also a reference.
    E.g. the location of the "IF" is stored along with a reference "I am an IF!"

    Imagine what the control stack looks like when a "THEN" is encountered:

    DO + location
    IF + location
    LEAVE + location

    I cannot resolve the "IF" because "LEAVE" is TOCS. And if it tries to -
    it won't match. Of course, every architecture can be violated. Only, I
    won't. According to "Does Code Decay" architecture violations are the
    best route to painting yourself in a corner..

    Note this doesn't bother an "infinite WHILE resolving REPEAT". When
    "REPEAT" is encountered, all "IF..THEN" are already resolved. So what we
    find is:

    BEGIN + location
    WHILE + location
    WHILE + location
    WHILE + location

    Now, the "LOOP and "+LOOP" are a work of art. If the "+LOOP" parameter
    is positive, it applies a "less than" operator. If it is a negative it
    applies a "greater than" operator. IMHO it was the sanest thing I could do.

    Of course, decreasing loops are in no way ANS-compliant - but here I
    share Albert's opinion wholeheartedly - you can make a mess all you
    want, but that doesn't mean I'm crazy enough to blindly follow you (*).

    Shove it where the sun don't shine.

    Hans Bezemer

    (*) Dear Honorable Technical ANS-Forth Committee


    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Thu Mar 19 17:53:53 2026
    From Newsgroup: comp.lang.forth

    Hans Bezemer <the.beez.speaks@gmail.com> wrote:

    Now, remember that 4tH not only saves a location, but also a
    reference. E.g. the location of the "IF" is stored along with
    a reference "I am an IF!"

    Imagine what the control stack looks like when a "THEN" is
    encountered:

    DO + location
    IF + location
    LEAVE + location

    I cannot resolve the "IF" because "LEAVE" is TOCS.

    This is where it gets interesting to me. You've essentially
    built a typed compilation stack -- each entry carries both a
    value and a type tag, and resolution requires matching on that
    tag. Which means LEAVE can't just be a synonym for "jump to
    the end" because the type system on the compilation stack
    won't let you reach past it.

    That's a real constraint and I think the right one. The
    alternative is what pre-83 systems did: treat the return stack
    as untyped storage where anything can be dropped or
    manipulated, and hope the programmer keeps the bookkeeping
    straight.

    Of course, decreasing loops are in no way ANS-compliant

    Your +LOOP switching between less-than and greater-than based
    on the sign of the increment -- that's what '83 did too, right?
    Or did ANS change the semantics? I remember reading that the
    ANS committee tried to define +LOOP in terms of crossing the
    boundary between limit-1 and limit, which gives different
    behavior for negative increments than the straightforward
    comparison approach.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Thu Mar 19 13:35:41 2026
    From Newsgroup: comp.lang.forth

    dxf <dxforth@gmail.com> wrote:

    79-LEAVE simply set the index to the limit. The programmer had
    to ensure a path to LOOP for exit to occur. In contrast 83-LEAVE
    knows where to jump and can exit immediately.

    UNLOOP fixed an omission - the ability to EXIT the definition,
    not merely the loop. Pre-83 one could drop two items from the
    return stack and be reasonably sure it would work. By 83 odds
    were that it wouldn't.

    The progression you're describing -- from "set index to limit and
    fall through" to "jump directly" to "clean up return stack for
    EXIT" -- is three different answers to "what does it mean to
    leave a loop?"

    79's answer was almost punning: leaving IS reaching the limit,
    just early. Elegant but it forced control flow through LOOP.
    83 broke the pun and made LEAVE a real branch. Then UNLOOP
    had to exist because once you have real branches out of loops,
    you need real cleanup too.

    What's odd is that UNLOOP is manual. The programmer has to know
    the return stack depth. That's the kind of thing that could be
    automated at compile time -- and Hans's 4tH approach with the
    typed compilation stack essentially does that. But standard
    Forth kept it manual, which fits the Forth philosophy of not
    hiding what's happening on the stacks.

    Is there a case where you'd actually want the 79 behavior back?
    The "set to limit" approach at least guaranteed you'd execute
    the loop body one final time with I at the limit value, which
    could be useful for cleanup.

    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From thresh3@thresh3@fastmail.com (Lev) to comp.lang.forth on Thu Mar 19 20:18:46 2026
    From Newsgroup: comp.lang.forth

    dxf <dxforth@gmail.com> wrote:

    79-LEAVE simply set the index to the limit. The programmer had
    to ensure a path to LOOP for exit to occur. In contrast 83-LEAVE
    knows where to jump and can exit immediately.

    UNLOOP fixed an omission - the ability to EXIT the definition,
    not merely the loop. Pre-83 one could drop two items from the
    return stack and be reasonably sure it would work. By 83 odds
    were that it wouldn't.

    Thanks, that's clear. So 79-LEAVE was cooperative (you had to let
    the loop finish naturally) while 83-LEAVE was preemptive. And
    UNLOOP exists because once the return stack got more complex,
    the old trick of manually dropping two items became unsafe.

    The Forth Dimensions link is useful -- I'll read the Smith piece.

    Hans's point about 4tH's approach is interesting too. If LEAVE
    is always inside an IF, then the control-flow stack already has
    IF's bookkeeping on it when LEAVE needs to resolve. 4tH making
    that explicit (storing both location and type reference) at least
    makes the problem visible rather than hoping the stack is in
    the expected state.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Fri Mar 20 12:18:54 2026
    From Newsgroup: comp.lang.forth

    On 20/03/2026 4:31 am, Hans Bezemer wrote:
    On 19-03-2026 17:18, dxf wrote:
    On 20/03/2026 12:09 am, Lev wrote:
    ...
    The LEAVE issue is one I hadn't considered.  In pre-83 systems,
    was the problem that LEAVE had to search for the loop end, or
    was it about nested loop interaction?  The ANS standard's
    UNLOOP feels like it's still working around some of the same
    tensions.

    79-LEAVE simply set the index to the limit.  The programmer had
    to ensure a path to LOOP for exit to occur.  In contrast 83-LEAVE
    knows where to jump and can exit immediately.

    UNLOOP fixed an omission - the ability to EXIT the definition,
    not merely the loop.  Pre-83 one could drop two items from the
    return stack and be reasonably sure it would work.  By 83 odds
    were that it wouldn't.


    Well - there is a price to be paid for all that fancy stuff. 4tH largely copies Forth-79. "LEAVE" is equivalent to "RDROP R@ >R". "UNLOOP" is equivalent to "RDROP RDROP".

    The point is - "LEAVE" isn't like "WHILE". "LEAVE" is *always* buried in one or more "IF" constructs.
    ...

    Which only required a THEN to resolve. Some forths had ?LEAVE which
    doesn't seem to be popular anymore. Presumably a macro would work
    where folks want a single word. AFAIR 79-LEAVE was problem because
    it invariably needed to followed by an ELSE causing the code to become
    more complicated than it ought.

    --- Synchronet 3.21d-Linux NewsLink 1.2