Clean Code in Elixir

Posted on Fri 29 April 2016 in english

If you've ever read "Uncle Bob" Martin C. Fowler's Book Clean Code: A Handbook of Agile Software Craftsmanship there's an example in chapter 2 of what good (or bad) use of context means.

It reads as follows:

private void printGuessStatistics(char candidate, int count) {
  String number;
  String verb;
  String pluralModifier;
  if (count == 0) {
    number = "no";
    verb = "are";
    pluralModifier = "s";
  } else if (count == 1) {
    number = "1";
    verb = "is";
    pluralModifier = "";
  } else {
    number = Integer.toString(count);
    verb = "are";
    pluralModifier = "s";
  }
  String guessMessage = String.format(
    "There %s %s %s%s", verb, number, candidate, pluralModifier
  );
  print(guessMessage)
}

Clearly this one function is way too long to read and understand. The refactored version from the book is:

public class GuessStatisticsMessage {
  private String number;
  private String verb;
  private String pluralModifier;

  public String make(char candidate, int count) {
    createPluralDependentMessageParts(count);
    return String.format(
      "There %s %s %s%s", verb, number, candidate, pluralModifier
    );
  }

  private void createPluralDependentMessageParts(int count) {
    if (count == 0) {
      thereAreNoLetters();
    } else if (count == 1) {
      thereIsOneLetter();
    } else {
      thereAreManyLetters(count);
    }
  }

  private void thereAreManyLetters(int count) {
    number = Integer.toString(count);
    verb = "are";
    pluralModifier = "s";
  }

  private void thereIsOneLetter() {
    number = "1";
    verb = "is";
    pluralModifier = "";
  }

  private void thereAreNoLetters() {
    number = "0";
    verb = "are";
    pluralModifier = "s";
  }
}

Obviously the later version is better. It's structured in small chunks which are read and understood in mere seconds. You can read it downwards and it's variable names give you an idea of what the author meant.

My point is now: Weeks before reading "Code Clean" I've read "Programming Elixir" by Dave Thomas. Elixir features a great language construct named pattern matching in conjunction with guard clauses, which let's you write the code in the following way:

defmodule GuessStatisticsMessage do

  def print_guess_statistics(count) when count > 1 do
    {Integer.to_string(count), "are", "s"}
  end

 def print_guess_statistics(count) when count == 1 do
   {"one", "is", ""}
 end

 def print_guess_statistics(_) do
   {"no", "are", "s"}
 end

 def format_output(tuple, candidate) do
   "There #{elem(tuple,1)} #{elem(tuple,0)} #{candidate}#{elem(tuple,2)}"
 end

end

This code snipped is one of the many reasons I love functional programming and Elixir in general: It enables you to write small, clean and precise code without all of the abstraction layers and inherited state declared somewhere else.

Conclusion: Elixir == beautiful code.

UPDATE

After putting this post online, I got an even shorter, cleaner solution by @keita:

def format_output({counter, verb, plural}, candidate) do
  "There #{verb} #{counter} #{candidate}#{plural}"
end

Thank, Keita!