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.

cloudytoday

cloudytoday

What I love about functional programming is that it’s essentially just graph reduction (“everything is expression”), which makes reasoning about code much more simple. Early returns (as well as exceptions) throw this out of the window. If they are rather simulated with an elegant abstraction (e.g. Haskell’s do-notation for certain types, or Elixir’s with) then that’s fine because it’s still graph reduction.

D4no0

D4no0

Early returns are a remnant of goto statements from languages like cobol, they encourage break of flow. A classical example would be:

def first_id_or_null(list, id) do
  Enum.map(list, fn el -> if el.id == id, do: return el end)
  return null
end

Now you can notice here that while such a function would work without problems if early returns were a thing in elixir, it is introducing a notion that we can return data from everywhere, hence breaking the concept of what a map function should be able to do, introducing a new concept that you can break out of all contexts.

This example can be as well rewritten to:

def first_id_or_null([], id), do: null
def first_id_or_null([h | t], id) do
  if(h.id == id), do: h, else: first_id_or_null(t, id)
end

What is interesting is that there are cases where you would want to return early, a good example would be Enum.reduce_while/3 and it is easily possible to implement this without using throw/catch.

Where Next?

Popular in Discussions Top

bartblast
With the core component system and HTTP/WebSocket infrastructure solid, it’s time to tackle Pub/Sub support. What We Have vs What’s Miss...
New
bartblast
StackOverflow Survey results for 2025 have been published: Gleam - 2nd most admired language Elixir - 3rd most admired language Phoenix...
New
lud
Hello, I just extracted the boilerplate management code that I used to work with in previous years: It is yet another generic input d...
New
stefannovak
Hi all, I’m going to be giving a little 20 minute tech talk at my company which uses .NET C# across all of our 100ish size IT team. I’m ...
New
AstonJ
Have you changed the way you learn? Maybe you started off using docs and tutorials and are now an avid book reader or course watcher? Or ...
New
neilberkman
Carson Katri from DockYard posted today about swift-erlang-actor-system, which enables Swift programs to join Erlang clusters as distribu...
New
James_E
I see that the current ExUnit source code has support for rich failure messages on a small whitelist of “recognized” assertion patterns, ...
New
rhcarvalho
Very interesting value proposition! Having watched some interviews with Elm’s creator recently, I wanted to ask where you stand in terms...
New
jonator
FLAME is basically the dream for running AI agents that need system command access such as coding agents. However, I have concerns about ...
New
ronindev
Hey everyone! :waving_hand: I just wanted to recommend https://seenode.com/ for deploying Phoenix apps. I’ve been using it recently and ...
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
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
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New

We're in Beta

About us Mission Statement