Resolving Dialyzer Issues in Elixir

Until set theoretic types arrive Dialyzer remains a useful tool for catching type errors, but can be tricky to debug.

 A spilled, melted ice cream cone.
Photo by Sarah Kilian / Unsplash - Just a fun image that came up when searching "Error"

Set theoretics types are coming in Elixir, but haven't landed just yet. Until then we've got Dialyzer to help us catch type errors, but the error messages aren't always the most friendly to read, so I wanted to a few tips for handling them.

Generally I see two kinds of issues, depending on if you're working on a piece of code that already has validated type specs, or if you're working on a piece that is just getting them added.

Starting at the bottom

If you're working on a piece of code where you're just adding type specs, generally you'll get at long list of type errors, because generally there is actually an issue/bug that you need to address, where the type you're passing in doesn't match.

For these it's generally best to scroll down to the very bottom of the list of errors, start with that one, and work your way up.

You'll want to be looking for an error where something you're passing in may genuinely not match. Generally those errors up the stack are actually fine, and if you address the one at the bottom, the rest all go away.

Starting at the top

I put this heuristic second because I find it's generally less useful or applicable.

If you've got pieces of code that already have validated type specs, and suddenly an error appears, you'll get a single error, but it's obscuring a number of new hidden errors, because down the stack you haven't addressed the new type, but Dialyzer is assuming your type specs down the stack are actually correct, and you need to update the type specs (and possibly functions) down the stack.

This one generally occurs when you allow a function to take a new type, for example if previously your function had taken a User.t and now you want it to take a User.t or UnvalidatedUser.t, and it calls to multiple functions where this old type is passed.

Dialyzer is extremely literal, so even if the only thing your functions actually need is a map with the key :email your User and UnvalidatedUser are what it is going to check for, so look at your code accordingly.

Wrapping Up

Until set theoretic types arrive, I find Dialyzer is still really useful in finding and preventing bugs.

It can take some time to learn how to read and use the error messages, but hopefully these tips help you a bit to get more use out of it.