This is a document explaining our reasoning behind the formatting decision for list comprehensions.
This document conforms to the decisions already made for lists and does not rehash decisions already taken in that document.
This document focuses on list comprehensions that are broken up over multiple lines, because of:
- a long function name,
- long parameters,
- long generators
- long filters
- etc.
We will choose a format that best conforms to our goals:
- Create/keep differentiation between the generator, body and filters.
- Consistent with formatting of Lists
- Handles multiline expressions consistently.
- Consistent indenting by 4, to aid writability
Here you can see all the other candidates we evaluated against our goals.
- ✅ Differentiating
- ✅ Consistent Lists
- ✅ Multiline expressions
- ❌ Indenting by 4
We considered indenting the filter expressions by 3.
[
function_with_long_name(A)
|| {A, B} <- Cs,
filter(B)
]
This does allow you to move some parts onto the same line, if they fit they will be kept there:
[
function_with_long_name(A)
|| {A, B} <- Cs, filter(B)
]
Here is an example with a multiline expression, that shows what a long argument in the function would look like:
[
function_with_long_name(
A,
ALongArgument
)
|| {A, B} <- Cs,
filter(B)
]
- ✅ Differentiating
- ✅ Consistent with lists
- ✅ Handles multiline expression consistently
- ❌ Indenting by 4
The one negative in the previous example is inconsistent indentation, where filter(B)
was indented by 3 spaces.
This alternative tries to correct that, but now we have a misalignment between {A, B}
and filter(B)
by a single space.
[
function_with_long_name(A)
|| {A, B} <- Cs,
filter(B)
]
- ✅ Differentiating
- ❌ Consistent with lists
- ✅ Handles multiline expression consistently
- ✅ Indenting by 4
We also considered dedenting the double pipes to the left.
[
function_with_long_name(A)
|| {A, B} <- Cs,
filter(B)
]
This doesn't seem very consistent with how lists are formatted.
[
function_with_long_name(A)
|| {A, B} <- Cs, filter(B)
]
- ✅ Differentiating
- ✅ Consistent with lists
- ✅ Handles multiline expression consistently
- ✅ Indenting by 4
This is the formatting erlfmt
chose.
Another alternative is dedenting the double pipes to the left, but just a little:
[
function_with_long_name(A)
|| {A, B} <- Cs, filter(B)
]
All parts of the list comprehension stay aligned if they are broken apart at a consistent 4 spaces:
[
function_with_long_name(A)
|| {A, B} <- Cs,
filter(B)
]
Here is an example with a multiline expression, that shows what a long argument in the function would look like:
[
function_with_long_name(
A,
ALongArgument
)
|| {A, B} <- Cs,
filter(B)
]
- ✅ Differentiating
- ✅ Consistent with lists
- ✅ Handles multiline expression consistently
- ✅ Indenting by 4
erlfmt
chose the above format over this one, purely because this alternative had much larger complexity and seems to require inventing a new operator in the algebra.
We can also considered dedenting the pipes, with two spaces after, to keep expressions aligned:
[
function_with_long_name(A)
|| {A, B} <- Cs, filter(B)
]
All parts of the list comprehension stay aligned if they are broken apart at a consistent 4 spaces:
[
function_with_long_name(A)
|| {A, B} <- Cs,
filter(B)
]
Here is an example with a multiline expression, that shows what a long argument in the function would look like:
[
function_with_long_name(
A,
ALongArgument
)
|| {A, B} <- Cs,
filter(B)
]
- ✅ Differentiating
- ❌ Consistent with lists
- ✅ Handles multiline expression consistently
- ✅ Indenting by 4
We also considered giving the double pipes their own line.
[
function_with_long_name(A)
||
{A, B} <- Cs,
filter(B)
]
[
function_with_long_name(A)
||
{A, B} <- Cs, filter(B)
]
- ✅ Differentiating
- ❌ Consistent with lists
- ❌ Handles multiline expression consistently
- ✅ Indenting by 4
The compressed option is inconsistent with how erlfmt
formats lists,
but does make differentiation clear.
[function_with_long_name(A)
|| {A, B} <- Cs,
filter(B)]
There is also a problem with how to indent multiline expressions consistently:
[function_with_long_name(
A,
AlongArgument
)
|| {A, B} <- Cs,
filter(B)]
- ✅ Differentiating
- ❌ Consistent with lists
- ❌ Handles multiline expression consistently
- ✅ Indenting by 4
We could also consider an option where indentations are made with four spaces.
[function_with_long_name(A)
|| A <- a_generator(),
some_check(A)]
Unfortunately since the first function is technically already indented by one space, this still results in inconsistent handling of multiline expressions:
[function_with_long_name(
A,
AlongArgument
)
|| A <- a_generator(),
some_check(A)]