elt547

elt547

What do y’all think about the defaults of the mix formatter? I often find long lines being being turned into multiple breaks with weird indentation. The output is so often hideous and completely unreadable.

This happens almost exclusively in long function definitions with pattern matching. Since pattern matching function parameters is a core part of elixir and incidentally very wordy, it doesn’t make sense for formatters to make such a fuss over line length. It’s making me doubt whether I’m writing elixir in the intended manner.

Look at the example below. It’s almost impossible to visually separate the function definition from the first line of the function.

This version is objectively easier to read, but apparently wrong according to the flagship formatter. Is 103 columns really that sinful for a function definition?

Showing Posts 1 to 10

LostKobrakai

LostKobrakai

Line length is one of the few things you can configure on the formatter. If you feel the default is to short increase it.

elt547

elt547 OP

Thanks, that’s not actually in the hex docs so I wasn’t aware. That should do for now!

sorentwo

sorentwo

Oban Core Team

Personally, 9 out of 10 times I see a function clause that spans multiple lines as a code smell. Ask yourself:

  • Do you need all of that pattern matching in the function head?
  • Can you match some in the body instead?
  • Can you write a private guard to reduce matching or guard clauses?
  • Can you use a case within a function (or all compiles down to a case anyhow)?
sodapopcan

sodapopcan

Agreed. I’ve come across “over-matching” to the point where it takes a second to figure out which matches are required for flow control.

handle_event is pretty ubiquitous so it’s not as big a deal, but I always write it like this:

def handle_event("save", %{"profile" => profile_params}, socket) do
  profile = socket.assigns.profile
  # ...
end

I feel that setting profile in the body is a lot less noisy than nested destructuring, especially when combined with capturing the whole value. My other reasoning is that the event name and param arguments are specific to the function itself whereas profile already exists in state, so destructuring it the head isn’t telling me anything new—I already know that I always have access to profile if I have a socket.

It’s all a matter of taste, of course, and totally up to you!

elt547

elt547 OP

After playing with it a while, I really want to configure the formatter to never breakdown a long function head. I like how it formats other lines, so changing the line length breaks that preference. Is this possible?

elt547

elt547 OP

Are you able to give examples of the last two in alleviating this issue? Thanks!

elt547

elt547 OP

Thanks for the kick, I think this might be a case of me being new to the language. I find myself doing the same thing with “over-piping” the socket struct instead of making a regular function call with the socket as first param.

{:noreply, socket |> assign(:other, "other")}

# instead of

{:noreply, assign(socket, :other, "other")}
baldwindavid

baldwindavid

Think you mean subjective :slight_smile: I actually find the formatted version easier to read. It’s easier for me to scan vertically than horizontally and I don’t like long lines. I tend to think one can get used to just about any formatting if you look at it enough, but also agree with others that too many matches in a function head can be hard to parse.

sorentwo

sorentwo

Oban Core Team

Sure thing!

  1. Here’s an example of writing a private guard from Oban: oban/lib at main · oban-bg/oban · GitHub
  2. Here’s an example of reducing the number of function clauses and minimizing the head size by matching with a case in the function body (it is from Oban Pro, so no link)
  def job_to_key(%{worker: worker, args: args}, partition) do
    case partition do
      %{fields: ["worker"], keys: []} ->
        with_hash({worker, nil})

      %{fields: ["args"], keys: keys} ->
        with_hash({nil, Map.take(args, keys)})

      %{fields: [_, _], keys: keys} ->
        with_hash({worker, Map.take(args, keys)})

      _ ->
        with_hash({nil, nil})
    end
  end
thiagomajesk

thiagomajesk

The formatter is something that I have a bittersweet feeling about and it’s perhaps the tool that I least like in the ecosystem. It’s great to keep a codebase consistent across teams if you don’t really want to think about it.

I hope for more configuration options in the future while keeping some opinionated default because I really care how my code reads. I also got my code butchered by the formatter a lot in the past because it doesn’t care about nuances as I do.

TLDR: It’s great for standardization, but the idea that a tool would decide how your code reads better than yourself is nuts to me.

There’s some value in those considerations overall if you want to increase readability in some cases, but I disagree with the premise of considering it as code smells. Mainly because there’s no real underlying issue in your code if you don’t follow this. It’s all a matter of preference on how you want to read your code. BTW, extracting code elsewhere is not always preferred for legibility.

That’s exactly my point, thanks @baldwindavid! Legibility is something really subjective and I think the main gripe some people might have with the formatter is that it’s not really flexible in the first place. It actually enforces very opinionated defaults which are also very subjective.

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
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
maennchen
:warning: Security advisory: Decimal DoS vulnerability A vulnerability has been published for decimal where very large exponents can cau...
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