Source-Code Formatting

This is the section where I get into trouble.

The Elixir core team wanted to standardize the format of source code that was submitted to them for inclusion in the various Elixir core projects. Rather than beat people up and reject pull requests, they made it easy for submitters by including a source-code formatting tool in Elixir 1.6. This tool is pretty smart—it knows not just the syntax of Elixir but also the parse tree, meaning that it will often move things between lines, drop commas, add parentheses, and so on.

This magic is done using the mix format command. It can format single files, directory trees, and whole projects (see mix help format for information). This formatting replaces the files it touches, so you might want to make sure you’re checked in before running it.

Let’s have a look at some before-and-after formatting:

If we feed it code that looks like a dog’s dinner:

 def​ no_vowels string
 do
  string |>
  String.replace(​~r/[aeiou]/​, ​"​​*"​)
 end
 
 def​ separator(column_widths) ​do
  map_join(column_widths, ​"​​-+-"​, ​fn​ width ->
  List.duplicate(​"​​-"​, width)
 end​)
 end

the formatter tidies it nicely:

 def​ no_vowels(string) ​do
  string
  |> String.replace(​~r/[aeiou]/​, ​"​​*"​)
 end
 
 def​ separator(column_widths) ​do
  map_join(column_widths, ​"​​-+-"​, ​fn​ width ->
  List.duplicate(​"​​-"​, width)
 end​)
 end

It is also pretty smart about multiline constructs:

  @names [
 Doc, Grumpy, Happy,
  Sleepy, Bashful, Sneezy,
  Dopey
 ]

This produces:

 @names [
  Doc,
  Grumpy,
  Happy,
  Sleepy,
  Bashful,
  Sneezy,
  Dopey
 ]

Here, because the original split onto a new line, the formatted result was normalized into one element per line.

There’s lots to like about the formatter. But I personally don’t use it, because it destroys some elements of layout I think are important.

For example, I like to line things up vertically. I find this much, much easier to read and to maintain. Most editors have support for this:

 options = %{
 style:​ ​"​​light"​,
 background:​ ​"​​green"
 }

and:

 name = ​"​​Alphabet"
 url = ​"​​https://abc.xyz"
 entry_count = 10

Unfortunately, the Elixir formatter goes to some trouble to remove that extra space, producing:

 options = %{
 style:​ ​"​​light"​,
 background:​ ​"​​green"
 }
 
 name = ​"​​Alphabet"
 url = ​"​​https://abc.xyz"
 entry_count = 10

Then there’s the contentious trailing comma.

When I list things out, I put a comma after each item and put each item on a new line. This makes it easier to move things around, add new items, sort the list, and so on. Each line is just like the other: the first and last lines are not special.

 plugins = [
  Format,
  Index,
  Print,
 ]

The formatter thinks this is silly, and removes the comma.

 plugins = [
  Format,
  Index,
  Print
 ]

And finally, there’s the trailing comment. I rarely use comments inside a block of code. When I do, and if it is short, I add it to the end of the line:

 def​ format(template, ​# a binary in eex format
  bindings, ​# the bindings to use
  options) ​do​ ​# :verbose | :narrow
 # ...
 end

I know this is a bad example, but even so the formatter makes some unfortunate decisions:

 # a binary in eex format
 def​ format(
  template,
 # the bindings to use
  bindings,
 # :verbose | :narrow
  options
  ) ​do
 # ...
 end

The upshot? If you like what it does, or if you’re submitting code to a project that requires it, use the formatter.