www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Deep nesting vs early returns

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
Kate Gregory makes a good argument on something I've often commented in 
code reviews: https://youtu.be/n0Ak6xtVXno?t=2682
Oct 02 2018
next sibling parent reply Jacob Carlborg <doob me.com> writes:
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=2682
Swift 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
parent reply Faux Amis <faux amis.com> writes:
On 2018-10-02 21:09, Jacob Carlborg wrote:
 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=2682
Swift 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
Is it: For optional binding, Swift 2 has sugar for if let !( ){} Or am I missing the point of the guard statement?
Oct 02 2018
parent Jacob Carlborg <doob me.com> writes:
On Tuesday, 2 October 2018 at 20:53:40

 Is 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
prev sibling next sibling parent reply Gopan <gggopan gmail.com> writes:
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=2682
Thank 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
next sibling parent Jack Applegame <japplegame gmail.com> writes:
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
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
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
prev sibling next sibling parent Claude <no no.no> writes:
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
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
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
parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
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:
  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.
`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.
Oct 05 2018
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
[...]
 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.
`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).
+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.
 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
parent =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
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
prev sibling next sibling parent Trass3r <un known.com> writes:
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=2682
Sean 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
prev sibling next sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
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=2682
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". 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
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
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
parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 10/04/2018 11:40 PM, rikki cattermole wrote:
 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
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
parent reply Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
On Friday, 5 October 2018 at 19:04:26 UTC, Nick Sabalausky 
(Abscissa) wrote:
 On 10/04/2018 11:40 PM, rikki cattermole 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.
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
Oct 05 2018
parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
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:
 On 10/04/2018 11:40 PM, rikki cattermole 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.
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!
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
parent Paolo Invernizzi <paolo.invernizzi gmail.com> writes:
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:
[...]
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.
Fox, the speediest indexes in the country... what a time! :-P /P
Oct 06 2018
prev sibling next sibling parent =?UTF-8?B?UsOpbXkgTW91w6t6YQ==?= <remy.moueza gmail.com> writes:
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=2682
I'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
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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=2682
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. 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
parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
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
prev sibling parent Johan Engelen <j j.nl> writes:
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=2682
I 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