Obsession with line length and hideous formatting (am I writing elixir wrong?)

You can write plugins for the formatter. For example, a trailing comma can be added by the freedom formatter.

It is kind of annoying committing to a library that uses their own personal style as it is just another thing to think about that you don’t normally have to bother with.

3 Likes

I’m relatively new to Elixir but encountered some similar frustration with multi-line formatting at the beginning, though I’ve since gotten more used to it. I realized, however, that I was often overmatching in order to reduce line count at the expense of readability.

The standard I’ve started to stick to is to only match in the function head on the minimum amount necessary to make that function body applicable to the given arguments. Everything else can be destructured in the function body. This means more lines of code on average, but function heads become more readable and convey more information by highlighting the specific elements of arguments that they’re really matching on.

4 Likes

That first formatted function head actually made me slightly queasy :rofl:

While we’re somewhat on the topic… has there ever been any public discussion of Elixir adopting JavaScript-style destructuring for atom keys in maps? %{foo} equivalent to %{foo: foo}. At a glance I can’t see a conflict with existing syntax.

I suppose there may be a conflict with the construction syntax %{foo | bar: “bar”} which could be problematic, but I don’t think the above is allowed without the pipe.

Many. Before and after this blog post: A story of regret and retiring a library from Hex – Andrea Leopardi

2 Likes

Thanks for the context and history!

Here, I’m just curious about something. I wonder wether there is a performance gain between getting “sub-parameters” in a function head instead of doing it inside its body.

And more specifically:

a = param.a
b = param.b

vs

%{a: a, b: b} = param

Well this is not really about the head or the body of the function…

Exactly when we are working with other people, I think we have to follow the same rules in formating the code.

You could benchmark it, but I strongly suspect no measurable difference in 99.9% of cases that Elixir users care about. You’d theoretically be doing fewer lookups and nil/membership comparisons with access in the body, but I wouldn’t be surprised if the Erlang compiler ends up “pre-loading” the data you’re going to be using in function bodies anyways. (I’m not an expert here at all, I just know enough to know that I know nothing about compiler optimization tricks that can be pulled to make stuff fast.)

1 Like

Not using a single pipe is something that was beat into me. While hurts readability in some cases, I never really found a big problem with it, yet here we are. I do find in some cases it makes sense, like I have some factory helpers like this:

build(:article)
|> mark_published()

which aren’t so nice to read like this:

mark_published(build(:article))

so I end up writing:

:article
|> build()
|> mark_published()

which isn’t the worst but there is something odd-looking about it and it appeases credo and my inherited distaste for single pipes.

It also works with the pro-trailing comma argument for diffs:

def changeset(entity, attrs) do
  cast(entity, attrs, [:one, :two, :three])
end

Now if I want to pipe on a validation at a later date, the diff is a bit more confusing than if I had just originally had:

def changeset(entity, attrs) do
  entity
  |> cast(attrs, [:one, :two, :three])
end

Anyway, I’m getting on a bit of tangent here. Regardless of all that, I’m very pro-community standards and follow those I don’t necessarily like.

2 Likes