Live data from Hacker News

Avoid Else, Return Early (2013)

blog.timoxley.com

81–90 of 601 posts

Re: Avoid Else, Return Early (2013)

#81

Programmers with lots of hours of maintaining code eventually evolve to return early, sorting exit conditions at top and meat of the methods at the bottom. Same way you evolve out of one liners. Same way comments are extra weight that should only be in public or algorithm/need to know areas. Same way braces go on the end of the method/class name to reduce LOC. Same way you move on from heavy OO to dicts/lists. Same w…

Exactly, I did all of the above. Spent too many hours fixing broken code (of my own) and finally I started to learn.

What about error handling? No mention here. What I do not is let the library/framework to handle most of it. With a recent server library I wrote, I can throw anywhere and it will be caught and displayed as expected without extra try/catch.

Re: Avoid Else, Return Early (2013)

#82

Earlier quoted context omitted.

Goto is heavily used to return early, by jumping to the cleanup section at the bottom of the function. Otherwise, you have to repeat the cleanup code at each exit point, or use the dreaded pyramid of doom where each failure point introduces another indent level, which is what the author here is trying to avoid in the first place.

"cleanup section at the bottom of the function" is pretty rare in JavaScript, which is what the original blog post is discussing. Same with Java, C#, Ruby etc. No, it doesn't play well with early return, so avoid mixing them.

Actually, in Ruby we have "ensure"

    def foo arg
      return true if arg == 42
      puts "got past the guard"
      raise "blah"
    ensure
      puts "ensure always"
    end
    
    foo(42)
    foo(3)
The "ensure" blocks gets executed whether or not you return early, throw exception or return at the end of the block.

Re: Avoid Else, Return Early (2013)

#83
Mmm, I disagree.

Influenced by functional programming, I prefer to use the style of: "keep all return statements at the same indentation level" in statement based languages. This way, it is easier to parse as an expression. For example:

  if (...) {
    let a = ...;
    return x(a);
  } else {
    return y;
  }
Can be easily mentally factored into the pseudo-expression:

  return ... ? x(...) : y;
It is easier to see what the side-effects (or ideally lack of) are. It also makes case analysis easier, and you don't hide the fact that you have 2^{indentation levels} number of possible states.

Early return while excusable for some very particular and idiosyncratic error handling examples (e.g. fortified C APIs that accept null pointers as no-ops) in general feels like "cheating", making the code look less complicated than it actually is (it "silently" multiplies the size of your state space without increasing indentation). But most importantly, it puts too much emphasis on control flow: I'd rather emphasize the underlying declarative intent, the state machines, and pre/post-conditions. This is is better achieved, in my opinion, by trying to delay returns, and to try to minimize the amount of code after a branching (this often means having unnecesary else branches, that on the other hand help with readability.) As shown before, this helps mentally factoring the code into reducible expressions and guessing state space and overall complexity.

I agree though that having one single-return is not good, cuz most of the time it forces one to use mutable variables that could otherwise be avoided.

Re: Avoid Else, Return Early (2013)

#85

Programmers with lots of hours of maintaining code eventually evolve to return early, sorting exit conditions at top and meat of the methods at the bottom. Same way you evolve out of one liners. Same way comments are extra weight that should only be in public or algorithm/need to know areas. Same way braces go on the end of the method/class name to reduce LOC. Same way you move on from heavy OO to dicts/lists. Same w…

> Same way you move on from heavy OO to dicts/lists.

OO is better if the objects tell a story in a way that ProviderStrategyDataFetchers fail to do so and are effectively just wrappers for data structures. If you go pure data structure without story you just end having to add comments to explain purpose. The comments are the classes.

Re: Avoid Else, Return Early (2013)

#86
post #82

Earlier quoted context omitted.

"cleanup section at the bottom of the function" is pretty rare in JavaScript, which is what the original blog post is discussing. Same with Java, C#, Ruby etc. No, it doesn't play well with early return, so avoid mixing them.

Actually, in Ruby we have "ensure" def foo arg return true if arg == 42 puts "got past the guard" raise "blah" ensure puts "ensure always" end foo(42) foo(3) The "ensure" blocks gets executed whether or not you return early, throw exception or return at the end of the block.

There are several constructs for it, see also "try ... finally" on C# and Java, and "using" statements in C#.

All make the "goto manual cleanup at end" less necessary, and make early return easier to use.

Re: Avoid Else, Return Early (2013)

#87
post #63

This also works well with a C++ `defer` helper. #define CONCAT_LITERAL(x, y) x ## y #define CONCAT(x, y) CONCAT_LITERAL(x, y) template struct DeferWrapper { F f; DeferWrapper(F f) : f(f) {} ~DeferWrapper() { f(); } }; template DeferWrapper deferWrapper(F f) { return DeferWrapper (f); } #define defer(code) auto CONCAT(_defer_, __COUNTER__) = deferWrapper([&]() code) Example of usage: { Foo *foo; if (initializeFoo()) {…

You're turning RAII upside down... Learn to use proper RAII and you won't need this defer hack.

Re: Avoid Else, Return Early (2013)

#88

Programmers with lots of hours of maintaining code eventually evolve to return early, sorting exit conditions at top and meat of the methods at the bottom. Same way you evolve out of one liners. Same way comments are extra weight that should only be in public or algorithm/need to know areas. Same way braces go on the end of the method/class name to reduce LOC. Same way you move on from heavy OO to dicts/lists. Same w…

Exactly, I did all of the above. Spent too many hours fixing broken code (of my own) and finally I started to learn. What about error handling? No mention here. What I do not is let the library/framework to handle most of it. With a recent server library I wrote, I can throw anywhere and it will be caught and displayed as expected without extra try/catch.

Errors are the exception, not the rule :)

Re: Avoid Else, Return Early (2013)

#89
post #63

This also works well with a C++ `defer` helper. #define CONCAT_LITERAL(x, y) x ## y #define CONCAT(x, y) CONCAT_LITERAL(x, y) template struct DeferWrapper { F f; DeferWrapper(F f) : f(f) {} ~DeferWrapper() { f(); } }; template DeferWrapper deferWrapper(F f) { return DeferWrapper (f); } #define defer(code) auto CONCAT(_defer_, __COUNTER__) = deferWrapper([&]() code) Example of usage: { Foo *foo; if (initializeFoo()) {…

So with this, you literally had just thrown away the whole concept of RAII...
Post reply on HN