cjbottaro

cjbottaro

What do you think about early returns?

I’ve been primarily doing Elixir development for the past 6 years or so, and during that time whole heartedly committed to functional paradigms.

But recently I did some Go programming, and I hate to admit it, but working with early returns again was kinda nice.

I’m not a fan of lots of small little functions, and using with seems to necessitate that; a lot of the times I’ll just deal with case/if nesting.

So one day, I threw (pun intended) in the towel and refactored a plug that has many success cases as well as error cases that need to return early, to use throw/catch to emulate early returns… and I was really happy with the results.

But there is this nagging feeling that I’ll be excommunicated from the community if this code ever sees the light of day publicly. I kid, I kid… :joy:

So why are early returns bad? I’ve since wrapped up the throw/catch paradigm into a tiny library that let’s it be used like this:

v = returnable do
  if some_condition?()
    return "foo"
  end
  ...
end

I understand that early returns make mechanical “refactor into function” difficult, but I think wrapping it up in an expression like above negates the issue. Not sure.

I’ve seen some posts (on Reddit, not here) where people suggest you can use throw/catch to emulate early returns “but you need to be an expert to do it safely and properly.” Why is that? What pitfalls are they alluding to?

As always, thanks for the help and info!

P.S. A little further in that video, he describes a use block that he wishes existed, but doesn’t know any programming language that has something like. I’m nearly positive it can be accomplished with metaprogramming in Elixir… but probably a topic for a separate post.

Most Liked

codeanpeace

codeanpeace

tl;dr in my experience, multiple function heads + guards > early returns

Coming from Ruby where I appreciated how early returns could un-nest code for readability, I remember searching for an equivalent when I began learning Elixir. My rule of thumb when using early returns in Ruby were to limit them to the beginning of a function body to avoid the need to “chase” down all the possible returns lurking within a function, which would reduce readability.

What I soon realized was that leveraging function arity aka multiple function heads and guards in Elixir accomplished much of what I wanted out of early returns in Ruby while pulling it out of the function body and into the function head – arguably improving readability. ¯\_(ツ)_/¯

dorgan

dorgan

One thing about the particular snippet I posted is that the hypothetic check function wraps a conditional, to avoid ad hoc patterns like this:

with {:foo, true} <- {:foo, foo > 42},
     more_steps_here do
  profit!()
else,
  {:foo, false} -> ...
end

so instead you’d do this:

with :ok <- check(foo > 42, :too_low),
     more_steps_here do
  profit!()
else
  {:error, :too_low} -> ...
end

Your snippet assumes you already wrote functions that play nicely with with, while mine is more about the ad-hoc use cases, like checking for a bunch of predicates that are so mundane extracting them too functions would just add lots of verbosity and noise for the sake of using with

Related, I don’t particularly like having a specific tag like :foo_error instead of just :error with a more descriptive value because it makes it harder to write helpers that can just assume a normalized ok/error tuple. This is one thing I do find useful about ADTs like some people mentioned early, if you can assume normalized shapes, it’s easier to write composable functions.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

I think this conversation is suffering from a lack of a definition about what counts as small or fast here. These are relative terms, and for people say, returning HTTP results, the overhead of a remote function call vs a local one is indeed small. For a hot loop processing a million records, maybe it isn’t small.

Where Next?

Popular in Discussions Top

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41454 115
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30840 112
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

We're in Beta

About us Mission Statement