Elixir's With Statement

07 Nov 2016

A reason that I'm such a huge fan of Elixir is that everything just seems to click. The code is elegant and expressive while managing to be efficient.

One thing that hasn't clicked until just now is the with statement. It turns out that it's pretty easy to understand and really quite useful. It addresses a specific and not-uncommon problem in a very Elixir-way. What's the problem? Pretend we have the following:

defmodule User do
defstruct name: nil, dob: nil
def create(params) do
end
end

The create function should either create a User struct or return an error if the params are invalid. How would you go about doing this? Here's an example using pipes:

The problem with this approach is that every function in the chain needs to handle the case where any function before it returned an error. It's clumsy, both because it isn't pretty and because it isn't flexible. Any new return type that we introduced has to be handled by all functions in the chain.

The pipe operator is great when all functions are acting on a consistent piece of data. It falls apart when we introduce variability. That's where with comes in. with is a lot like a |> except that it allows you to match each intermediary result.

Every statement of with is executed in order. Execution continues as long as left <- right match. As soon as a match fails, the else block is executed. Within the else block we can match against whatever WAS returned. If all statements match, the do block is executed and has access to all the local variables in the with block.

Got it? Here's a test. Why did we have to change our success cases so that they'd return {:ok, dob} and {:ok, name} instead of just dob and name?