A Saner way to deal with complex flows

cover Making it flow

Yes, finally! We now have in Elixir the with keyword! (See what I did there with the post title?)

In a gist, with allows you to sequence function calls and return a certain value, unless one of the functions returns something else.

But what does that actually mean? Allow me to explain with a real life sample of something that was a pain to deal with previously: sequential flows.

Let's say you have a User record that you want to insert in your database, but you want to make sure that:

  • Values that have been provided are correct

  • The record is inserted in the database

  • An e-mail is sent afterwards.

Given there is validation, database operation and communication with external services, things can go sideways. Before with you'd likely have to do something like:

Having the code layout like this allows us to keep a clean public API function, but the private functions always have to take into account that an {:error, ...} can be received as an argument. Variations on this exist using cases or ifs.

Not pretty and highly coupling: the definition of a function depends on the different outputs form other.

Enter with!

The beauty of with is that we now don't have to worry about taking special care of error cases. As long as the function on the right of <- successfuly pattern matches the definition on the left of <-, the next block will be executed.

At the end, the do block is returned. In the case above, that'd be {:ok, user, "foobar"}.

If, for example, create_user/1 returned {:error, [:id, " primary key violation"]}, then this would be the value returning from with and send_email would never be executed. Awesomeness!

Hope I helped shedding some light to with, here are the official docs for good measure.

Check my other Elixir posts here.

Thanks for reading!