In BlogPost, Elixir

Untangling Nested Case Statements Using Elixir’s With Statement

I love spaghetti and meatballs. And when I first heard the term spaghetti code, it sounded tasty. Alas, it’s a pejorative phrase for code that is messy and overly complicated. If you write spaghetti code, no one will want to hire you.

That’s true in any language, including Elixir.

One way to introduce spaghetti code into your Elixir code base is with a nested case statement.

What is a nested case statement?

Below is a simple example of a double nested case statement. Don’t worry too much about making sense of the higher level context about what this code is doing. I took some actual production code from a project and altered it for educational purposes. Instead, if you need a higher level context, pretend we’re simply setting the is_snow_tire property to a boolean value based on the diameter property of a tire.

The whole point is to focus on the nested case aspect of this code.

It’s not too hard to reason about, but I still find myself stopping to wonder what the difference between the two error return clauses are and if they are actually doing anything different.

Looking at the actual code upon which this was based off of, it actually doesn’t matter what the second element of the return error tuple is (I’m asking you to take my word for it in this instance as I don’t want to clutter up this post with more code that is not related to the concept we’re discussing).

So, what is wrong with a nested case statement?

I would argue there are 3 things wrong with it.

Point 1 – Readability

A nested case statement is simply not as readable as relying on pattern matching or using a with statement (coming later in this post). It’s harder for new developers coming into your team to understand your code base and make changes to it.

Point 2 – Maintainability and Adding New Features

The more you have to stop and think about what your code is doing, the harder it is to debug effectively and promptly. It’s also harder to add new features.

You want to be able to build a great product and have an easy time maintaining it. This will help you win against your competitors against the marketplace.

An extreme example for clarity

In case (pardon the pun) you didn’t believe the above example, here is an example of a nested case statement from a user in the elixir forum. I changed some of the variable names, but hopefully you can see my point about nested case statements.1

Notice how much harder this makes the code to read.

So what is is one to do? One solution is the with statement.

Syntax of a with statement

Let’s look at rewriting the double nested case statement using a with statement.

So the great thing about the with statement is that it lets you say “each intermediate result must match each pattern to the left of the “<-“ to execute the code in the with/do block, otherwise proceed to else”. Notice that your eye now can scan each statement to check what must match much more easily than it can with a nested case statement.

Advantages to using with

Advantage 1 – In our specific example, we must pattern match on the {:ok, tire} tuple for each function call. If a function should pattern match on a one element tuple like {:ok} instead, you can specify that too. Thus, the pattern match is very flexible.
Advantage 2 – You get can handle the same conditions as a nested case but with more concise code.
Advantage 3 – The with statement is an alternative way to do pattern matching on results and doing error handling. I almost think of it as an alternative to railway-oriented programming.

A Word on Elixir versions

Now this fellow named Scott notes that it’s not until Elixir 1.3 that you can use an else block in a with statement2. In Elixir 1.2, you only get the with/do block only.

Summary

I love spaghetti, but not in my code. Hopefully, this example of using the with statement will help you keep your codebase tidy too!

Footnotes

Recent Posts
elixir basics