Hacker News new | ask | show | jobs
by mrighele 1339 days ago
In a functional language technically you don’t have multiple returns, because the function is a single expression so in a way you’re right. On the other hand the actual result of the expression is determined on a leaf of the expression so you could consider it a return point

To make an example the following expression can be considered to have two returns:

    max x y =
      if x > y
      then x
      else y

The Java equivalent is

    int max(int x,int y) { 
      if (x>y) 
        return x;
      else 
        return y;
   }

Some people abhor the idea of having multiple returns in a method, and say that you should write it like

    int max(int x, int y) {
      int result;
      if (x>y) 
        result = x;
      else 
        result = y;
      return result;
    }
(Disclaimer: Contrived and buggy example as I am on mobile)
1 comments

Yes this is exactly what I meant. After years of coding in Haskell and Clojure, and then going back to Java I have absolutely no problems with

    int max(int x,int y) { 
      if (x>y) 
        return x;
      else 
        return y;
   }
I just looks completely fine to me, but colleagues would complain about it in reviews and I just don't understant the problem at all. The variant with the extra result variable looks just wierd to me, and I have seen much uglier code written by people desperately avoiding early/multiple returns.
What do these languages do when you accidentally miss a branch in you decision tree? Is there any lexical or static analysis error, or does it cause a runtime error or implicit null return when you hit the actual missed condition? I think these differences in potential outcomes are what guide many of these cultural rules of thumb in different programming styles.

In imperative programming, a bunch of nested conditionals can easily have incomplete coverage of possible program states, and it can be easy to overlook problems if you have a mixture of side-effect branches and early returns. I think some people struggle with this more than others, and it can flummox them almost like goto-laden spaghetti code.

Of course, there are other areas where similar errors can occur in different languages, i.e. in exception-handling or pattern-matching constructs. There are many different coding styles which can make these control-flow structures easier or harder to debug. But, I think there can also be a lot of "cargo culting" where zombie rules of thumb continue beyond when they were really particularly helpful.

And this is likely one of those zombie rules. It's a trivial case and anyone with a semblance of programming knowledge understands what happens. You don't even need the else statement in GP's case. Meanwhile, any non-trivial case is so context-dependent you can't blindly apply rules and claim to have the best result or even just a better result from what people would come up with naturally.

The fact everything is context-dependent is the basis of all these discussions to begin with. We severely lack evidence and most rules are hearsay taken as gospel.

Cargo-culting from C where you have explicit malloc() and free(). Best practice was to have only 1 return to ensure free() was always called on everything just before the return, so you wouldn't have a memory leak if one was missing from an early return.
I was wondering where this 1 return practice originated from, but yeah, it makes sense in that context.