Byzantine Reality

Searching for Byzantine failures in the world around us

Chess and Elixir

A nice habit that I picked up from the Pragmatic Programmer long ago was the excellent idea to commit to learning one new programming language a year. In the past, I’ve called this “the perennial Java problem”, referring to my search to find a language that “sucks less than Java”. This is a bit vague and perhaps unnecessarily offensive, so let me clarify it by saying I want to find a language that “can be used for actual programming and is less verbose than Java.” With that, I’ve been reading Dave Thomas’ Programming Elixir, an excellent read for programmers looking to learn the Elixir programming language. Since the idea is to use Elixir in a “real world app”, let’s go over what Elixir looks like for an app I wrote to help me get better at chess.

The Big Picture

While I learn a lot about a language from reading about it, I definitely learn a lot more by actually doing something with it. So while I was watching Daniel Rensch’s video on Full Board Awareness I saw an easy opportunity to write a program that does one of the exercises. The exercise is summarized as follows:

Have a partner pick a random square on a chess board and name it to you. You name if the square is white or black. Your partner informs you if this is correct and repeats this process (typically 20 to 25 times).

Of course, “a partner” will be “a program” for this exercise, but it seems like something simple enough to get my hands dirty in Elixir. Let’s see what it actually looks like!

Enter Elixir

So like other programming languages, we have a main function that we can use as an entry point for our program. Let’s look at what ours is:

1
2
3
4
5
6
7
8
9
  def main(argv) do
    :random.seed(:erlang.now())

    argv
      |> parse_args
      |> print_welcome_message
      |> quiz_user
      |> generate_report
  end

The : and |> and operators are likely to be the biggest sources of confusion in this example, so let’s take a look at what they do. The : denotes an atom, so if you’ve messed around with Ruby or Erlang, this won’t be new for you. Basically it’s a string constant, but the interesting thing about Elixir is that everything is a function, so :random.seed is actually invoking a function named seed in the random module. Since Elixir runs on the Erlang VM, this is an easy way to invoke Erlang functions. With that in mind, it’s straightforward to see that the line

1
:random.seed(:erlang.now())

just seeds the runtime’s random number generator to the current time. The pipeline operator |> is much more interesting to me though, so let’s take a look at that. The idea behind the pipeline operator is that it’s very common for a developer to write code like this:

1
d(c(b(a(args))))

Writing it like this is fine, but is difficult to understand when I come back to it at a later time. This is because the way I think of it is:

1
a(args) -> b -> c -> d

Elixir’s pipeline operator very nicely solves this problem. It means that instead of writing this:

1
generate_report(quiz_user(print_welcome_message(parse_args(argv))))

I can simply write this:

1
2
3
4
5
argv
  |> parse_args
  |> print_welcome_message
  |> quiz_user
  |> generate_report

Another cool and interesting thing about Elixir is that there’s no for construct, which effectively forces recursion as the means by which looping is done. For example, here’s the main meat of my program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  @doc """
  Does nothing, since there are no more questions to ask the user.
  """
  def ask_question(0) do
    []
  end

  @doc """
  Determines if the user is able to correctly identify the color of a single
  chess square, and then recursively calls itself to ask as many questions as
  the user would like.

  Returns a list of booleans, where each boolean corresponds to true if the
  user answered the question correctly, and false otherwise.
  """
  def ask_question(total_questions) do
    {square, color} = generate_square()

    # ask the user if it's black or white
    answer = get_response(square)

    # see if they're correct
    this_answer = (color == answer)
    check_response(this_answer)

    # ask more questions
    [this_answer | ask_question(total_questions - 1)]
  end

It gets invoked by quiz_user, which simply starts the process and counts how many questions the user got correct (indicated by true in the resulting list):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  @doc """
  Asks the user a series of questions about whether random squares on a
  chessboard are white or black.

  Returns {correct_answers, total_questions}. In the future we
  should consider expanding this to return more detailed information. In
  particular, we could gain more insight by returning a list, where each item
  indicates if the question was answered correctly or not, what square was
  chosen, and how long it took the user to respond.
  """
  def quiz_user(total_questions) do
    results = ask_question(total_questions)
    correct_answers = Enum.count(results, fn(x) -> x == true end)
    {correct_answers, total_questions}
  end

The pattern matching in function definitions is what makes Elixir really cool as far as I’m concerned. We call ask_question(n), and it will result in questions being asked until zero questions are left. For something like ask_question(3), it would look like this (assuming the user guesses all questions correctly):

1
2
3
4
5
[true | ask_question(2)]
[true | [true | ask_question(1)]]
[true | [true | [true | ask_question(0)]]]
[true | [true | [true | []]]]
[true, true, true]

We also use recursion as the very last part of our function (as opposed to something like [ask_question(n-1) | this_answer]) since Elixir has some neat tail call optimization tricks it does to free up stack space.

From Here

For the interested, all of the code in this program is open source and available on GitHub. I still need to add in better unit test coverage, and learn how to mock out things like I/O (so I can put in fake answers from the user in my unit tests). I’ve also been watching the original video I linked to above and thinking about how to (1) put a web interface on it instead of a command-line interface, and (2) adding in other exercises to keep my visualization skills sharp.

I’m also still reading the Elixir book, and am currently at the chapter about how Elixir does distributed computation. It’s actually really cool, so once I get through the remainder of the book I’ll see if there’s some useful way to shoehorn that stuff in. Regardless, I definitely recommend picking up Dave Thomas’ excellent Programming Elixir – it’s an intuitive introduction to the language that teaches you more than enough to get actual things done with Elixir, which is much more than I can say for other books and instantly justifies the purchase price. Happy coding!