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.
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.
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.
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 :) 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.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.
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.
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.
Do I understand correctly that you tend to make large functions because they're easier to refactor (and not returning early makes this easier)?