I'd love to see a conventional format for the "with" keyword
laserlemon opened this issue ยท comments
I've seen:
with {:ok, user} <- User.get(id),
{:ok, posts} <- Post.by_author(user)
do
{:ok, posts}
else
error -> error
end
with \
{:ok, user} <- User.get(id),
{:ok, posts} <- Post.by_author(user)
do
{:ok, posts}
else
error -> error
end
amongst others including using the do:
form of the block, although I'm not sure if that format works with an else
clause as well.
I love the second form if it weren't for that pesky backslash. For that reason, I lean toward the first version.
Given that in the first version, the indentation depth will not change with the code (as opposed to, like I've seen in many styles
some_variable = foo
.bar # same with |>, point being renaming the variable means reindenting a lot
the only point speaking for the \
version is what editors will tolerate or support.
Howdy. You might find this with
syntax gist I did awhile back to try to get to a with
convention that allows for (optional) comments before each step. I try to write self-documenting code where comments aren't necessary, but sometimes it is very useful to be able to do this.
Because of this, the first version you have will have the comment before the entire with
statement, which I dislike because it looks like you're commenting on the with
statement as a whole as opposed to the first clause.
# 1. This comment is to the left and looks like it's for the `with` statement
with {:ok, user} <- User.get(id),
# 2. This comment is to the right, etc.
{:ok, posts} <- Post.by_author(user)
do
{:ok, posts}
else
error -> error
end
So I posted a suggested tweak on elixir forum, wherein I was pointed to the \
syntax. I thought that was great! ...only the \
operator doesn't allow for the immediately following line to be a comment.
But thankfully there was another alternative mentioned that uses parentheses:
with(
{:ok, user} <- User.get(id),
{:ok, posts} <- Post.by_author(user)
) do
{:ok, posts}
else
error -> error
end
This has several benefits:
- It allows for comments like I mentioned.
- It is less asymmetrical than the
\
version - It always has the same indentation.
- The indentation allows for the maximum usage of the width of the line.
- The
do
block is cleanly and clearly separated from the preceding clauses andelse
clause.
This is now how I do all of my with
statements. I thought you might like to hear about some of the reasoning behind the \
version and consequences of it and the parentheses version.
Interesting. I haven't seen the need yet for a comment in that space. Otherwise I would prefer the backslash version for being a tiny bit less noisy.
@bill-mybiz from this reply it seems the no comment after /
is a bug in the compiler. Did you ever follow up on that?
@schnittchen Yes, at first I also liked the \
version which is why I went with that when it was suggested. But I prefer the paren version now, because it not only allows for the comments, but also it cleans up and resolves the do
issue - , do:
vs do...end
.
@christopheradams Deja yep.
OK, so not a bug then if I read that right @bill-mybiz. thanks for the link.
@christopheradams Yes, it was a great insight to compare the behavior against Ruby and bash. I'm really enjoying the decision making going on with Elixir core stuff!
This is the style presented in the docs, although it may be hard to see since it's shown in an IEX session:
# with just do (a)
with :ok <- :ok,
:ok <- :ok,
do: :ok
# with else (b)
with :ok <- :ok,
:ok <- :ok do
:ok
else
:error ->
:error
end
(This indentation style is supported by Emacs elixir-mode. What does everyone else's editor do with this?)
Here's my summary so far:
- indenting successive
with
arguments is the preferred style do
vs.do:
if you have more than one line to do, or anelse
, then usedo
do
on a line by itself or not? docs and emacs prefer not. some of you prefer yes. (no other style rules havedo
on a line by itself.)- if you must comment above each
with
argument, use parentheses
What to you think?
I opened PR #124 for this. I didn't add a rule about commenting above with
clauses, since it's relatively complicated and uncommon. But if there were a rule it might read something like this (not sure how to describe what to do with do
):
-
If you need to add a comment above each
with
clause, surround them with
parentheses. Put each clause on its own line, indented two spaces.with( # look up foo {:ok, foo} <- fetch(opts, :foo), # look up bar {:ok, bar} <- fetch(opts, :bar) ) do {:ok, foo, bar} end
@christopheradams I think overall your changes look like they reflect the majority of core developers.
I mainly pointed out the paren version to show the reasoning behind the \
version, which I personally had mentioned both on the Elixir Forum and in the compiler issue I linked to (so perhaps I was responsible for their confusion). I tried to explain the dynamics of the paren version from my personal experience, because I use with
a lot, i.e. almost every primary function. I used to do it the ways you are listing, but for me, they were insufficient.
As an aside, I found your observation that no other rules have the do
keyword on its own line very interesting. I'd say the reason that the do
keyword is on the same line in other rules is that the bulk of code is usually inside the do
block. The with
construct is the only one that has the arguments be the bulk of the code with the do
block being most commonly a simple return value.
An alternative indentation for do:
is shown in this rule from a different guide:
with {:ok, date} <- Calendar.ISO.date(year, month, day),
{:ok, time} <- Time.new(hour, minute, second, microsecond),
do: new(date, time)
If that style catches on we can consider it.