Programming thread

  • Want to keep track of this thread?
    Accounts can bookmark posts, watch threads for updates, and jump back to where you stopped reading.
    Create account
like what
Screen_Shot_2019-11-14_at_12.59-2850715685.jpg
I linked the article for a reason, Dumb Dude 43. It answers your question at length.
 
like what
imo the main use case for goto is breaking through multiple layers of nested loops, for everything else there's better options
something like
Code:
setupcode1()
if (somethinggowrong)
    goto td1;
setupcode2()
if (somethinggowrong)
    goto td2;
setupcode3()
if (somethinggowrong)
    goto td3;
setupcode4()
if (somethinggowrong)
    goto td4;

dostuff()
td4:
teardowncode4()
td3:
teardowncode3()
td2:
teardowncode2()
td1:
teardowncode1()

Can be useful for handling cleanup code.

My goodness, no. I cited the context of the paper: go to statements. This is Knuth firing back at Dijkstra's "Go To Considered Harmful", and is Knuth explaining that, no, GOTO commands are just fine, and shoehorning other control flow methods into the space where GOTO fits naturally is bad practice.

Some reading materials, because apparently people in this thread are illiterate.

https://archive.org/details/Structured_Programming__Dahl_Dijkstra_Hoare - Structured Programming by Dahl, Dijkstra, Hoare
https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf - go to Considered Harmful by Dijkstra
https://dl.acm.org/doi/epdf/10.1145/356635.356640 - Structured Programming with go to Statements by Knuth
Yeah I did a tardery. Don't drink and post.
 
honestly i believe if arrays werent a syntax sugar over a pointer but their own object/abstraction they would start at one, since in mathematics first element of something is denoted using a "1" not "0"
but it does make math a little bit wonky, for example taking a mod of something would require adding a one and such
If you think that mathematicians keep a standard for denoting first element you are mistaken. Infact it is even less standarized than in programing as most often it is whatever is most convinent
 
My goodness, no. I cited the context of the paper: go to statements. This is Knuth firing back at Dijkstra's "Go To Considered Harmful", and is Knuth explaining that, no, GOTO commands are just fine, and shoehorning other control flow methods into the space where GOTO fits naturally is bad practice.
I think we read different papers. Because Knuth is stating quite clearly that goto should be the last resort for when higher level control flows do not suffice. And even then the first question should be whether you are thinking about problem in right manner.

He taught them to use go t o
only in unusual special cases where i f and
w h i l e aren't right, b u t he found [78] t h a t
"A disturbingly large percentage of the
students ran into situations t h a t require
go to's, and sure enough, it was often because
w h i l e didn't work well to their plan, b u t
almost invariably because their plan was
poorly thought out." Because of arguments
like this, I ' d say we should, indeed, abolish
go t o from the high-level language, at least
as an experiment in training people to
formulate their abstractions more carefully.
This does have a beneficial effect on style,
although I would not make such a prohibi-
tion if the new language features described
above were not available. T h e question is
whether we should ban it, or educate against
i t ; should we a t t e m p t to legislate program
morality? In this case I vote for legislation,
with appropriate legal substitutes in place
of the former overwhelming temptations.
Other than that recursion elimination is solved by tail call optimization nowadays.
Coroutines can be easily implemented as language feature.

I really don't believe there is much use of goto nowadays, espcially true goto, which would be longjump in C.
 
https://dl.acm.org/doi/epdf/10.1145/356635.356640 - Structured Programming with go to Statements by Knuth
its great to see people were calling things "literally 1984" even before the internet
1778280428774.png
Why even bother with array indices? Any decent language worth using has support for foreach.

C:
#include <stdio.h>
#define foreach(e,a)for(__typeof__(*a)*e=a,*a##_last=(a+(sizeof(a)/sizeof(*a))-1);e<=a##_last;++e)
int main(void) {
    int xs[]={1,2,3,4,5};
    foreach(x,xs)*x=*x%2?*x*3+1:*x/2;
    foreach(x,xs)printf("%d%s",*x,x!=xs_last?", ":"\n");
}

This might look hideous, but this is next level memory management which saves multiple bytes of disk space by removing extranous whitespace and using minimal variable names. But I guess this sort of thing would probably go over the heads of a bunch of cave-dwelling webdevs who only know JavaScript... :story:
thats how APL niggers actually genuinely unironically write c btw
except they wouldve called the macro "f" or "fe" if we're being generous
 
something like
Code:
setupcode1()
if (somethinggowrong)
    goto td1;
setupcode2()
if (somethinggowrong)
    goto td2;
setupcode3()
if (somethinggowrong)
    goto td3;
setupcode4()
if (somethinggowrong)
    goto td4;

dostuff()
td4:
teardowncode4()
td3:
teardowncode3()
td2:
teardowncode2()
td1:
teardowncode1()

Can be useful for handling cleanup code.

this feels like the type of thing that raii was made to avoid
 
this feels like the type of thing that raii was made to avoid
yeah
gcc also has the cleanup attribute as an extension which does exactly that
gcc manual said:
cleanup (cleanup_function)
This attribute applies to variables.

The cleanup attribute runs a function when the variable goesout of scope. This attribute can only be applied to auto functionscope variables; it may not be applied to parameters or variables with static storage duration. The function must take one parameter,a pointer to a type compatible with the variable. The return value of the function (if any) is ignored.

When multiple variables in the same scope have cleanupattributes, at exit from the scope their associated cleanup functionsare run in reverse order of definition (last defined, first cleanup).
 
I think we read different papers. Because Knuth is stating quite clearly that goto should be the last resort for when higher level control flows do not suffice. And even then the first question should be whether you are thinking about problem in right manner.
You're taking my comments out of context. Here is how the Conclusion begins.
2026-05-08-162032_339x611_scrot.png

Remember, this is a response to "GOTO Considered Harmful". My statement "GOTO commands are just fine" shouldn't be read as "GOTO commands are always fine", but more that "GOTO elimination" as a dogmatic belief is an instance of premature optimization.

"should be the last resort" is stretching what Knuth is saying here.
 
"should be the last resort" is stretching what Knuth is saying here.
I think it's perfectly acceptable interpretation given modern context, especially given that he himself states that "new types of syntaxes are being developed".
Which is also my arguement, goto is replacement for missing language feature.

My statement "GOTO commands are just fine" shouldn't be read as "GOTO commands are always fine", but more that "GOTO elimination" as a dogmatic belief is an instance of premature optimization.
I don't think I would interpret what Knuth is saying as this. I honestly believe he has opposite intention from this text, especially if you read later part of his conclusion which talks about students.
In which yes goto is acceptable, but in many, and maybe most, cases it's not the best choice and often a result of insufficient care in designing of structure.

Also in the context of premature optimization in knuth paper refers to using goto as optimizing transformation. So I think you have it flipped.
Using goto early is premature optimization, but you might want to use it.
1778284217139.png
 
Are there any real world cases for go to other than continuing from a deeply nested loop? I can't think of any in my experience, and this was a specific circumstance, usually nested loops are a code smell.
 
But goto is almost the only control statement. Everything else is just syntactic sugar on top of goto.
If is just conditional goto. Function calls are push stuff on the stack then goto. Return is pop stuff off the stack and goto.
 
But goto is almost the only control statement. Everything else is just syntactic sugar on top of goto.
If is just conditional goto. Function calls are push stuff on the stack then goto. Return is pop stuff off the stack and goto.
Well, all things considered, all programming is an abstraction over selection, sequence, and repetition, unconditional GOTO being sequence and conditional GOTO being selection/repetition; or rather, all you need is BEQ/JE/conditional jump and you're good to go; the unconditional jump is already a single layer of abstraction over the conditional jump where the operands are always true/equal.

However every layer of abstraction above this is meant to make the mental load of dealing with these things easier, even if it maps imperfectly to a cpu. Imperfect software that is done is better than perfect software that's never finished - and maintainable, finished software is even better.
 
It's a bit amusing to see people question why goto exists because it shows that they can't see how so much of modern software is contorted to avoid it. So much of software today is written in a manner that effectively obfuscates the control flow by replacing the good old program counter with lots of miscellaneous flags, values that double as flags (nullable values), etc. Control flow gets turned into a string or enum or boolean which gets tested again to get back to control flow.

If you really want to find the places where goto is necessary, look at places where you can't just use other jumps as exact replacements. Lots of procedures use "return" as a means of skipping code that would otherwise be executed, but what exactly it skips is inherently tied to the procedure it's contained in - if you've ever tried copy+pasting a helper procedure's contents into another, you quickly discover that it also skips the rest of the containing procedure. Unless, of course, you're using a half-decent language that permits nested procedures or even just Common Lisp's "block" and "return-from" forms.

Also, sometimes the use of structures instead of goto's is just a plain nuisance. For example, many languages maintain a statement/expression distinction and will only allow expressions in certain parts of the structure, such as the condition for "if" or, much worse, "while". Many a CS101 student has been baffled at the need to write

Python:
foo = input()
while not len(foo) == 0:
    #... do something ...
    foo = input()

especially considering how much you need to repeat yourself as the code that needs to be evaluated immediately before each test of the condition grows in size. People will try to cope by trying to combine multiple expressions together, using assignments as expressions, etc, but you can't include control structures themselves in the condition, to say nothing of exception handling clauses or finally blocks. For example, suppose our CS101 student didn't have "input" and only had "getchar". He would need to write a getchar loop twice. Of course, he could factor it out into a separate procedure, but that procedure may not be general in the slightest, and it rules out accessing the enclosing scope, and it adds an indirection that anyone trying to follow the control flow will have to look up. All because of an arbitrary syntax decision.

By comparison, goto is freedom:
C-like:
loop_start:
foo = input();
if (len(foo) == 0) goto loop_end;
/* ... do something ... */
goto loop_start
loop_end:

The smart-aleck will look at this and notice that the same can actually be achieved using a while loop like so:
C-like:
while(1)
{
    foo = input();
    if (len(foo) == 0) break;
    /* ... do something ...*/
}

Which is functionally the same thing, except letting the language create the labels and the goto at the bottom for you, at the cost of the language also creating a conditional branch that always fails (will typically be optimized out, but the cognitive overhead and eyesore remains).

Or consider the case of trying multiple nontrivial ways of getting something, then doing something complex with it. Normally the way of trying a bunch of alternatives is to write if ... else if ... else if ... and so on, in sequence, but nothing nontrivial can really go inside the test expression of the "if". So you end up writing something like
C-like:
/* Try to acquire using method 1 */
if (!succeeded)
{
    /* Try to acquire using method 2 */
    if (!succeeded)
    {
        /* Try to acquire using method 3, and so on... */
    }
}
/* Complex usage of thing */

But that's terrible, so you accept the pain of repeatedly checking "succeeded" even when it's already known to be true:
C-like:
/* Try to acquire using method 1 */
if (!succeeded)
{
    /* Try to acquire using method 2 */
}
if (!succeeded)
{
    /* Try to acquire using method 3, and so on... */
}
/* ... */
/* Complex use of thing acquired */

You see how we're encoding control flow in an extra flag that has to keep its value across all these attempts now instead of the program counter as God intended? Meanwhile with goto it's as simple as
C-like:
/* Attempt to acquire using method 1 */
if (succeeded) goto use;
/* Attempt to acquire using method 2 */
if (succeeded) goto use;
/* Attempt to acquire using method 3, and so on */
if (succeeded) goto use;
/* ... */
use:
/* Complex use of acquired thing */

Of course, the truly skilled at coping with an insistence on avoiding gotos will factor out all the "acquire" parts into a separate procedure so that they can use "return" to skip over all the remaining methods on success. Better yet, they might add a do...while(0) so that they can use "break" to skip over all the remaining methods on success.

Basically, when things get complex, programmers often reach for "return" or "break" or "continue", not because they want to return a value or explicitly need a conditional loop, but because they need "goto" but have been told "goto" is bad (or, worse, their language doesn't have it). This is the same sort of abstraction inversion that gives us things like RAII-instead-of-finally-blocks in C++ or object-with-no-fields-and-a-single-method-instead-of-function-pointer in Java. It's niggerlicious and I cannot take any language that refuses to have gotos (or an explicit equivalent such as tail calls) seriously.
 
That's simplifcation that's probably a reason why RAII have such a bad rep. RAII is not about memory, it's about resources which are much more abstract.
Containers still uses RAII to construct/destruct objects, but you don't have to allocate memory a lot of the times.
Moreover you can easily compose classes which should uphold their invariants thanks to ctors and release everything thanks to dtors after just object of top class is destroyed. All without having to manually keep track of what's need to be initialized and what's need to freed.
RAII is absolutely killer feature of C++, and reason why it keeps dominating in certain industries.
I really don't like RAII in C++ because it hides acquisition of scarce resources by making them look like normal variable declarations. Since using can't currently be followed by a left parenthesis in legal C++ code, I suggest that a new statement of the form using (decl1; decl2; ...) statement be introduced, where all the declarations have types that have been declared with a new [[resource]] attribute or are locally declared as having a new [[large]] attribute. This way, it would be clear that there's more going on that a usual local variable.

As it is, if you have code like:
Code:
class Lock {
    Lock(Mutex *m) { ... }  // acquire mutex
    ~Lock() { ... }  // release mutex
}
...
if (...) {
    Lock lock(some_mutex);
    ...
} else {
    std::vector<T> vec(N);  // allocate ~5% of total system memory
    ...
}

You could make it look like this:
Code:
class [[resource]] Lock {  // now legal in using (...) statements and a warning when used elsewhere
    ....
}
...
if (...) {
    using (Lock lock(some_mutex)) {
        ...
    }
} else {
    using ([[large]] std::vector<T> vec(N)) {
        ....
    }
}

As it is, it's tempting to add more code to a block that contains an RAII variable and unnecessarily extend the amount of time that a scarce resource is held, since it's syntactically identical to a normal, non-RAII block, and I think a new explicit syntax would help.

...
guys i think arrays SHOULD start at 1
Agreed, but only if they adopt full Icon semantics for arrays, where the indices refer to the gaps between elements, identify the element to their immediate right, and run in both directions: so a[1] is the first element of a, a[-1] is the last element of a, and a[2:0] is a slice of a containing everything but the first element (i.e., everything from the position between the first and second elements and the position immediately following the end of the array).
 
Last edited:
Of course, the truly skilled at coping with an insistence on avoiding gotos will factor out all the "acquire" parts into a separate procedure so that they can use "return" to skip over all the remaining methods on success. [...] Basically, when things get complex, programmers often reach for "return" or "break" or "continue", not because they want to return a value or explicitly need a conditional loop, but because they need "goto" but have been told "goto" is bad (or, worse, their language doesn't have it).
goto? Where? Why? return, break, and continue, are not just synonyms for goto, they have extra semantic meaning. I don't have to look for the label or assume the programmers intention. Goto has a bad image because most of the people definding it are midwits. RAII (and defer) are much better solutions to the only problem that goto actually solves.

I know ":=" is relatively new in python, but C has always had the same construct.
Python:
def get_next_line():
    # add linux support (^D)
    try:
        line = input()
    except EOFError:
        return None

    # empty string is falsey, but maybe you want it to be explicit?
    if len(line) == 0:
        return False

    return line

while line := get_next_line():
    print(line)

i think arrays should take indices that are unicode strings

arr[🍑] = 60;
arr[:eggplant:] = 7;
int i = [arr[🍑] + arr[
:eggplant:
];
cout << i; //67
C can do that, as long as you use single characters. I was having trouble with emojis, I hope the cursed array access makes up for it. chars are just ints, which are (de facto) signed 32 bit, big enough for any unicode character but you might have trouble with negative indices, depending on the emoji.

C:
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *x = malloc('€');
    x['['] = 34,
    x[']'] = 35;
    printf("%u\n", '['[x] + ']'[x]);
}

(apparently I'm not the only one with emoji problems)
2026-05-09-162043_1131x65_scrot.png
 

Attachments

  • 2026-05-09-162043_1131x65_scrot.png
    2026-05-09-162043_1131x65_scrot.png
    16.8 KB · Views: 4
Back
Top Bottom