cjbottaro

cjbottaro

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.

Showing Posts 50 to 41

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.

stevensonmt

stevensonmt

It’s really interesting how widely varied the stylistic choices one person might like versus another are. I would find that sort of check style much worse than tagging the steps of a with clause. I actually kind of love the pattern of

with {:ok, foo} <- some_foo_stuff(),
     {:ok, bar} <- some_bar_stuff(foo),
       profit!(bar)
else
   {:foo_error, error} -> handle_foo_error(error)
   {:bar_error, error} -> handle_bar_error(error)
   _ -> just_blow_it_up_but_be_nice_about_it()
end
dorgan

dorgan

defmodule Utils do
  def check(condition, error \\ :failed_check) do
    if condition, do: :ok, else: {:error, error}
  end
end

import Utils

with :ok <- check(foo > 42),
     :ok <- check(velocity.x < 27, :too_fast) do
  profit!()
end

(?)

Edit: I don’t personally do this but I do have the muscle memory of writing “checks” to make it easier to use with and not have to “tag” the steps
As with anything it’s not always the most ergonomic way and compromises are made

cjbottaro

cjbottaro OP

Yes, that’s what I meant. It reduces the nesting and makes it more readable to me.

Also to recap, I don’t want to use early returns pervasively. As most people, I typically reach for with or even nested case statements (and my tolerance for nesting is probably higher than most here).

But in some cases, I think it helps a lot. In our pretty large codebase, I found only two instances where I found early returns compelling. But imo, it made a big difference in these two cases.

stevensonmt

stevensonmt

I’m curious what you mean by “fixes” because I think we are thinking about different issues. I think you mean early returns allow you to avoid nesting conditionals, but I just meant that sprinkling returns throughout the conditional branches does not improve readability. I recognize readability is totally subjective so maybe that is where we differ.

cjbottaro

cjbottaro OP

Early returns fixes it for me in a lot of cases… :man_shrugging:

This discussion has been helpful to me, even if I don’t agree with a lot of y’all. I think what I’m realizing is that I don’t like having lots of small functions, which seems to be the answer to early returns.

stevensonmt

stevensonmt

I disagree with this. I think it’s much less readable having to scan a function/method for return which could occur at any position or even in multiple positions or not at all (at least in Rust). With Elixir you can just start at the bottom and boom, you know what the return should be. Even in a more complex case where the last expression is a conditional of some form you still only have to look through that conditional expression and not worry about the things that came before it. Of course if you’re nesting conditionals 5 deep this is unreadable but that’s not fixable with early returns.

dimitarvp

dimitarvp

That usually means the functions are too big. Address that and it gets significantly easier.

D4no0

D4no0

What? Compared to what language? I have to review code in OOP languages and it is just horrible. A class that inherits from a generic class, that implements some kind of logic and some interfaces.

In elixir you have functions that return some data, of course if the function is 300+ lines I can see how it can get hard very fast.

conradfr

conradfr

Personally the lack of return keyword is not in the pro column :wink:

It’s not only about early returns, I just think it has better readability, especially when reading/interacting with code you didn’t write.

I find reviewing PRs in Elixir harder than other languages for example.

But I prefer to use the idiomatic way of the language to do it rather than use throw or other clever tricks to simulate it.

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
maennchen
:warning: Security advisory: Decimal DoS vulnerability A vulnerability has been published for decimal where very large exponents can cau...
New
marciol
It would be helpful to have a list of companies worldwide that hire engineers without prior experience in Elixir. Often, it can be quite ...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New

Other Trending Topics Top

marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews