Developer Argues For 'Forgotten Code Constructs' Like GOTO and Eval (techbeacon.com)
mikeatTB quotes TechBeacon:
Some things in the programming world are so easy to misuse that most people prefer to never use them at all. These are the programming equivalent of a flamethrower... [But] creative use of features such as goto, multiple inheritance, eval, and recursion may be just the right solution for experienced developers when used in the right situation. Is it time to resurrect these four forgotten code constructs?
The article notes that the Linux kernel uses goto statements, and links to Linus Torvalds' defense of them. ("Any if-statement is a goto. As are all structured loops...") And it points out that eval statements are supported by JavaScript, Python, PHP, and Ruby. But when the article describes recursion as "more forgotten than forbidden," it begs the inevitable question. Are you using these "forgotten code constructs" -- and should you be?
The article notes that the Linux kernel uses goto statements, and links to Linus Torvalds' defense of them. ("Any if-statement is a goto. As are all structured loops...") And it points out that eval statements are supported by JavaScript, Python, PHP, and Ruby. But when the article describes recursion as "more forgotten than forbidden," it begs the inevitable question. Are you using these "forgotten code constructs" -- and should you be?
Honest question: Am I not supposed to use recursion? Am I missing something?
Recursion is forgotten? More like essential, if you want to write readable code for some things like traversal. Multiple inheritance is also nowhere near the evil that Sun wanted you to think it was when they designed Java. Eval can be necessary in some shitty systems or circumstances, but we all know that it's naughty and should expect the spanking if we try to use it.
But goto...? It's 2017, if your coworker inserts a goto into your codebase you should just resign now. If you're using gotos yourself then you might be more cut out for manual labor.
Just use the one with the more clearer intent, unless it really affects performance.
Just keep in mind that familiarity is not the same as clarity.
Those who do not learn from commit history are doomed to regress it.
I seriously do not think I have ever worked on a code base of any significance that did not involve recursion.
Little toy programs, sure.
Or you can simulate it with a stack and a loop, but that's just recursion wearing a different hat.
For the last few years I've been on a crusade. If your operating system's kernel contains anything other than architecture specific assembler, then someone has made a broken abstraction in your operating system and should probably scrap the whole thing. Portable kernels are a real plague in computing these days, it encourages way too much to be lumped into the kernel so that we have fat bloated abstractions that collapse under their own weight when any new concern must be supported.
A kernel must sit at the lowest level, and provide enough of an abstraction for portable code to run on top but not necessarily provide every imaginable construct or service. What is important to remember is that no bit of high level code is an island, an operating system is a collection of services, libraries and programs that interact. Once you have a base kernel that permits binaries to run more than just the developer's own computer, you have an abstraction. From there you can extend the system, even if the extensions have bits of platform specific code, they primarily interact through the already established platform neutral interfaces and services.
Not sure about the idea of recursion being forgotten, but I used to work on a system (GEC 4000 series) which had no stack, making recursion slightly more difficult. The neat way of achieving it was by using a goto, back to the beginning of the function.
Recursion is an easy way to implement solutions to a number of problems. But if you don't have a clearly finite depth then it can be dangerous. There is often a way to use a loop that doesn't pile on the stack the way recursion can.
That said, it doesn't seem like it belongs in this list.
Frankly, it doesn't seem like a great article. Yup, those things can be misused. Yup, if something can be misused, it will be. I use ruby, so I have access to at least 3/4 of these dark techs. Whatever.
In recursive routines "goto" doesn't increase stack usage because you're not calling the parent function a bunch of times.
I'm not sure what the issue is here, other than someone's sense of aesthetics --
This seems like something from Pascal (or a professor) where there "should be" only be one exit point per function (says who??), and every function ends up looking the letter "V" with the point facing to the right (and subsequently runs past the 80th column in your editor).
What am I missing?
"Creative" and "used with care by experienced programmer" are two things that don't work out very well in software that has to be maintained by less experienced developers. Of course creative solutions that are self evident in retrospec are not bad, as the difficult part is finding the solution. But creative solutions that require experience to understand what how they work mean it is possible for someone to break or make changes without understanding the consequences. They can still be used if necessary, if the gain in performance out weighs any of the consequences. But for most projects, robustness, prevention of bugs, and security far out weigh minor performance gains (getting the algorithm right will get you most of the way there for most projects, even without gotos and eval).
recursion is very elegant but I see how it could be used unnecessary.
As a laymen I see the following problem:
You write an algo, but you never expected it to be run a trillion times. In a loop this becomes a time problem. With a recursive algo this also may become a RAM problem.
It depends.
In some cases, you want to allow goto statements, for instance because they help manage failure handling without adding condition or exception constructs.
In some case, you want none of these gotos, because you are using processes or tools which are (partly or entirely) not compatible with them, and you need these tools to work more than you need gotos.
In some cases, you don't want recursivity because the contex does not favor them (think embedded SW with restricted stack size).
In some cases you want recursion because it makes code simpler and closer to the principles behind it, thus more maintainable.
In some cases, you want class-like constructs in C be don't want C++ because the legacy code, people involved, time alloted, or general context just does not allow you to rewrite the whole thing.
Etc.
It's only the first thing you learn to do in SICP.
Do what you want with your GOTOs, but do not bring ALTER back.
Somebody will publish a paper entitled: "Class statement considered harmful." and he will be applauded as the new IT guru!
10 Everything Has Its Purpose
20 Goto 10
The article talks about 4 features: goto, eval (run code from a string), multiple inheritance, and recursion. It discusses why the 4 get attacked by simplicity advocates:
goto -- incomprehensible logic in programs
eval -- security risks
multiple inheritance -- breaks single responsibility since one module can have subtle impacts on how other modules acts in this context
recursion -- article isn't clear though the comments above are mostly correct. In non-tail recursive languages recursion usually creates algorithms that are O(n) in memory. Even in tail recursive languages this can happen (and in fact in those languages because more complex recursions are encourages O(n^2) isn't uncommon when recursion isn't used carefully / well understood).
It then mentions that these things should be used to avoid complexity in certain situations.
goto -- error handling
multiple inheritance -- is generally too useful to give up. implement with interfaces and be careful
eval -- JSON, HTML, math...
recursion -- trees, some list algorithms... recommend to implement imperative style mostly though (article assumes the language can't handle recursion)
Now my opinion:
Recursion is obviously the best understood of the 4. It is easily provable that there exists recursive algorithms which are both important and are not implementable as loops. Recursion classification is a still active research problem. Most imperative programers don't even bother to think deeply about their algorithms and not using design patterns from recursive features means the same bugs are introduced over and over again in code. IMHO there is no reason not to be abstracting loops away using built in functional design patterns in code.
Multiple inheritance is too powerful to give up. Java was wrong here. Better safety than the C++ style seems to be needed though. For OO languages this should be an active area of experimentation.
goto is today rarely used and when it is it often avoids complexity. I think we hit the right level of compromise here decades ago and this is a dead issue.
Eval I think history has shown that without explicit evals developers end up having to create implicit evals where the code acts in complex ways on input. The code / data duality is not dead. Complex evaluation of input and layering aren't going away. Perl's concept of taint checking is likely the best approach: make it explicit and let the compiler check for accidental security risks.
People who hate goto but love exceptions drive me up the wall. You hate the cute little goto with the well defined label you can instantly fly to in any IDE from vi on up, but you love the goto that jumps across any number of functions and puts you in some code (std:terminate if you fucked up!) ? And don't forget that in C++ (and others I think), you aren't even sure if a given function can throw an exception except by inspection.
Worst. Flow control. Evar.
Eval has been part of the Lisp programming language since the dawn of computing, but few experienced Lisp programmers ever used it outside of read-eval-print loops and software development tools (where its existence is a totally awesome gift). Eval is widely considered a beginner's trap. It is rarely "the right thing," typically because it is super inefficient. But my biggest concern is that it is a huge security hole, especially when the expression to be evaluated comes from the user. Think SQL injection attack. It is wise to require some sort of security credentials and/or filters to make sure the expression is within a "safe subset" of the language... at which point you might as well write the mini-interpreter you really need, instead of making the entire, underlying language and the application's entire data environment available for anyone to use or misuse.
Any "dirty" style should be avoided as a rule and applied with good reasoning.
My guilty pleasure is to return multiple return statements. The reasoning behind this is that it sometimes makes code better readable. Less nesting/fewer methods. Translating "avoid multiple return statements" to loops would result in avoiding break and continue statements.
If one's self-critical enough, code will be cleaned up eventually. Cleanly avoiding multiple return statements -and too many avoiding break and continue statements- usually comes with a breakthrough in the functional decomposition of the problem at hand.
I hadn't the slightest objection to his spending his time planning massacres for the bourgeoisie... (P.G. Wodehouse)
"Are you using these "forgotten code constructs" -- and should you be?">
I honestly think sometimes these newgen coders are just crazy ass stupid. Forgotten constructs meaning recursion, multiple inheritance or evals? Seriously? Flamethrower? And this written by some "senior" developer? You just have to stop wondering why lots of current software are so idioticlally written, since if there are more than one of such ignorant preachers behind them, instead of some real programmers, it's a real wonder how any software of value can be produced anywehere.
It's bad enough such people are part of the sw industry at all, letting them spread their wisdom is a disgrace to everyone invlved.
I am putting myself to the fullest possible use, which is all I can think that any conscious entity can ever hope to do.
goto is perfectly fine and should be used as often as you need it. It will cause a branch/jump just as you expect it to.
But when the article describes recursion as "more forgotten than forbidden," it begs the inevitable question.
No, it does not.
Pretty sure you're doing a lot better than the jackass who came up with the phrase "Forgotten code construct".
Recursion is either so easy it is more of a trick than a useful tool, or it is more complex in which case it is generally much harder to get the terminal conditions right then it is to avoid recursion.
I remember when I learned C, digging through K&R to find how to do goto. I also remember learning ruby and not even thinking about whether or not goto was supported. It really isn't needed and as Dikstra pointed out is mostly used for creating sloppy code.
it depends on the system environment , if your writing code to be used by a multi-thread o/s environment where use is discuraged as fearlessly as when you writing stand alone code for a embeded stand alone system where use of such can be expected , the other is the use of global vars
> at which point you might as well write the mini-interpreter you really need, instead of making the entire,
Greenspun's tenth rule:
Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Sometimes code has to do multiple things and if any of those things fail, the whole lot has to be cleaned up. Using a "goto cleanup" is usually a lot more preferable way of cleaning up that nesting the code in conditionals rendering it unreadable or duplicating the cleanup in several places.
There is no such thing as an inherently bad code construct. Sometimes it comes down to picking the solution that is least bad, buta good solution is always contextual. Unfortunately, some people believe in a 'one true way' of programming and will compulsively repeat the same algorithm/syntax/language every, single time. Not only that, they often vocally (and not very politely) tell everyone else they are wrong when they don't follow that pattern. This is not the same as being aware of the pitfalls of a given solution. Awareness allows you to weight up the pros and cons effectively –but even then, there may be multiple solutions that would be roughly equal.
Just out of curiosity, checking against a codebase that is generally of very high quality...
$ find go/src -name \*.go -exec grep goto {} \; | wc -l
793
Then again, they are mostly found in compiler and networking code, and not the packages. They are useful, and can be used well to make code clearer and faster. Honestly, I'd like to hear the experiences of someone implementing such code that doesn't use goto. Anyone?
Linus' argument leans on the readability of the code.
Do we _need_ goto to produce readable code?
No, in fact it tends to produce less readable code (depending on the skills of the programmer)
There is a good reason we are forgetting these contructs ; (and that goes for eval doubly so) they encourage bad ideas.
We don't need them, and they're dangerous.
I had a CS Prof in 1991 that would take points off assigments if one used GOTO and aven rejected them.
When writing C code where exceptions are not available, I'll often use goto statements to perform error handling. Its useful because you only have to write any necessary memory de-allocation code once. Its pretty easy to simulate a try/finally block using them and its a way better and more readable way to write that sort of code then the other alternatives you have in C. All that said, I prefer exceptions, they more or less get the same job done, its easier to nest them, and IMHO are a bit more readable than the goto method. Only downside of exceptions is the execution overhead is often very bad compared to a goto.
eval is very, very, very useful.
The problem with eval() is that it's incredible power can't be used safely in most languages, as whatever you eval() becomes part of the main program. If you could eval() code in a sandbox and apply memory and CPU cycle limits to it, it would be very useful, but most languages don't have such features making eval() little more than a dangerous toy that sometimes becomes useful for debugging.
As for goto, the Linux kernel is full of it and quite readable because of it. But outside of C you tend to have other mechanism to deal with resource cleanup that make it no longer necessary (RAII in C++, 'with' statements in Python).
Recursion again depends heavily on the details. In Python you quickly run into stack overflows when you use recursion to much (1000 is the default), so it's best avoided. In Haskell or Scheme it's the normal way of doing things, thanks to tail recursion support.
Multi-inheritance I never found very useful, even single-inheritance is rarely a good solution and you are better of doing composition most of the time.
Recursion has never been dead.
Tail recursion is an essential part of all modern efficiency in compilation.
Many languages, especially functional languages, cannot really even begin without recursion.
If you haven't got what I am saying, read this comment from the beginning.
This comment was written with the intention to opt out of advertising.
In my opinion there are valid uses of GOTO where it could be used, but nowhere else, especially in C.
Modern languages should preferably have constructs especially for those cases so that you wouldn't have to use GOTO, but many languages don't.
The article already mentions error handling in languages without RAII or exceptions, where resources have to be deallocated. Note also that not all resources are objects on the heap: The function may need to close a file or a network connection etc.
I find goto most useful for for breaking out of loops. Many languages have constructs especially for breaking out of nested loops to a scope that encloses the enclosing loop but there is one class of loop that is rarely supported: Loops for looking up an item in a data structure, you want to take a different code paths for when an item has been found to when the loop has run its course without a result. ... break ... else: ..., where the else-block is taken only if you did not break out of the loop.
The only major language I know of that supports this with its own construct is Python in the form: for:
In languages that allow nested break statements you could emulate that by enclosing the loop within an outer loop and breaking out of that but IMHO that would be even less readable than using GOTOs.
"We mustn't be caught by surprise by our own advancing technology" -- Aldous Huxley
Function APKMacLikeTrashCan(ProcName: ShortString): SmallInt; register;
var
APKTest42: ShortInt;
Label
WeOuttie;
begin
APKSizeCheck();
Screen.Cursor:=crHourGlass;
if ProcName = 'ConvertAndFilter' then
begin
APKTest42:= APKFileScrub(Trim('tempfile.txt'));
end;
GoTo WeOuttie;
if ProcName = 'BuildFinalHostsFileClick' then
begin
APKTest42:= APKFileScrub(Trim('tempfile.txt'));
GoTo WeOuttie;
WeOuttie:
Screen.Cursor:=crDefault;
Application.ProcessMessages;
Result:=1;
end;
* No "spaghetti code" going up & backward - Linus Torvalds IS correct, goto IS ok!
APK
P.S.=> Take a look @ that - only goes "down & out" 1 direction (another example is Microsoft's use of it in errtrapping in VBScript/VB too) - as long as you don't make your code go jumping back up & then DOWN again using goto? It only goes 1 way based on conditions (down to the EXITING label)... apk
It is rarely "the right thing," typically because it is super inefficient.
In compiling implementations of programming languages, it's anything but inefficient.
But my biggest concern is that it is a huge security hole, especially when the expression to be evaluated comes from the user.
One of my favourite quotes in programming: "If you don't want to do something, just don't do that."
at which point you might as well write the mini-interpreter you really need,
Will you also rewrite the compiler, and the memory manager, and write a set of interfaces between your two separate kingdoms?
Ezekiel 23:20
"Of course, in stupid languages like Pascal, where labels cannot be descriptive, goto's can be bad. But that's not the fault of the goto, that's the braindamage of the language designer. Linus"
- by Linus Torvalds from http://koblents.com/Ches/Links/Month-Mar-2013/20-Using-Goto-in-Linux-Kernel-Code/
Mr. Torvalds, @ least Pascal/Object Pascal/Delphi, what I used in my code example in the post I replied to here, have length built-in to their strings, unlike null-terminated C you use (that creates exploitable code possibles & YOU USE IT IN A KERNEL of an OS)!
* Also - I could also make my label in my post I replied (with a partial excerpt, edited out some bulk to fit in here, from a program of mine for file cleanups based on what's going on so it can do repeated runs & NOT buildup excess bulk in files it works on & @ shutdown so those files are also cleared to not take space on disk as they are temporary only) to MUCH longer & more descriptive than I did IF I wanted but it describes exactly what I said - 1 way out 1 direction clean!
APK
P.S.=> @ least we agree on GoTo being ok... apk
Eval, properly used, can be extremely valuable and reduce a lot of code bulk. Improperly used it presents various nightmare scenarios for security and stability.
In reality though, I don't see much more than a slightly nuanced difference between using eval and dynamically constructing any portion of a SQL statement for execution at runtime.
Use it, but use it carefully.
Suppose you were an idiot. And suppose you were a member of congress. But then I repeat myself. -- Mark Twain
Goto: A way to enable lousy programmers to write impenetrable code. Are there extremely unusual circumstances, where a superstar might use a Goto in a good way? Yes, but the price - encouraging use by the incompetent - is not worth it.
Multiple inheritance: Middle ground. In a few circumstances useful, but the conceptual complexity is too high for many programmers. On the other hand, those will not be the ones designing your architecture. Mixed feelings about this one.
Recursion: Many algorithms can be implemented more cleanly with recursion than with iteration. If recursion were better supported, it would be more widely used. Unfortunately, the most widely used languages have poor implementations (C# and Java, to name two), making recursion horribly inefficient. Optimizing for tail-recursion is not hard (Scala does it on the JVM), so it's weird that this isn't done in all modern languages.
Enjoy life! This is not a dress rehearsal.
Most language compilers are using goto constructs under the covers already. Disassemble some MSIL, for example, and you'll see how there are some in every single if-else or switch-case block.
I'm tired of this mentality. I'd rather we favored those with skill rather than those with a lack of it. We as a people would go much farther.
Do you cripple the use of bicycles by forcing everyone to ride with training wheels? Or do we in fact favor those who can ride and instead burden the new-comer with the difficulties around obtaining and installing training wheels on very poor low-end bicycles?
Why should coding be any different? Sometimes people craft very complex and difficult pieces of software that tie together more than 20 libraries all which have their own quirks. I need the ability to share raw pointers, I need the ability to avoid ref-counting or shared_ptrs. I need to sometimes work with systems that have their own scheduler (Erlang, cough) and then bind C libraries into that ecosystem which doesn't allow blocking for more than 1ms. So I need crazy thread logic sometimes and odd code to support linking two separate mutex idioms from 2 different libraries so the lock works across the boundry....
Sometimes I just wish to be left alone in a complex space where another soul's mere presence is essentially proof of their abilities and understanding of logic. Similar to how adults sometimes wish to leave behind children and mingle only with other mature adults, I desire this of a programming language. Something to scare away all the posers and poor misguided (but righteous and well meaning) individuals.... It's not elitist thinking just like Adults aren't really being rude when trying to mingle with other like-minded adults.... it's more of a time-saver for people who find that many "adults" are actually children in disguise and only after 30 minutes of talking can you determine they are fake. I grow tired of wasting my time and eventually wish to move to a place where it's harder for the fake to blend in. It was amazing going from finding 1-2 good people every 50 to instead finding 1-2 good every 10.
For me that language has been C++ and simply put it's the most amazing thing I've ever discovered in my programming career. I also love how everyone still is scared to death of it and clamoring for it's deprecation while simultaneously using programs written in C++ to post these complaints.
If you think a certain construct is somehow inherently bad, you are a shit coder. There is NOTHING wrong with goto, multiple inheritance, etc. They all have there uses. If you think they are bad. STOP. I deal with idiots like you all the god damned day, and I'm sick of it.
I think some time in the misty past (1970s?) recursion went through a fad phase, and it was hailed as the solution to every programming woe, not to mention the secret key to artificial intelligence. I can remember studying Logo (which is a variant of LISP) at one time. Logo composed every function call recursively: when it hit a key word that required arguments, then it would put that on hold and go looking for those arguments, some of which might be keywords that required their own arguments, etc. That's not unique among programming languages, but the syntax provided no clues or organization: no parenthesis, no brackets, no braces, just a string of words, and the only way to figure out which was an argument to what was if you already knew (or stopped to look up!) how many arguments each word takes. But supposedly you wouldn't need help reading it because it's recursive, and recursion is wonderful magic.
Incidentally, Forth suffered from a similar readability problem, but at least it executed way way way faster.
The other thing I remember about Logo and recursion was the textbooks and tutorials trying to teach me how every loop could be done using recursion -- and should be! Why would you do that? Because it's the Logo Way, of course. And because recursion is wonderful magic.
It was overly complex and inefficient, to be sure. However. . . I happily use recursion for actually recursive tasks, such as traversing various kinds of tree structures.
Here's a much better pattern for you, in a c-ish form:
How to manage iffy resources in a structured manner
You can generalize that pattern into almost any situation and it will work well. If you need details, then instead of true/false, pass can be a value or a bit mask, etc. and then the check at the end can be verbose about what exactly went wrong. Essentially still the same pattern.
I've fallen off your lawn, and I can't get up.
You do realize there are CPUs with user stack pointer(s), right?
You do realize there are cases where the recursion is limited in depth based on the nature of the problem, rather than the size of the data, right?
You do realize there are languages where the amount of data involved in the levels of the recursion can be precisely controlled, right?
It's not as black and white as "recursion bad, mkay?"
I've fallen off your lawn, and I can't get up.
To understand the revulsion some hold toward GOTO, you have to mentally turn back the clock to a time when it was used for almost everything. Back in the wild west days of computering, there were no conventions for organizing program code. There was no Structured Programming. Early languages provided simple branching tools (like IF-GOTO) but no guidance. A good programmer would soon figure out his own way of organizing his code, and he could become quite productive. The problem was, everyone had their own individual, eccentric methods, and looking at somebody else's code was often confusing. Then structured programming came along, and it provided (or some might say imposed at sword point) a common organizational methodology and a common vocabulary. Two programmers who were trained in the doctrine of structured programming could read one another's code much more easily.
If you see the keywords and indentation of a WHILE-REPEAT loop, or a REPEAT-UNTIL loop, or an IF-THEN-ELSE condition, then you already have a clue, you already have a starting point to understand what the code is doing. If you see GOTO, then it communicates almost nothing. Then you have to look at the context. There may also be some code comments. It may not be a problem, and in today's environments there's no reason why it should be. This isn't the wild west anymore, and we don't use GOTO for everything. If it's there, somebody presumably had some reason for it.
regularly. About every other month. It's kind of eerie how reglarly it happens that I'm saying 'this is an A and a B with some new stuff but I can't do this can I" to my java IDE.
See my subject: Missing an "end" after 2nd block (not really needed though, can just use then & statement) & not using APKTest42 var (I do both in my real code that's yet to have a bug or security issue found in it 4++ yrs. after public release & it's proven safe/clean by many security pros on audit + antivirus tests) using an IF statement in those blocks to spit an abend/error IF it doesn't return 1/clean (vs. 0 dirty/bad).
Plus, some indenting took a beating on paste (I just posted what my code has outta the Delphi XE4 IDE - /. seems to disagree on it).
APK
P.S.=> Yes, I just HAVE to do that, or I'd never hear the end of it from 'nitpickers' & trolls - this IS the price of editing on Slashdot (especially for us AC posters - our postlength is SEVERELY limited vs. registered 'lusers') & yes, the price of my being in a hurry this a.m. too admittedly (I should've used preview 1st)... apk
Same for MATLAB. In fact, the MATLAB documentation indicates that eval should be avoided.
In what way does it beg the question?
I can sum this up as, "ONLY A SITH DEALS IN ABSOLUTES HURRRRR!"
It's a stupid nerdy soundbyte that doesn't even make fucking sense at face value. Just like, "RECURSION IS BAD HURR".
Recursion isn't what allocates the stack frames. Limited compiler translate the recrsive algorithm into code that allocates the stack frames. I'm not sure, but I suspect that there is plenty of room for compiler optimization compared to what they are doing now, but I am not certain that they are still doing as you say they are.
And what are you doing with that stack if still not recursion. You don't just magically make it 'not recursion' by implementing one element that you didn't even change the name of differently
(And I'm not saying 'goto' is a bad thing. Using it to uncreatively break out of multiple nested loops or do error handling is easier to understand than the alternative. Also, in about every programming language, there are pretty much always several ways to achieve a certain behavior. The one that is easiest to understand should be chosen unless there are pressing reasons for one of the other ways.)
Disregard my rant about maintainability if you code one-shot things that no one - including you - will look at again once you're done.
Internal company politics, probably.
I don't get why people defend the usage of GOTOs with claws and teeth.
In modern languages, and that includes C, the question if to use or not to use a GOTO never was an issue!
The phrase 'GOTO considered harmful' comes from times where the predominate language was FORTRAN!!!
In FORTRAN you easily see a 1000 lines program with 1 or 2 dozens of GOTOs, where the same program written in proper C or Pascal (and any other modern language) has zero.
Furthermore, the example of 'good use of goto' given in the linked article is bollocks. I would fire (or educate) a programmer giving me code like that.
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
Using your own stack doesn't make it 'not recursion'.
But the entire modern GUI API is based on "event driven" programming. Replete with "OnRightButtonDown()" , "OnWindowClose()" ... . These are nothing but COMEFROM statements. COMEFROM could be as harmful or even more harmful than GOTO. With a good design based on a valid state machines and object oriented code we not only handle these with east, we are successfully developing incredibly complex code.
So, no. We did not forget GOTO just because some authority figure railed against it. We replaced it with a better concepts like event loop, event dispatching, object orientation.
sed -e 's/Chuck Norris/Rajnikant/g' joke > fact
And, after this, we'll see and article titled "Developer argues for unilateral editor switch" that ends in "it begs the inevitable question. Are you ditching emacs in favor of vi -- and should you be?"
"It's sometimes better to light up a flame thrower than to silently curse in the dark"
> Without recursion, data structures like trees or graphs are useless. Kind of a problem if you have a directory tree structure, or you want to follows URL links for web scraping.
I would say web scraping is a good example of when you should NOT use a recursive function. My first web scraper looked like this:
scrape_page(url) {
links = get_links(url);
foreach(link in links) {
scrape_page(link);
}
}
On a site like Slashdot, which has many pages (on for eaxh comment posted), that fails wonderfully.
My i improved version, which has been running successfully for many years, looks like this:
scrape_page(url) {
links = get_links(url);
foreach(link in links) {
add_to_queue_database(link);
}
}
Note scrape_page no longer calls itself, it's not a recursive function. It's driven by this main() function:
while (url = get_url_from_queue_database() ) {
scrape_page(url);
}
The queue is held in a database which can easily store hundreds of thousands or millions of urls, There is no recursive function and it doesn't die when spidering a forum. It also doesn't lose all its queue when stopped and restarted.
Some people would point out this solves a recursive and say it's therefore a recursive algorithm. The algorithm isn't recursive by definition because no function calls itself, even indirectly.
Good programmers may not be commonplace and that in itself might be to reason to ban freedom and make everything as constrained and idiot proof as possible... I'm not one of those people... I am for formal language "modes" where you can specify your skill level. Then GOTOs can be forbidden except for those who have an approved skill level.
As we should all know, loop and if constructs are merely specialized use cases for GOTO. Not every situation has been abstracted into a safe language construct. C lacks the ability to break out of nested loops, same with C++. One has to work around the language constraints adding complexity and performance loss. Now adding an argument maybe a label could have provided another abstraction for another GOTO use case for specifying the loop to jump to.
Exception handling is another abstraction use case for GOTO. Along with the "come from" event model we use which is especially awkward in async and parallel situations. These continue to have poor abstractions that add complication trying to do things that a GOTO or an evolved GOTO would address. I'm saying we should leave power tools around for those who know how to use them so that better abstractions can be created for the lesser programmers. Exceptions being a great example which really couldn't be done without an evolved GOTO but behind the scenes language programmers did all the difficult work. For async event callback coding we currently have kind of a mess everywhere with Java 8 being an impressive hack but only to bring it into the same mess.
How about call back closure situations where you flip the situation and have the code block until the async completes but it jumps to a function or up the call stack for the idle running code. Quite often you have the a where you just want to spawn async tasks and the response to the async task belongs right next to it (hence the popularity of closures.) So now you don't have closures, it just blocks and life is easier "the fork in execution" instead is jumping to the idle code instead of jumping to the callback code. Do I make myself clear? The OS does this already for processes-- your process blocks without any concerns and the OS shifts processing elsewhere.
Democracy Now! - uncensored, anti-establishment news
Developers who speak out against GOTO, show their amateur or assumed armature level or experience. `I had a prof in University who spoke out strongly against it, even going to lengths such as costly loops and costly checks to get around using a simple GOTO, his code was made worse because he refused to use it. If you honestly feel GOTO is an unexceptionable statement in programming, then please write a program in ASM and NEVER use JMP!
That's a bit subjective. I think I would call it an alternative pattern.
I have seen it work well but more often than not I have seen create code that looks like an arrow. In real life, outside of trivial snippets, arrow code can be horrible to read and maintain. That's not always the case though hence I don't say it's a "worse" pattern - just an alternative.
Maybe if you use some new hipster frameworks. Real programmers know their constructs and when to use them.
By your criteria, Python is the wrong language, and this is intentional. See Guido van Rossum's explanation: part 1 and part 2.
Eval is not something to be used casually, but it's vital to how some languages work. My general rule is that, if you use it while writing application code, it's probably a mistake, but it can be very useful in libraries to do things and the like. Much like C++ template metaprogramming: very powerful, and most people should leave it for someone else to write.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
I'd say that's still recursive however with a buffer.
Map the data flow and you should see why I think it's still recursive...
Some people would point out this solves a recursive and say it's therefore a recursive algorithm. The algorithm isn't recursive by definition because no function calls itself, even indirectly.
The algorithm is still recursive; the recursion is just built in to the language in the form of predefined iteration constructs like "foreach" and "while". Any time you have an iterative program of the form "first do X, and then repeat the process for the remaining input", that's recursion: one of the steps in the process (the repetition) is defined in terms of the process itself.
Some recursive algorithms can run in constant space, while others (such as most operations on trees) inherently require more space to process larger inputs—even when implemented "iteratively". The space used does not depend on whether the recursion is implemented with explicit function calls or loops, but rather on the quality of the compiler, including, but not limited to, tail-call optimization. Far from being complex wizardry, this optimization is really just the correction of a common implementation flaw wherein a function's stack frame remains allocated until the function returns, rather than being freed once it is no longer needed. This simplifies the compiler slightly at the cost of forcing the programmer to distinguish between implicit and explicit recursion, thus impairing the readability of naturally recursive algorithms and sparking endless debates over what ought to be a trivial difference of coding style with no runtime effect.
"The state is that great fiction by which everyone tries to live at the expense of everyone else." - Bastiat
It may be very well advisable for Matlab's eval, especially since Matlab itself is better avoided. In other languages, you can often use COMPILE instead of EVAL anyway. (Chances are that this is what you actually want to do in many cases.)
Ezekiel 23:20
People who adhere to taboos only restrict their view of what is possible, i call such people idiots.
Arguments might be avoided by saying it's not a recursive FUNCTION - no function calls itself, can't run out of stack space. It *is* a lot like what happens when you start with a tail-call recursion then remove(?) the recursion. If somebody wants to call it a recursive algorithm I feel no need to really argue about it, though I will ask you a question or two.
> Any time you have an iterative program of the form "first do X, and then repeat the process for the remaining input", that's recursion: one of the steps in the process (the repetition) is defined in terms of the process itself.
Care to point to which step "is defined in terms of the process itself"? I don't see that anywhere.
It's very similar to recursion, but my understanding of the definition of recursion is that the definition/implementation of X must reference X. I don't see that in my code anywhere.
What's your definition of recursion? My informal definition:
X is recursive if and only if X is defined / implemented by calling X, directly or indirectly.
One dictionary definition is similar:
A routine, function etc in which a part is defined in terms of the whole.
Just because the output of a function X(a) may be, at some later time, be passed to another seperate instance of X(b) doesn't make it recursive, imho, if X(a) TERMINATES before X(b) begins, because X() makes no reference to itself.
Here' s an alternative definition:
A recursive algorithm is defined as any algorithm which is recursive. ;)
In all cases, the answer is, it should be used sparingly, and after the question is asked "Is there a better way?".
Recursion, I use rarely-- but the last case was enumerating all the members of an active directory group. It was the cleanest way to implement the function (if user object, insert into hash. If group, call function again).
Similarly, while "goto" should be avoided as a general practice, sometimes, it's the only way to get your code where it needs to go-- but it should always be regarded with suspicion.
"eval" is a fantastically useful function, but you need heavy validation on anything being handed to it, unless you want eval to be fantastically useful for hackers, or you enjoy debugging bizarre code behavior.
As an Erlang programmer I endorse recursion
Goto does ease redability of complex logical jumps .. and would still be a prefer choice.
Recursion bit difficult(or impossible) to replace -- it may change in syntax, and may be called something else, but at core recursion does exist -- whether in parser, far known leaf node traversal, email parts parsing etc etc....
They both are neat and clean ... I always support them, and use them as and when necessary.
In days of yore, it was said that Niklaus Wirth, inventor of Pascal and GoTo hater supremo, had to use a single GoTo in his famous compiler found in old AppleII's with the Language Card.
Fact or Fiction?
A chainsaw don't know the difference between a log and a leg. But sometimes it's the right tool.
http://web.archive.org/web/200...
What most people miss on Y2K is that given the constraints at the time most of those programs were written, the cost of avoiding the problem would have been roughly triple (in real terms, discounted dollars, etc.) as ultimately fixing it was.
Yes, economists went back and actually measured, estimated, etc. Also, most of the code from the era did not, in fact, make it to Y2K, and would have incurred the costs without receiving a benefit.
Although the resources would be negligible today, we are talking about the extra labor to punch two more digits on each and every 72 column card, the limited number of digits on those punch cards, memory for which it was a break thought when the *rental* price dropped down to a buck a byte a month, the extra processing time to handle three or four digits, etc.
It also didn't help that so many (a strong majority?) coded in the year ending in 00 exception to leap years without including the divisible by 400 exception to the exception . . . other attempts to accommodate 2000 might have had similar blunders.
hawk
It is rarely "the right thing," typically because it is super inefficient.
In compiling implementations of programming languages, it's anything but inefficient.
More and more platforms have begun to establish Data Execution Prevention (W^X) policies that give third-party applications no way to flip a page from writable to executable. These include iOS, Windows Runtime, and current-generation game consoles and gaming handhelds. Thus all compilation to native code must be either done ahead of time before packaging or done by an operating system component, such as WebKit JavaScript in iOS.
Decades ago there was a programming language called Algol. It was the basis for the Burroughs computers. Look up in a history book. But Edgar Dijkstra ran much of the Burroughs corporate research and one of his papers (buried somewhere one of my in those vertical file cabinets) was a look at how recursion was actually used in production programs. It turned out that five levels of nesting was enough to handle 95+ percent of actual code. That is really nothing! So I would not feel bad about using recursion. If you want to have some fun, code the Ackerman function and try running it :-)
(Exclude recursion here - i think he talks about the examples given in school for recursion, which often are not good in terms of resource usage)
About the rest: Bullshit. These are not forgotten, and they are forbidden in most coding guidelines for a reason. His musing about that goto is only bad if you jump "backwards" is borderline funny.
There is a reason that goto should only be used in extremely rare occasions (and i would agree that some of these *may* be for kernel programming). The examples of this "programmer" are excellent examples why for people like him these should be forbidden. Any of his examples are better off without goto, and if you don't see a more efficient, clearer and safer solution without his shit, you should not program any high-level language.
Eval is another piece of cake (I exclude python here because it seems to get eval right). My practical experience is that i is rather overused by inexperienced programmers. Filtering a string that should be evaled in a way that it has a predictable outcome is as difficult as writing an eval. Typically my recommendation would be that evals need to be a) sanctioned by a senior developer (just to make sure nobody uses these out of pure incompetence) b) encapsulated in a function (e.g. for the dection of a laguange feature/level) c) filtered carefully if they accept user input.
...it "raises" it.
GOTO was kinda special...It was to either completely exit a procedure when the input conditions were just TOO F'd up.
Quite often the jump was to the equivalent of a "Halt and Catch Fire" sorta error processing.
GOD, I LOVED recursion. Of course, I slung a boat load of LISP code in my younger days.
Some would say this is bad programming, but if you are programming a pacemaker, heart monitor or the like, an infinite loop is EXACTLY the construct you want. I've written some pretty ugly code (breaking out of deeply nested loops) when a simple GOTO would have cleaned things up a lot. They are all tools that have a place and time to be used.
Care to point to which step "is defined in terms of the process itself"? I don't see that anywhere.
It's part of the compiler's internal definition of the loop construct, and does not appear in the source code. In the object code it can be identified as the opcode that branches back to the beginning of the loop.
This becomes more obvious if you consider programming languages where iteration is not a built-in construct, and loops are written in terms of explicit recursion. In Haskell, for example, a simplified C "while" keyword (disregarding "break", "continue", etc.) could be implemented as the following recursive function:
Or using the fixpoint operator, without referencing the current definition by name:
Building on the fixpoint example above, would you call this a recursive function?
Sometimes it isn't so simple. The body of f() does not refer to f(). However, what happens if you call f(f, 0)? The function might be recursive, or it might not be, depending on the arguments. It all depends on that function pointer.
Arguments might be avoided by saying it's not a recursive FUNCTION - no function calls itself, can't run out of stack space.
It's not a recursive function, since it's not the entire function that's repeated. It would be more accurate to say that it contains a recursive function, though that inner function is hidden behind the loop construct. Barring the use of alloca() or similar it won't run out of space on the function-call stack, since alloca() / variable-length arrays and function calls are the only ways to allocate from the function-call stack, but the iterative version of the function still needs to track the same data and can still run out of memory given large enough inputs—just not stack memory. Sometimes that distinction is important, but more often it isn't, especially given the stack sizes available to programs running on most virtual memory based operating systems, ranging from several megabytes to "malloc() would fail first". (After all, some languages allocate stack frames from the heap.)
Anyway, the fact that (unoptimized) tail calls use stack space while iteration constructs ordinarily do not is nothing but an artifact of poor compiler implementation, and easily corrected through tail-call optimization. It's really no different from the way some languages with simplistic garbage-collection algorithms (e.g. Perl) fail to collect sets of objects with cyclic references. You can either take pains to avoid cyclic references (tail recursion), or just switch to one of the many modern languages which don't cause arbitrary runtime errors when you write code in the most natural style. Given a reasonable guarantee of tail-call optimization, function calls in general may allocate additional function-call stack space, but a call in tail position won't, so tail recursion is exactly the same as iteration in terms of expected stack use.
"The state is that great fiction by which everyone tries to live at the expense of everyone else." - Bastiat
> but the iterative version of the function still needs to track the same data
It seems to me that it does not. Looking at the actual state information of my program after running for three years appears to confirm that.
In a moment, you may wish to refer to my code posted above, but we'll start by talking about the clearly recursive version.
Some years ago, I pointed my spider at a large, deep directory of urls with many links. Imagine dmoz.org. Since then, it's followed links several million deep. Were it recursive (in the traditional sense), there would now be several million copies of the function *running*, each with a stack pointer to its parent. For the URL it's currently spidering, we'd have the full history of how it got there, the url that linked to it, all on the stack. Also any other variables local to the function would still be live in memory - millions of instances because millions of instances of the function are still running.
Using the iterative algorithm I posted above, I think you can see that we do NOT track any data whatsoever about how we got to the current URL. All of the information about past pages, which would be on the stack in a recursive algorithm, do not exist in my program. So in fact it does *not* "track the same data" and your idea that it "can still run out of memory" is disproven by the fact that it's been running for years, millions and millions of iterations, and it's still using the same 3MB of RAM that it started with, plus a few MB for the queue.
You are certainly correct that you *can* simulate iteration by using recursion. That doesn't mean you must.
The key point, to me, is that at least what I mean by "recursive" is that there is a parent and a child. The parent doesn't return/exit until its child does - and its child, and its child, and its child, and its child ...
With the iterative algorithm, one completes, and releases all of its memory, then another one starts. There is only one instance running at any given time, and therefore only one copy of the variables. There is no "parent", only "previous", which has no other relation to the currently running code - the previous URL isn't even the one that linked to the current url!
Using the iterative algorithm I posted above, I think you can see that we do NOT track any data whatsoever about how we got to the current URL. All of the information about past pages, which would be on the stack in a recursive algorithm, do not exist in my program. So in fact it does *not* "track the same data"...
You're comparing apples and oranges. The iterative version of your program is not using the same algorithm as the recursive version. You replaced a depth-first search with a breadth-first search, which makes your example completely irrelevant for comparing iterative vs. recursive implementations of the same algorithm. Among other things, the order in which your web crawler visits pages is very different in the iterative version. To maintain the original order you would need to restore the depth-first search algorithm, which would imply tracking the chain of pages you came from—exactly like the recursive implementation, though you could store that information on the heap or in a database rather than on the stack. (Note that a tail-recursive implementation would also store that information on the heap or in a database... this isn't a matter of recursion vs. iteration.)
You are certainly correct that you *can* simulate iteration by using recursion.
The tail-recursive implementation is not "simulating" iteration. It is iteration. Expressing the same algorithm with a loop construct rather than an explicit function call would not make it any less recursive; the loop implies a built-in fixpoint operator. If this trivial difference of style makes any difference in how much stack space your function uses, that's a bug in your toolchain.
The key point, to me, is that at least what I mean by "recursive" is that there is a parent and a child. The parent doesn't return/exit until its child does - and its child, and its child, and its child, and its child ...
That is not what "recursion" means. Moreover, how would that definition even account for something like tail-recursion, where the "parent" is replaced by the "child"? Logically, the "parent" still does not exit until the "child" does, but in the meantime it occupies no space on the stack and the return from the innermost "child" is also the return from all of the "parents". No matter how deep the tail-calls become, the stack use remains constant. Recursion only requires additional stack space when there is more for the function to do after the call, i.e. when it is not tail-recursion. Such cases cannot be converted to iteration (using the same algorithm) without shifting all the data required by the continuation from the function-call stack into some other data structure.
"The state is that great fiction by which everyone tries to live at the expense of everyone else." - Bastiat
When we started this discussion, that quote at the bottom of the Slashdot page was James Baldwin
--
Those who say it can't be done are usually interrupted by others doing it.
--
> The iterative version of your program is not using the same algorithm as the recursive version.
Okay so you acknowledge there are iterative algorithms, there are recursive algorithms, and not all iterative algorithms are recursive, right? That's an important point.
> You replaced a depth-first search with a breadth-first search
In fact it's (mostly) depth-first.
> the depth-first search algorithm would imply tracking the chain of pages you came from
Let's try a little experiment. Clear your browser history, then try clicking a link. Amazingly, you can go forward (in depth) without knowing your full history of where you came from! This isn't theoretical, as I said, I've spidered millions and millions of pages, by roughly following the first unvisited link on each page. If I were in the mood, I'd give you a core dump from my spider and you'd notice that nowhere in the core does it have the starting page from several years ago. (Unlike a recursive tree walk, which would have that data.) "But how do you get back, to follow another branch?", you may ask.
The first thing apparent is that once you've spidered a page, you're not going to spider the same page again, so you can forget all about it! There is no need to track all the parents. You need only remember some *unvisited* pages, none of the visited pages. So no, it's not correct to say "depth-first search algorithm would imply tracking the chain of pages you came from". Rather, you must track the head of the branch you did NOT come from. That'll be where you go next if you end up on http://hmpg.net/ (check out that page).
Further, we're discussing spidering the web, which is first a network, not tree. You seem to be thinking in terms of a tree. More importantly, the web is a real practical, thing, for which I gave an algorithm that's actually been at work for many years, as opposed to walking some theoretical tree. Because of the nature of the web, as a real network, you can discard 99.999% of the information which you would need for a complete walk of a theoretical tree. No need to remember *every* link you didn't visit, you'll never spider every page on the web anyway, and if you wanted to try you could always start back at dmoz.org or *any* large list of links. The network (the actual www) is such that there are many nodes (sites) which connect indirectly to ~every other available site. So the actual working algorithm doesn't know where it came from. What it *does* remember is a list of 1,0000 URLs it can go TO next. That's all it needs to know - where to go next, not where it came from. It ends up being depth-first because I made the list of "next URLs" a 1,000 element LIFO. I've also run it breadth-first by simply working the other end of the list, FIFO.
MySQL will not allow table names to be passed in as parameters to a stored procedure [1] [2] [3]. Without eval, there is no way to dynamically use a stored procedure to operate over multiple, different, tables without enumerating all the tables. The code needed for the "safe" approach is massively bigger, n * m, where m is the body of your stored procedure and n is the set of tables you want to operate over. It also requires such a massive degree of code duplication to make it significantly less safe.
We have a reporting system called MyDBR that requires everything to be done in MySQL or JavaScript. Likely, a stored procedure isn't the best answer for my problems, but it's better than JavaScript. Probably the "safest" answer is to throw away MyDBR and use an actual programming environment to accomplish what I'd like (sane reporting.)
The entire premise of this article seems odd to me. Who would forget these techniques, unless they never really learned them in the first place? And why would the author want to revisit them? I think because it was a slow news day.