• Re: goto considered helpful

    From Kaz Kylheku@21:1/5 to Keith Thompson on Thu Dec 12 22:33:46 2024
    On 2024-12-12, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 11 Dec 2024 17:27:53 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    bart <bc@freeuk.com> writes:
    [...]
    My experience of multi-level break is that there are two main
    use-cases:

    * Used in the current loop only (not necessarily the innermost to
    an observer). This is the most common

    * Used to exit the outermost loop

    So to support these, named or even numbered loops are not
    necessary. (Eg. I use 'exit' or 'exit all'.)

    I would oppose a change to C that only applied to innermost and
    outermost loops. For one thing, I'm not aware of any other language
    that does this (except perhaps your unnamed one). For another,
    it's easy enough to define a feature that handles any arbitrary
    nesting levels, by applying names (labels) to loops.

    The better solution is education.
    Convince teachers in unis and colleges that goto is *not* considered
    harmful for this particular use case. Convince them to teach that
    attempts to avoid goto [for this particular use case] are really
    considered harmful. If you don't believe in authority of yourself then
    ask for help from somebody famous that share this view. I would guess
    that nearly all famous C programmers share it.

    Backward gotos tend to be dangerous. Forward gotos are less so.

    Backwards gotos are still fine if they are used to create a program
    graph that is also possible with structured control constructs.

    Compilers can still do loop analysis on the intermediate code and such.

    Backwards gotos can make control flow graphs that are in a different class,
    not expressible with conventional control constructs, like:

    if (condition1) {
    // ...
    label1:
    statement1;
    }

    if (condition2) {
    goto label1;
    }

    There are reasons for a coding convention around goto to disallow
    this, even if it allows backwards goto.

    Note how it is possible to execute "goto label1" without having ever
    executed the destination statement "statement1" before.

    In other words, the label1 node does not /dominate/ the goto.

    A coding rule against this might therefore be worded something like:
    "only true backwards gotos are allowed: every backwards goto must go to a node in the program which dominates the goto statement".

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Fri Dec 13 09:50:01 2024
    On 12/12/2024 22:50, Keith Thompson wrote:
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 11 Dec 2024 17:27:53 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    bart <bc@freeuk.com> writes:
    [...]
    My experience of multi-level break is that there are two main
    use-cases:

    * Used in the current loop only (not necessarily the innermost to
    an observer). This is the most common

    * Used to exit the outermost loop

    So to support these, named or even numbered loops are not
    necessary. (Eg. I use 'exit' or 'exit all'.)

    I would oppose a change to C that only applied to innermost and
    outermost loops. For one thing, I'm not aware of any other language
    that does this (except perhaps your unnamed one). For another,
    it's easy enough to define a feature that handles any arbitrary
    nesting levels, by applying names (labels) to loops.

    The better solution is education.
    Convince teachers in unis and colleges that goto is *not* considered
    harmful for this particular use case. Convince them to teach that
    attempts to avoid goto [for this particular use case] are really
    considered harmful. If you don't believe in authority of yourself then
    ask for help from somebody famous that share this view. I would guess
    that nearly all famous C programmers share it.

    Backward gotos tend to be dangerous. Forward gotos are less so.

    Dijkstras original "Go To Statement Considered Harmful" letter was
    written in 1968, at a time when many languages didn't necessarily
    have the structured control constructs we've come to expect since
    then. Using goto to implement a loop is almost always a bad idea,
    but it's something that a modern C programmer probably wouldn't
    even consider doing.

    I agree that gotos have valid uses.

    Here's an answer I wrote on Stack Exchange some years ago to the
    question "Is using goto ever worthwhile?" : https://softwareengineering.stackexchange.com/a/133523/33478

    Quoting from that answer :

    The main use of a goto in a reasonably modern language (one that
    supports if/else and loops) is to simulate a control flow construct
    that's missing from the language.

    I would support adding named loops and labeled exit/continue to
    a future version of C. I've used languages that have similar
    features, and have found them very useful. Given that C doesn't
    have multi-level break, I tend to agree that a goto statement is a
    reasonable way to simulate it, often better than the alternatives.
    (It's important to use a meaningful label name.) Similarly,
    the C code in the Linux kernel makes extensive use of gotos for
    error handling.

    If C didn't have a break statement at all, it could be simulated
    with goto. If that were the case, I'd still favor adding a break
    statement to the language. I support adding labeled break statements
    for the same reason. Goto is not the root of all evil, but it's
    worth some effort to avoid it when other constructs are clearer.


    I personally have found "goto" in C to be useful on no more than a
    couple of occasions during my entire career. I'd find a labelled
    "break;" statement somewhat more useful - but I think I can confidently
    say I would never want to use some kind of relative numbered "break 2;"
    or similar statement. Such multi-level breaks would be far too rare in
    code for people to interpret "automatically" - many programmers would
    need to think hard about whether the number is from the outside in, or
    inside out, and starting from 0 or starting from 1. A labelled break
    would be vastly clearer.

    If I find I have such complicated loops and need to break out of them, I
    will sometimes use local boolean flags - that gives you a convenient
    place to give a name to the condition, and the compiler turns it all
    into "gotos" in the generated code. Alternatively, the function can be
    split up - multi-level "break" from the inner loop is now just a
    "return" from the "inner" function. This would be neater if C supported
    nested functions of some sort - perhaps a restricted form of lambdas.
    (For C, it would be natural to limit these to cases where there is never
    a closure object created.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Dec 13 11:12:13 2024
    On 13/12/2024 08:50, David Brown wrote:
    On 12/12/2024 22:50, Keith Thompson wrote:
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 11 Dec 2024 17:27:53 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    bart <bc@freeuk.com> writes:
    [...]
    My experience of multi-level break is that there are two main
    use-cases:

      * Used in the current loop only (not necessarily the innermost to >>>>> an observer). This is the most common

      * Used to exit the outermost loop

    So to support these, named or even numbered loops are not
    necessary. (Eg. I use 'exit' or 'exit all'.)

    I would oppose a change to C that only applied to innermost and
    outermost loops.  For one thing, I'm not aware of any other language
    that does this (except perhaps your unnamed one).  For another,
    it's easy enough to define a feature that handles any arbitrary
    nesting levels, by applying names (labels) to loops.

    The better solution is education.
    Convince teachers in unis and colleges that goto is *not* considered
    harmful for this particular use case. Convince them to teach that
    attempts to avoid goto [for this particular use case] are really
    considered harmful. If you don't believe in authority of yourself then
    ask for help from somebody famous that share this view. I would guess
    that nearly all famous C programmers share it.

    Backward gotos tend to be dangerous.  Forward gotos are less so.

    Dijkstras original "Go To Statement Considered Harmful" letter was
    written in 1968, at a time when many languages didn't necessarily
    have the structured control constructs we've come to expect since
    then.  Using goto to implement a loop is almost always a bad idea,
    but it's something that a modern C programmer probably wouldn't
    even consider doing.

    I agree that gotos have valid uses.

    Here's an answer I wrote on Stack Exchange some years ago to the
    question "Is using goto ever worthwhile?" :
    https://softwareengineering.stackexchange.com/a/133523/33478

    Quoting from that answer :

         The main use of a goto in a reasonably modern language (one that
         supports if/else and loops) is to simulate a control flow construct >>      that's missing from the language.

    I would support adding named loops and labeled exit/continue to
    a future version of C.  I've used languages that have similar
    features, and have found them very useful.  Given that C doesn't
    have multi-level break, I tend to agree that a goto statement is a
    reasonable way to simulate it, often better than the alternatives.
    (It's important to use a meaningful label name.)  Similarly,
    the C code in the Linux kernel makes extensive use of gotos for
    error handling.

    If C didn't have a break statement at all, it could be simulated
    with goto.  If that were the case, I'd still favor adding a break
    statement to the language.  I support adding labeled break statements
    for the same reason.  Goto is not the root of all evil, but it's
    worth some effort to avoid it when other constructs are clearer.


    I personally have found "goto" in C to be useful on no more than a
    couple of occasions during my entire career.  I'd find a labelled
    "break;" statement somewhat more useful - but I think I can confidently
    say I would never want to use some kind of relative numbered "break 2;"
    or similar statement.  Such multi-level breaks would be far too rare in
    code for people to interpret "automatically" - many programmers would
    need to think hard about whether the number is from the outside in, or
    inside out, and starting from 0 or starting from 1.  A labelled break
    would be vastly clearer.

    If I find I have such complicated loops and need to break out of them, I
    will sometimes use local boolean flags - that gives you a convenient
    place to give a name to the condition, and the compiler turns it all
    into "gotos" in the generated code.  Alternatively, the function can be split up - multi-level "break" from the inner loop is now just a
    "return" from the "inner" function.  This would be neater if C supported nested functions of some sort - perhaps a restricted form of lambdas.
    (For C, it would be natural to limit these to cases where there is never
    a closure object created.)



    Some people don't like ordinary break either. There you could also
    suggest using convoluted logic, or using a function instead But here
    'break' would be clearly be simpler and easier.

    (I'd like to see a lambda example that is simpler than a two-level break
    or even a goto.)

    I've had a quick look through my codebases (not C), and I couldn't see
    an example of a numbered break. All I could find was the equivalant of:

    break # the vast majority
    break all

    The latter breaks out of the outermost loop.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to bart on Fri Dec 13 14:39:11 2024
    On Fri, 13 Dec 2024 11:12:13 +0000
    bart <bc@freeuk.com> wrote:



    Some people don't like ordinary break either. There you could also
    suggest using convoluted logic, or using a function instead But here
    'break' would be clearly be simpler and easier.


    Agreed.

    (I'd like to see a lambda example that is simpler than a two-level
    break or even a goto.)


    It would not be simpler than goto, but a little easier to follow.
    Relatively to two-level break, it's more general. Suppose, you have 3
    levels of nested loops. Two-level break does not help for breaking
    out of innermost loop *into* outermost loop.

    The problem with lambda in this role is that it would be an overkill.
    Also if one wants simplicity of writing then David's suggestion for
    lambda without capture is not sufficient. On the other hand, lambda
    with capture of full environment by reference is very easy to write,
    but potentially very hard to follow and error-prone.

    I am strictly inclined to think that goto is better than all
    alternatives proposed so far.

    I've had a quick look through my codebases (not C), and I couldn't
    see an example of a numbered break. All I could find was the
    equivalant of:

    break # the vast majority
    break all

    The latter breaks out of the outermost loop.


    For the reason explained above I don't like 'break all' idea.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Michael S on Fri Dec 13 14:31:23 2024
    On 13/12/2024 13:39, Michael S wrote:
    On Fri, 13 Dec 2024 11:12:13 +0000
    bart <bc@freeuk.com> wrote:



    Some people don't like ordinary break either. There you could also
    suggest using convoluted logic, or using a function instead But here
    'break' would be clearly be simpler and easier.


    Agreed.

    (I'd like to see a lambda example that is simpler than a two-level
    break or even a goto.)


    It would not be simpler than goto, but a little easier to follow.
    Relatively to two-level break, it's more general. Suppose, you have 3
    levels of nested loops. Two-level break does not help for breaking
    out of innermost loop *into* outermost loop.

    The problem with lambda in this role is that it would be an overkill.

    Some might say "overkill" - others would say that if C first gains a
    powerful feature that can be useful in many contexts, then multi-level
    break is unnecessary. I would not imagine adding lambdas to the
    language /solely/ for this use-case!

    Also if one wants simplicity of writing then David's suggestion for
    lambda without capture is not sufficient. On the other hand, lambda
    with capture of full environment by reference is very easy to write,
    but potentially very hard to follow and error-prone.


    I did not suggest lambdas without captures - I suggested lambdas with
    the restriction that closure objects would not be needed. In
    particular, as long as the lambda is only ever used within the lifetime
    of any variables it captures by reference, no closure object is ever needed.

    If a function is returning a lambda or passing it on somewhere, then I
    would think it would be appropriate for C to restrict the lambda to one
    without captures. In C++, such lambdas can be converted to function
    pointers of appropriate, and the same ought to apply in C. (constexpr
    data in scope at the definition of the lambda can be freely used as
    though they are implicit captures.)

    (In C++ programming, I am not keen on lambdas catching by default,
    whether [=] or [&]. I prefer explicit capture lists.)

    I am strictly inclined to think that goto is better than all
    alternatives proposed so far.

    I've had a quick look through my codebases (not C), and I couldn't
    see an example of a numbered break. All I could find was the
    equivalant of:

    break # the vast majority
    break all

    The latter breaks out of the outermost loop.


    For the reason explained above I don't like 'break all' idea.




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Dec 13 14:19:17 2024
    On 13/12/2024 12:12, bart wrote:
    On 13/12/2024 08:50, David Brown wrote:
    On 12/12/2024 22:50, Keith Thompson wrote:
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 11 Dec 2024 17:27:53 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    bart <bc@freeuk.com> writes:
    [...]
    My experience of multi-level break is that there are two main
    use-cases:

      * Used in the current loop only (not necessarily the innermost to >>>>>> an observer). This is the most common

      * Used to exit the outermost loop

    So to support these, named or even numbered loops are not
    necessary. (Eg. I use 'exit' or 'exit all'.)

    I would oppose a change to C that only applied to innermost and
    outermost loops.  For one thing, I'm not aware of any other language >>>>> that does this (except perhaps your unnamed one).  For another,
    it's easy enough to define a feature that handles any arbitrary
    nesting levels, by applying names (labels) to loops.

    The better solution is education.
    Convince teachers in unis and colleges that goto is *not* considered
    harmful for this particular use case. Convince them to teach that
    attempts to avoid goto [for this particular use case] are really
    considered harmful. If you don't believe in authority of yourself then >>>> ask for help from somebody famous that share this view. I would guess
    that nearly all famous C programmers share it.

    Backward gotos tend to be dangerous.  Forward gotos are less so.

    Dijkstras original "Go To Statement Considered Harmful" letter was
    written in 1968, at a time when many languages didn't necessarily
    have the structured control constructs we've come to expect since
    then.  Using goto to implement a loop is almost always a bad idea,
    but it's something that a modern C programmer probably wouldn't
    even consider doing.

    I agree that gotos have valid uses.

    Here's an answer I wrote on Stack Exchange some years ago to the
    question "Is using goto ever worthwhile?" :
    https://softwareengineering.stackexchange.com/a/133523/33478

    Quoting from that answer :

         The main use of a goto in a reasonably modern language (one that >>>      supports if/else and loops) is to simulate a control flow construct
         that's missing from the language.

    I would support adding named loops and labeled exit/continue to
    a future version of C.  I've used languages that have similar
    features, and have found them very useful.  Given that C doesn't
    have multi-level break, I tend to agree that a goto statement is a
    reasonable way to simulate it, often better than the alternatives.
    (It's important to use a meaningful label name.)  Similarly,
    the C code in the Linux kernel makes extensive use of gotos for
    error handling.

    If C didn't have a break statement at all, it could be simulated
    with goto.  If that were the case, I'd still favor adding a break
    statement to the language.  I support adding labeled break statements
    for the same reason.  Goto is not the root of all evil, but it's
    worth some effort to avoid it when other constructs are clearer.


    I personally have found "goto" in C to be useful on no more than a
    couple of occasions during my entire career.  I'd find a labelled
    "break;" statement somewhat more useful - but I think I can
    confidently say I would never want to use some kind of relative
    numbered "break 2;" or similar statement.  Such multi-level breaks
    would be far too rare in code for people to interpret "automatically"
    - many programmers would need to think hard about whether the number
    is from the outside in, or inside out, and starting from 0 or starting
    from 1.  A labelled break would be vastly clearer.

    If I find I have such complicated loops and need to break out of them,
    I will sometimes use local boolean flags - that gives you a convenient
    place to give a name to the condition, and the compiler turns it all
    into "gotos" in the generated code.  Alternatively, the function can
    be split up - multi-level "break" from the inner loop is now just a
    "return" from the "inner" function.  This would be neater if C
    supported nested functions of some sort - perhaps a restricted form of
    lambdas. (For C, it would be natural to limit these to cases where
    there is never a closure object created.)



    Some people don't like ordinary break either. There you could also
    suggest using convoluted logic, or using a function instead But here
    'break' would be clearly be simpler and easier.

    I can only express my own opinion - if someone else doesn't like
    "break;", that's up to them.


    (I'd like to see a lambda example that is simpler than a two-level break
    or even a goto.)

    <https://godbolt.org/z/7qWf966Er>

    There is a simple three-level nested loop function with an "escape" from
    the inside loop, written in several different ways. There is somewhat arbitrary calculations before and after the loops, so that the it can't
    just use a direct "return" from the middle of the loop - it needs some
    kind of multi-level break effect when "test(x, y, z)" passes. The
    lambda version code is C++, since that has lambdas and C doesn't, but
    the rest can be compiled as C or C++.

    (I've also used C++ tuples for returning multiple values. I could have
    used a struct, but tuples are more convenient for this kind of thing.)

    The generated code from gcc is all much the same for each function
    version - there are no significant overheads for any of the methods.

    I haven't compared to a multi-level break statement, because neither C
    nor C++ have such a feature (as yet).

    What you think is "simpler", is, of course, highly subjective. I like
    the lambda version ("foo5") for various reasons. All the variables are initialised in their declarations and (apart from the loop indexes)
    could be declared "const" - where possible, I prefer local variables to
    be initialised and unchanging as it makes the code easier to follow and
    easier to reason about. The inner loop function is at the appropriate
    place in the code as a local function, unlike the C version "foo4".

    The other big advantage of the "static inner function" version (valid C)
    and the lambda version (C++ only - though lambdas are being considered
    for C) is that it is immediately obvious when writing them that there is
    a huge difference between breaking out of the loops when the test
    passes, and falling off the end of the loops because the test never
    passes. I've been lazy in this example, but you can't miss it - with
    the goto to break versions, it's easily forgotten.

    int foo5(int m) {
    int n = m + 1;
    auto [x, y, z] = [n]{
    for (int x = 2; x < n; x++) {
    for (int y = 2; y < n; y++) {
    for (int z = 2; z < n; z++) {
    if (test(x, y, z)) return std::tuple(x, y, z);
    }
    }
    }
    return std::tuple(n, n, n);
    }();
    return 10000 * x + 100 * y + z;
    }


    I've had a quick look through my codebases (not C), and I couldn't see
    an example of a numbered break. All I could find was the equivalant of:

       break              # the vast majority
       break all

    The latter breaks out of the outermost loop.


    So you have a feature (numbered breaks) in your language that you never
    use, but have been recommending here as a useful addition to C?

    I can appreciate that "break out of all loops" would be the most
    commonly useful type of break beyond the standard "break out of the
    innermost loop".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Keith Thompson on Fri Dec 13 14:50:26 2024
    On 12.12.2024 22:50, Keith Thompson wrote:

    Backward gotos tend to be dangerous. Forward gotos are less so.

    I think I agree.


    Dijkstras original "Go To Statement Considered Harmful" letter was
    written in 1968, at a time when many languages didn't necessarily
    have the structured control constructs we've come to expect since
    then. Using goto to implement a loop is almost always a bad idea,
    but it's something that a modern C programmer probably wouldn't
    even consider doing.

    Also in languages with control constructs it could be observed that
    there was a tendency to write "spaghetti-code" with 'goto'. And yes,
    that observation was certainly the reason for Dijkstra's paper.


    I agree that gotos have valid uses.

    I'm unable to provide references, but from faint memories I recall
    that exiting deeply nested loops was academically mentioned to be
    such a "valid" use of 'goto'. - And, frankly, I've rarely seen (or
    felt the need) for [sensible] other uses of 'goto'. One other use
    was to implement an (explicitly programmed, not table-driven) FSA
    (but personally I'm using other methods even for that application
    case).


    Here's an answer I wrote on Stack Exchange some years ago to the
    question "Is using goto ever worthwhile?" :
    [...]

    I would support adding named loops and labeled exit/continue to
    a future version of C. I've used languages that have similar
    features, and have found them very useful. Given that C doesn't
    have multi-level break, I tend to agree that a goto statement is a
    reasonable way to simulate it, often better than the alternatives.

    Yes, that might be the case.

    I'm not so sure about the idea of adding yet more jump-types. I
    consider 'break' as a camouflage to not make that "evil goto" too
    obvious and to restrict its "wildness" to a safer program context
    (that also makes things like stack-unrolling etc. superfluous).
    Give how rarely (IME) they seem to be used one 'goto' seems to be
    enough - but 'break' and 'continue' are useful in specific types
    of program logic, though 'break' with numbers as arguments (that
    I used once or twice in Unix shell) I'd not consider to be a good
    idea; labels would certainly be better.

    [...]

    Here's an example of a small C program that completely avoids the
    use of goto statements. I reserve the right to ridicule anyone who
    takes this program seriously.

    Thanks for the warning! :-)


    [ source code with lots of setjmp/longjmp ]

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Fri Dec 13 14:16:45 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 12/12/2024 22:50, Keith Thompson wrote:
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 11 Dec 2024 17:27:53 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    bart <bc@freeuk.com> writes:
    [...]
    My experience of multi-level break is that there are two main
    use-cases:

    * Used in the current loop only (not necessarily the innermost to
    an observer). This is the most common

    * Used to exit the outermost loop

    So to support these, named or even numbered loops are not
    necessary. (Eg. I use 'exit' or 'exit all'.)

    I would oppose a change to C that only applied to innermost and
    outermost loops. For one thing, I'm not aware of any other language
    that does this (except perhaps your unnamed one). For another,
    it's easy enough to define a feature that handles any arbitrary
    nesting levels, by applying names (labels) to loops.

    The better solution is education.
    Convince teachers in unis and colleges that goto is *not* considered
    harmful for this particular use case. Convince them to teach that
    attempts to avoid goto [for this particular use case] are really
    considered harmful. If you don't believe in authority of yourself then
    ask for help from somebody famous that share this view. I would guess
    that nearly all famous C programmers share it.

    Backward gotos tend to be dangerous. Forward gotos are less so.

    Dijkstras original "Go To Statement Considered Harmful" letter was
    written in 1968, at a time when many languages didn't necessarily
    have the structured control constructs we've come to expect since
    then. Using goto to implement a loop is almost always a bad idea,
    but it's something that a modern C programmer probably wouldn't
    even consider doing.

    I agree that gotos have valid uses.

    Here's an answer I wrote on Stack Exchange some years ago to the
    question "Is using goto ever worthwhile?" :
    https://softwareengineering.stackexchange.com/a/133523/33478

    Quoting from that answer :

    The main use of a goto in a reasonably modern language (one that
    supports if/else and loops) is to simulate a control flow construct
    that's missing from the language.

    I would support adding named loops and labeled exit/continue to
    a future version of C. I've used languages that have similar
    features, and have found them very useful. Given that C doesn't
    have multi-level break, I tend to agree that a goto statement is a
    reasonable way to simulate it, often better than the alternatives.
    (It's important to use a meaningful label name.) Similarly,
    the C code in the Linux kernel makes extensive use of gotos for
    error handling.

    If C didn't have a break statement at all, it could be simulated
    with goto. If that were the case, I'd still favor adding a break
    statement to the language. I support adding labeled break statements
    for the same reason. Goto is not the root of all evil, but it's
    worth some effort to avoid it when other constructs are clearer.


    I personally have found "goto" in C to be useful on no more than a
    couple of occasions during my entire career. I'd find a labelled
    "break;" statement somewhat more useful - but I think I can confidently
    say I would never want to use some kind of relative numbered "break 2;"
    or similar statement. Such multi-level breaks would be far too rare in
    code for people to interpret "automatically" - many programmers would
    need to think hard about whether the number is from the outside in, or
    inside out, and starting from 0 or starting from 1. A labelled break
    would be vastly clearer.

    When I do use goto, which is rare, it is almost always to branch
    to the end of a function that has acquired a mutex so there there
    doen't need to be a call to pthread_mutex_unlock before every early
    "return" statement.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Dec 13 14:26:59 2024
    On 13/12/2024 13:19, David Brown wrote:
    On 13/12/2024 12:12, bart wrote:

    Some people don't like ordinary break either. There you could also
    suggest using convoluted logic, or using a function instead But here
    'break' would be clearly be simpler and easier.

    I can only express my own opinion - if someone else doesn't like
    "break;", that's up to them.


    (I'd like to see a lambda example that is simpler than a two-level
    break or even a goto.)

    <https://godbolt.org/z/7qWf966Er>

    There is a simple three-level nested loop function with an "escape" from
    the inside loop, written in several different ways.  There is somewhat arbitrary calculations before and after the loops, so that the it can't
    just use a direct "return" from the middle of the loop - it needs some
    kind of multi-level break effect when "test(x, y, z)" passes.

      The
    lambda version code is C++, since that has lambdas and C doesn't, but
    the rest can be compiled as C or C++.

    The lambda version needs closures from what I can see, although that
    might be optimised out.


    (I've also used C++ tuples for returning multiple values.

    From that 'inner' function presumably; the main one returns one value.


    I haven't compared to a multi-level break statement, because neither C
    nor C++ have such a feature (as yet).

    I've written a version in my syntax below, with some comments.

    The other big advantage of the "static inner function" version (valid C)
    and the lambda version (C++ only - though lambdas are being considered
    for C) is that it is immediately obvious when writing them that there is
    a huge difference between breaking out of the loops when the test
    passes, and falling off the end of the loops because the test never
    passes.  I've been lazy in this example, but you can't miss it - with
    the goto to break versions, it's easily forgotten.

    Do you mean this line:

            return std::tuple(n, n, n);

    Because even without it, it seems to work for M=10 (when the break is
    commented out). That is, it gives the same result on versions that just
    fall off the end of the outer loop.

    I've had a quick look through my codebases (not C), and I couldn't see
    an example of a numbered break. All I could find was the equivalant of:

        break              # the vast majority
        break all

    The latter breaks out of the outermost loop.


    So you have a feature (numbered breaks) in your language that you never
    use, but have been recommending here as a useful addition to C?

    I'm saying that if you don't have the general feature, then the more
    limited one might be worth considering, as it will take care of most of
    the use-cases not covered by break.

    (My personal codebase is not expansive enough to definitely say
    intermediate breaks will be never needed, or very rarely. Others may use
    nested loops a lot more.)

    --------------------------

    fun test(int x, y, z)bool = sqr(x)+sqr(y) = sqr(z)

    func foo(int n)int =
    for x in 2..n do
    for y in 2..n do
    for z in 2..n do
    exit all when test(x,y,z)
    od
    od
    od

    x*10000 + y*100 + z
    end

    proc main=
    println foo(10)
    end
    --------------------------

    Notes:

    * Your version seems to convert from 1- to 0-based; I reversed that. But
    that might have been to inject some extra code outside the loops

    * Despite what you say, such a example could easily just return from
    inside the loop. So the example is just to illustrate multi-level
    break

    * (If this is supposed to find Pythagorean triples, those loops could be
    shorter)

    * The program size in bytes is here half the size of your foo5() example
    in C++ (with other functions and cplusplus blocks removed). (288 vs
    570 bytes)

    * I tried your foo5 version in my dynamic language. There I found some
    scoping issues that I'd need to fix. Putting workarounds in, plus
    that extra return after the loops, made it much busier than my static
    version. That language of course runs the simple version too, and is
    somewhat shorter.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Dec 13 17:52:11 2024
    On 13/12/2024 15:26, bart wrote:
    On 13/12/2024 13:19, David Brown wrote:
    On 13/12/2024 12:12, bart wrote:

    Some people don't like ordinary break either. There you could also
    suggest using convoluted logic, or using a function instead But here
    'break' would be clearly be simpler and easier.

    I can only express my own opinion - if someone else doesn't like
    "break;", that's up to them.


    (I'd like to see a lambda example that is simpler than a two-level
    break or even a goto.)

    <https://godbolt.org/z/7qWf966Er>

    There is a simple three-level nested loop function with an "escape"
    from the inside loop, written in several different ways.  There is
    somewhat arbitrary calculations before and after the loops, so that
    the it can't just use a direct "return" from the middle of the loop -
    it needs some kind of multi-level break effect when "test(x, y, z)"
    passes.

      The lambda version code is C++, since that has lambdas and C
    doesn't, but the rest can be compiled as C or C++.

    The lambda version needs closures from what I can see, although that
    might be optimised out.


    Yes. Whether the closure is "optimised out", or was never created in
    the first place (no closure ever needs to be generated when the captured variables are all still in their lifetime when the lambda is run) is a
    question of implementation detail - and not one that I can answer.


    (I've also used C++ tuples for returning multiple values.

    From that 'inner' function presumably; the main one returns one value.


    Yes.


    I haven't compared to a multi-level break statement, because neither C
    nor C++ have such a feature (as yet).

    I've written a version in my syntax below, with some comments.


    It's fairly obvious how your code is working (that's a complement, by
    the way). "exit" seems a bit dramatic compared to "break", but perhaps
    that's just habit from C, Python and other programming languages.

    The other big advantage of the "static inner function" version (valid
    C) and the lambda version (C++ only - though lambdas are being
    considered for C) is that it is immediately obvious when writing them
    that there is a huge difference between breaking out of the loops when
    the test passes, and falling off the end of the loops because the test
    never passes.  I've been lazy in this example, but you can't miss it -
    with the goto to break versions, it's easily forgotten.

    Do you mean this line:

             return std::tuple(n, n, n);

    Because even without it, it seems to work for M=10 (when the break is commented out). That is, it gives the same result on versions that just
    fall off the end of the outer loop.

    That is why I picked that return value - it keeps it the same for all
    the versions of the function.

    However, in a real program, you would almost certainly want to treat
    this case differently - being forced to write something specific here is
    a good thing, and better than you get with a multi-level break.
    (Exactly how you treat this case would depend on the real code - it
    could be marked with "unreachable();", it could return a default value,
    it could return a canary value, it could throw a C++ exception, it could
    return a std::optional<> or std::expected<> or a C structure with
    similar functionality, or print an error message and exit the program.)


    I've had a quick look through my codebases (not C), and I couldn't
    see an example of a numbered break. All I could find was the
    equivalant of:

        break              # the vast majority
        break all

    The latter breaks out of the outermost loop.


    So you have a feature (numbered breaks) in your language that you
    never use, but have been recommending here as a useful addition to C?

    I'm saying that if you don't have the general feature, then the more
    limited one might be worth considering, as it will take care of most of
    the use-cases not covered by break.


    If lambdas are not added to C, then a multi-level break of some sort
    would be useful to some people - I agree on that.

    (My personal codebase is not expansive enough to definitely say
    intermediate breaks will be never needed, or very rarely. Others may use nested loops a lot more.)

    --------------------------

    fun test(int x, y, z)bool = sqr(x)+sqr(y) = sqr(z)

    func foo(int n)int =
        for x in 2..n do
            for y in 2..n do
                for z in 2..n do
                    exit all when test(x,y,z)
                od
            od
        od

        x*10000 + y*100 + z
    end

    proc main=
        println foo(10)
    end
    --------------------------

    Notes:

    * Your version seems to convert from 1- to 0-based; I reversed that. But
      that might have been to inject some extra code outside the loops

    It was merely to inject some extra code outside the loops - nothing more
    than that.


    * Despite what you say, such a example could easily just return from
      inside the loop. So the example is just to illustrate multi-level
      break

    The point was to put the the handling of the x, y, z result /outside/
    the loop, precisely to avoid a return from inside the loop. Pretend it
    did something more than evaluate a simple arithmetic expression.


    * (If this is supposed to find Pythagorean triples, those loops could be
      shorter)


    Nothing here is doing a particularly difficult job - the whole thing
    could have been replaced by "return 30405;", but that would not be very helpful!

    * The program size in bytes is here half the size of your foo5() example
      in C++ (with other functions and cplusplus blocks removed). (288 vs
      570 bytes)

    The size cannot sensibly be compared.


    * I tried your foo5 version in my dynamic language. There I found some
      scoping issues that I'd need to fix. Putting workarounds in, plus
      that extra return after the loops, made it much busier than my static
      version. That language of course runs the simple version too, and is
      somewhat shorter.

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