A saner way to deal with complex flows
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 case
s or if
s.
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!