4 ms·
As, with everything, it should be applied wisely. Early returns is not a feature of the language. It's an artefact of assembly language. You have no reasons to
by quadcore 7y ago
As, with everything, it should be applied wisely.
Early returns is not a feature of the language. It's an artefact of assembly language. You have no reasons to use it in the first place.
In most cases, I think it is perfectly fine to have early exits on parameter checks at the start of the function/method.
I believe you'd be surprised what aberrations you'd find in your code and all the simplifications you could make if you would removed all the jumps.
- solarkraft 7y agoAre early returns essentially equivalent to gotos? I suppose I could get rid of early returns with a variable and breaks, but is it worth it? My functions tend to be small enough for me to not run into problems with early returns.
- quadcore 7y agomassively edited. very interesting, thanks for the comment They are literally goto end. It used to be done like that a lot actually. I suppose I could get rid of early returns with a variable and breaks, but is it worth it? Very good question. My claim is that yes it is tremendously worth it if you care about great code. The reason is because by introducing intermediate variables (or by using more the ternary operator ?) you will have a more expressive code, and then you will subsequently take better decisions when you will write/maintain/refactor it. And more importantly, you will refactor it because you can (how do you move code that's full of early returns?). At the end, your program will be much simpler and better (less bugs, faster, etc). Just try it in a big-enough program, you will not come back to the old ways, I think. My functions tend to be small enough for me to not run into problems with early returns. Very interesting. This point would require a live discussion. Many things are at play here, between over engineered object-oriented programs where everything is broken into little pieces that shouldn't (most of the time you should make a function when it's called twice, not because it represent a world object) and also, I'm not surprised you guys don't do bigger functions since you fill them with early returns. In clear, what I'm saying is: are you guys not able to write any big functions because of the early returns? I wasn't expecting that, sounds almost plausible. You end up breaking things up way too much, over engineering things and all just to avoid bigger functions because you can't have them because of the early returns? Very very interesting. Of course this is complete conjecture and certainly not true. Or is it? edit: it also depend on what you mean by worth it. In terms of market value, you will not be rewarded because the market the doesn't reward great code so much anyway, broadly speaking.
- kd5bjo 7y ago> They are literally goto end. So are a lot of other constructs: break is goto the end of the loop, continue is goto the beginning of the loop, the closing of a loop block is goto the beginning of loop, else is goto the end of the conditional, if is maybe goto the next else, and return (early or not) is pop from the stack and goto that address. The fact that a language feature uses a JUMP instruction does not make it necessarily evil; most of them do. Everything has both benefits and drawbacks which are often highly situational. You may find it easier to convince people of the problem with early returns if you limit the scope of the argument to a more specific context than “always”: it’s usually easier to make an argument for a concrete case rather than an abstraction, and then show how the same reasoning applies to a large class of situations.
- solarkraft 7y agoThanks a lot for the elaborate reply. I do tend to break things up into small functions. Why is that over-engineering? It makes it easier for me to reason about my code. It also makes it easier to refactor sometimes, although of course you're now restrained by the function's construct, so it becomes more difficult to move things between functions. Do I understand correctly that you tend to make large functions because they're easier to refactor (and not returning early makes this easier)?
- irjoe 7y agoI'm currently writing a lot of Elixir where early returns are a feature of the language (https://hexdocs.pm/elixir/Kernel.html#module-guards https://hexdocs.pm/elixir/Kernel.html#module-guards). I also write a lot of JS and tend to use guard clauses fairly liberally. I find they make boundaries (x < 0) much clearer. My functions are also < 10 lines in most cases. For me it leads to less convoluted code but YMMV.
- sterwill 7y agoI love Elixir, but I don't think of guards as "early returns". They're more like preconditions or filters: they prevent the function from running at all if they don't match, and because they're applied top-down in the module, it's really easy for the programmer to find the one that should match. They're great! Other programming languages let you return from any line in the function, no matter how deep in a conditional, inside nested loops, before or after variable side effects, etc. Tricky to know just _what_ that function will do given some inputs.
- irjoe 7y agoYou're right, they're not really "early returns" but for me at least they are often in similar fashion to the "early exit" pattern in the article: def transform_data(raw_data) when length(raw_data) == 1, do: [] def transform_data(raw_data) do # actual function code goes here end That said, I wouldn't want to see the above snippet in my team's codebase :)
- rrobukef 7y agoNot a fan of prolog then? As an excercise, find the guard in this snippet (https://rosettacode.org/wiki/Babbage_problem#Prolog https://rosettacode.org/wiki/Babbage_problem#Prolog) and imagine such code in a team codebase!
- irjoe 7y agoQuite the opposite :) My preference would be to rely on pattern matching (unification) in the function head: def transform_data([_|[]]) do: [] I just wanted an example of using guard clauses. It does depend on the actual code though, there's probably a more elixiry solution.