digitalmars.D - Deep nesting vs early returns
- Andrei Alexandrescu (2/2) Oct 02 2018 Kate Gregory makes a good argument on something I've often commented in
- Jacob Carlborg (7/9) Oct 02 2018 Swift has the "guard" statement to help with early returns (and unwrap
- Faux Amis (5/15) Oct 02 2018 Is it:
- Jacob Carlborg (6/10) Oct 04 2018 The variable declared in the guard statement is available after
- Gopan (27/29) Oct 03 2018 Thank you Andrei for mentioning this. I always had this question
- Jack Applegame (4/19) Oct 04 2018 In terms of code maintenance, multiple break statements have no
- Jacob Carlborg (4/9) Oct 04 2018 D has the “scope” statement for this.
- Claude (52/53) Oct 05 2018 In C, I will layout my functions like that (that's especially
- Nick Treleaven (8/18) Oct 05 2018 I think `switch (0) default:` is better, because it's not a loop
- Patrick Schluter (14/32) Oct 05 2018 `do ... while (0);` and also the loop above are abominations.
- H. S. Teoh (20/48) Oct 05 2018 +1. D (and C/C++) has a 'goto' keyword for a reason. It's stupid to
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (5/7) Oct 05 2018 Yes it's ugly but it's also the only way to define a multi-statement mac...
- Trass3r (7/9) Oct 04 2018 Sean Parent covered early exits years ago in an excellent talk I
- Nick Sabalausky (Abscissa) (14/16) Oct 04 2018 I was in college during the height of the Java craze, so my instructors
- rikki cattermole (8/14) Oct 04 2018 "Terminology invoking "objects" and "oriented" in the modern sense of
- Nick Sabalausky (Abscissa) (10/27) Oct 05 2018 It's not *my* statement about newer/older. If you recall the programming...
- Paolo Invernizzi (8/21) Oct 05 2018 The hype was hight also in the 90...
- Patrick Schluter (13/34) Oct 06 2018 In the 90s I used to add the C preprocessor to other languages
- Paolo Invernizzi (4/18) Oct 06 2018 Fox, the speediest indexes in the country... what a time! :-P
- =?UTF-8?B?UsOpbXkgTW91w6t6YQ==?= (9/11) Oct 05 2018 I've found a good explanation for the reason behind nesting here:
- Jonathan M Davis (9/11) Oct 05 2018 It's one of those things that I would have thought would just be obvious
- Stanislav Blinov (3/8) Oct 08 2018 Sadly, one of these "folks" is DMD's inliner. Then again, returns
- Johan Engelen (9/11) Oct 09 2018 I very much like LLVM's practices, and one of them is it's Coding
Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682
Oct 02 2018
On 2018-10-02 20:14, Andrei Alexandrescu wrote:Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682Swift has the "guard" statement to help with early returns (and unwrap optionals). https://thatthinginswift.com/guard-statement-swift/ https://docs.swift.org/swift-book/ReferenceManual/Statements.html#ID524 -- /Jacob Carlborg
Oct 02 2018
On 2018-10-02 21:09, Jacob Carlborg wrote:On 2018-10-02 20:14, Andrei Alexandrescu wrote:Is it: For optional binding, Swift 2 has sugar for if let !( ){} Or am I missing the point of the guard statement?Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682Swift has the "guard" statement to help with early returns (and unwrap optionals). https://thatthinginswift.com/guard-statement-swift/ https://docs.swift.org/swift-book/ReferenceManual/Statements.html#ID524
Oct 02 2018
On Tuesday, 2 October 2018 at 20:53:40Is it: For optional binding, Swift 2 has sugar for if let !( ){} Or am I missing the point of the guard statement?The variable declared in the guard statement is available after the statement. It’s like an if statement without the then part, only an else. — /Jacob Carlborg
Oct 04 2018
On Tuesday, 2 October 2018 at 18:14:55 UTC, Andrei Alexandrescu wrote:Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682Thank you Andrei for mentioning this. I always had this question of which one to choose - early return or nesting. But the idea of 'early return' leads to multiple return statements too (for separate return conditions), right? Certain people recommend that there be only one return statement (usually at the end) from a function. The said advantage is that, in a maintenance code, if you later want to do something before returning, you can add it just above the return statement. I have seen people enclosing the function logic inside a while(1) merely to stick on to single return at the end. while(1) { ... break; //otherwise return would come here. ... break; } return ...; I (no expert) still don't have clear idea about the multiple return statements scattered inside a function. So, I return where ever I want to return. And, I use your C++ ScopeGuard to do that extra thing that is supposed to be done before returning ( from your article http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758 ) Any advices?
Oct 03 2018
On Thursday, 4 October 2018 at 06:43:02 UTC, Gopan wrote:Certain people recommend that there be only one return statement (usually at the end) from a function. The said advantage is that, in a maintenance code, if you later want to do something before returning, you can add it just above the return statement. I have seen people enclosing the function logic inside a while(1) merely to stick on to single return at the end. while(1) { ... break; //otherwise return would come here. ... break; } return ...;In terms of code maintenance, multiple break statements have no difference from multiple return statements. Those people are just trying to deceive themselves.
Oct 04 2018
On Thursday, 4 October 2018 at 06:43:02 UTC, Gopan wrote:Certain people recommend that there be only one return statement (usually at the end) from a function. The said advantage is that, in a maintenance code, if you later want to do something before returning, you can add it just above the return statement.D has the “scope” statement for this. — /Jacob Carlborg
Oct 04 2018
On Thursday, 4 October 2018 at 06:43:02 UTC, Gopan wrote:Any advices?In C, I will layout my functions like that (that's especially good for initializer functions): int init(struct my_handle *handle, ...) { if (handle == NULL) return -EINVAL; // direct return for parameter safety check if (other_arg > MY_MIN || other_arg > MY_MAX) return -EINVAL; // same here handle->data == calloc(1, SIZE); if (handle->data == NULL) return -ENOMEM; // return when memory allocation fails, // I assume my program won't recover anyway // create some system resource handle->fd = fopen("bla.txt", "w"); if (handle->fd == NULL) goto error; // goto error, because I have some cleanup to do // I will use goto's from now on every time I have // some action that return an error ... return 0; // Success error: close(handle->fd); // maybe check first fd is valid... free(handle->data); return -EFAIL; } My basic rules are: 1- Argument safety check before anything else, return straightaway. 2- If memory allocation fails, return (or assert), no cleanup, unless you expect to recover from it, so that is a resource allocation like described in the next point. 3- If some resource allocation fails, escape using a goto statement to final label. I call it "error" for simple cases, else "error_closepipe", "error_epoll" etc, depending on the resource I'm cleaning up. 4- error handling is after a successful "return 0;" which finalizes a nominal execution flow. 5- Maybe use some "int ret" to carry some error code at the end, and maybe log it, for easier debugging. 6- Don't use a return statement in a loop. Break from it: there could be some cleanup to do after the loop. As a side-note, one instruction is one operation, I don't like: if ((ret = handle->doSomeStuff(args)) < 0) goto error; I prefer: ret = handle->doSomestuff(args); if (ret < 0) goto error; It's especially annoying to read (and thus maintain) if some other conditions are tested in the if-statement.
Oct 05 2018
On Thursday, 4 October 2018 at 06:43:02 UTC, Gopan wrote:I have seen people enclosing the function logic inside a while(1) merely to stick on to single return at the end. while(1) { ... break; //otherwise return would come here. ... break; } return ...;I think `switch (0) default:` is better, because it's not a loop so the intent is clear - no continue statements somewhere below (so it's also better than `do ... while (0);`). Also you might forget the final break statement with `while` and get an infinite loop. This is an occasionally useful general pattern (I use early returns).
Oct 05 2018
On Friday, 5 October 2018 at 16:02:49 UTC, Nick Treleaven wrote:On Thursday, 4 October 2018 at 06:43:02 UTC, Gopan wrote:`do ... while (0);` and also the loop above are abominations. They are for goto hypocrits, i.e. people who think that a goto when it is not named goto but works exactly like a goto is something better (the Voldemort goto). The breaks above and in are GOTOS, no point in obfuscating them. Sorry if I'm a little bit inflammatory about these constructs, but I have to work with code written by a `do {} while(0)` champion and I can tell you, it's the horror when you have to make changes. It was so bad that I finally used __attribute__((__cleanup__(x))) gcc extension to implement something resembling D's scope(exit) so that I'm sure that the code doesn't leak like it did before.I have seen people enclosing the function logic inside a while(1) merely to stick on to single return at the end. while(1) { ... break; //otherwise return would come here. ... break; } return ...;I think `switch (0) default:` is better, because it's not a loop so the intent is clear - no continue statements somewhere below (so it's also better than `do ... while (0);`). Also you might forget the final break statement with `while` and get an infinite loop.
Oct 05 2018
On Fri, Oct 05, 2018 at 08:00:03PM +0000, Patrick Schluter via Digitalmars-d wrote:On Friday, 5 October 2018 at 16:02:49 UTC, Nick Treleaven wrote:[...]On Thursday, 4 October 2018 at 06:43:02 UTC, Gopan wrote:+1. D (and C/C++) has a 'goto' keyword for a reason. It's stupid to try to avoid it like a taboo keyword when it's what the code actually *means*. Just because you write it with "more acceptable" constructs does not change the fact that the code is essentially doing a goto. Not to mention the fact that modern-day `goto` statements are nowhere near as powerful (nor as evil) as the original goto, which was program-wide (and sometimes even more than that, before the days of protected mode and process isolation :-P), and could jump from anywhere to literally anywhere else, including the middle of another construct. Modern-day `goto`s are far tamer, and not overly hard to reason about provided you don't abuse them for stuff that could be better-written with other constructs.`do ... while (0);` and also the loop above are abominations. They are for goto hypocrits, i.e. people who think that a goto when it is not named goto but works exactly like a goto is something better (the Voldemort goto).while(1) { ... break; //otherwise return would come here. ... break; } return ...;I think `switch (0) default:` is better, because it's not a loop so the intent is clear - no continue statements somewhere below (so it's also better than `do ... while (0);`). Also you might forget the final break statement with `while` and get an infinite loop.The breaks above and in are GOTOS, no point in obfuscating them. Sorry if I'm a little bit inflammatory about these constructs, but I have to work with code written by a `do {} while(0)` champion and I can tell you, it's the horror when you have to make changes.I've had to work with code that had multiply-nested #define macros involving `do {} while(0)`, and have hated every minute of it.It was so bad that I finally used __attribute__((__cleanup__(x))) gcc extension to implement something resembling D's scope(exit) so that I'm sure that the code doesn't leak like it did before.Ouch. T -- "I suspect the best way to deal with procrastination is to put off the procrastination itself until later. I've been meaning to try this, but haven't gotten around to it yet. " -- swr
Oct 05 2018
H. S. Teoh <hsteoh quickfur.ath.cx> wrote:I've had to work with code that had multiply-nested #define macros involving `do {} while(0)`, and have hated every minute of it.Yes it's ugly but it's also the only way to define a multi-statement macro that can be used like a function. --- Tobias
Oct 05 2018
On Tuesday, 2 October 2018 at 18:14:55 UTC, Andrei Alexandrescu wrote:Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682Sean Parent covered early exits years ago in an excellent talk I just can't find anymore (maybe someone can point me to it?). He nicely described how all that nesting builds up a huge context you need to keep in your brain while trying to reason about the function.
Oct 04 2018
On 10/02/2018 02:14 PM, Andrei Alexandrescu wrote:Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682I was in college during the height of the Java craze, so my instructors highly recommended the deep nesting approach. This was because return statements are control-flow, and control-flow isn't very object-orientedy, and is old-fasioned and in the same category as the dreaded goto and was therefore bad. So I switched to the nesting-instead-of-returning style because it was "The Right Way". Eventually, I realized the deep nesting approach meant the main core of my functions were carrying around extra context with them, thus decreasing my ability to read and reason about my code. By contrast, with early-returns, all of that special-case junk is taken care of and moved out of the way (much like catch blocks), so the main core of the function no longer had to be burdened by it. So I switched back to early-returns and learned to love them.
Oct 04 2018
On 05/10/2018 8:23 AM, Nick Sabalausky (Abscissa) wrote:I was in college during the height of the Java craze, so my instructors highly recommended the deep nesting approach. This was because return statements are control-flow, and control-flow isn't very object-orientedy, and is old-fasioned and in the same category as the dreaded goto and was therefore bad. So I switched to the nesting-instead-of-returning style because it was "The Right Way"."Terminology invoking "objects" and "oriented" in the modern sense of object-oriented programming made its first appearance at MIT in the late 1950s and early 1960s."[0]. And this is why you have to be very careful with any sort of trend in programming. Because it was already done before you were born (assuming you began learning after 1990) ;) [0] https://en.wikipedia.org/wiki/Object-oriented_programming#History
Oct 04 2018
On 10/04/2018 11:40 PM, rikki cattermole wrote:On 05/10/2018 8:23 AM, Nick Sabalausky (Abscissa) wrote:It's not *my* statement about newer/older. If you recall the programming atmosphere around 2000, OO was widely being touted as a newer thing, superior to "old-fashioned" imperative, even though there's a million things about that whole assessment that are false (not the least of which being the at-the-time popular notion that Java-style OO somehow wasn't still imperative, or, as you pointed out, that OO was a new invention). There's one minor aspect of it that was true though: Widespread popularity of OO was certainly a new thing, even if OO itself wasn't.I was in college during the height of the Java craze, so my instructors highly recommended the deep nesting approach. This was because return statements are control-flow, and control-flow isn't very object-orientedy, and is old-fasioned and in the same category as the dreaded goto and was therefore bad. So I switched to the nesting-instead-of-returning style because it was "The Right Way"."Terminology invoking "objects" and "oriented" in the modern sense of object-oriented programming made its first appearance at MIT in the late 1950s and early 1960s."[0]. And this is why you have to be very careful with any sort of trend in programming. Because it was already done before you were born (assuming you began learning after 1990) ;) [0] https://en.wikipedia.org/wiki/Object-oriented_programming#History
Oct 05 2018
On Friday, 5 October 2018 at 19:04:26 UTC, Nick Sabalausky (Abscissa) wrote:On 10/04/2018 11:40 PM, rikki cattermole wrote:The hype was hight also in the 90... I remember having used (in production!) a 3rd party extension to Clipper (I don't remember if Summer 87, or 5.0.x) that added OO to the language! 0__o /Paolo[...]It's not *my* statement about newer/older. If you recall the programming atmosphere around 2000, OO was widely being touted as a newer thing, superior to "old-fashioned" imperative, even though there's a million things about that whole assessment that are false (not the least of which being the at-the-time popular notion that Java-style OO somehow wasn't still imperative, or, as you pointed out, that OO was a new invention). There's one minor aspect of it that was true though: Widespread popularity of OO was certainly a new thing, even if OO itself wasn't.
Oct 05 2018
On Saturday, 6 October 2018 at 05:36:59 UTC, Paolo Invernizzi wrote:On Friday, 5 October 2018 at 19:04:26 UTC, Nick Sabalausky (Abscissa) wrote:In the 90s I used to add the C preprocessor to other languages which lacked efficient constant definition (i.e. compile time constructs). AutoLISP, the LISP dialect used to write application in AutoCAD. There were nearly a 100 of small programs in different files and throughout the whole project there were a lot repetitions that could not be factorized with AutoCAD means. Include, define and ifdef allowed to do things, that were very difficult to do at that time (it was on AutoCAD v9.0 which had only 64K memory for the LISP code). I also added the C preprocessor to the DBASE III and the compatible MS-DOS based Foxbase.On 10/04/2018 11:40 PM, rikki cattermole wrote:The hype was hight also in the 90... I remember having used (in production!) a 3rd party extension to Clipper (I don't remember if Summer 87, or 5.0.x) that added OO to the language![...]It's not *my* statement about newer/older. If you recall the programming atmosphere around 2000, OO was widely being touted as a newer thing, superior to "old-fashioned" imperative, even though there's a million things about that whole assessment that are false (not the least of which being the at-the-time popular notion that Java-style OO somehow wasn't still imperative, or, as you pointed out, that OO was a new invention). There's one minor aspect of it that was true though: Widespread popularity of OO was certainly a new thing, even if OO itself wasn't.
Oct 06 2018
On Saturday, 6 October 2018 at 18:55:48 UTC, Patrick Schluter wrote:On Saturday, 6 October 2018 at 05:36:59 UTC, Paolo Invernizzi wrote:Fox, the speediest indexes in the country... what a time! :-P /P[...]In the 90s I used to add the C preprocessor to other languages which lacked efficient constant definition (i.e. compile time constructs). AutoLISP, the LISP dialect used to write application in AutoCAD. There were nearly a 100 of small programs in different files and throughout the whole project there were a lot repetitions that could not be factorized with AutoCAD means. Include, define and ifdef allowed to do things, that were very difficult to do at that time (it was on AutoCAD v9.0 which had only 64K memory for the LISP code). I also added the C preprocessor to the DBASE III and the compatible MS-DOS based Foxbase.
Oct 06 2018
On Tuesday, 2 October 2018 at 18:14:55 UTC, Andrei Alexandrescu wrote:Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682I've found a good explanation for the reason behind nesting here: https://softwareengineering.stackexchange.com/a/118793 The "single entry, single exit" principle seems to predate structured programming. It was a best practice of a time when gotos and other jumps were preventing the call stack to be properly cleaned up or local variables to be initialized thus leading to difficult to debug errors.
Oct 05 2018
On Tuesday, October 2, 2018 12:14:55 PM MDT Andrei Alexandrescu via Digitalmars-d wrote:Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682It's one of those things that I would have thought would just be obvious with experience, but if nothing else, some folks still try to stick to the whole "single return" idea even though I think that most folks agree at this point that it causes more problems than it solves. Certainly, if you try to do it both ways, it shouldn't take long to see exactly what she shows in your own code. - Jonathan M Davis
Oct 05 2018
On Friday, 5 October 2018 at 21:34:38 UTC, Jonathan M Davis wrote:It's one of those things that I would have thought would just be obvious with experience, but if nothing else, some folks still try to stick to the whole "single return" idea even though I think that most folks agree at this point that it causes more problems than it solves.Sadly, one of these "folks" is DMD's inliner. Then again, returns aren't it's only bane.
Oct 08 2018
On Tuesday, 2 October 2018 at 18:14:55 UTC, Andrei Alexandrescu wrote:Kate Gregory makes a good argument on something I've often commented in code reviews: https://youtu.be/n0Ak6xtVXno?t=2682I very much like LLVM's practices, and one of them is it's Coding Standards. It prescribes early returns: https://llvm.org/docs/CodingStandards.html#use-early-exits-and-continue-to-simplify-code It adds "use continue" to the early returns advice. (The equivalent of "early returns" in loops.) -Johan
Oct 09 2018