With and else

How to use with and else statement for control structure

Using the with statement is a helpful control feature within Elixir. If you have a sequence of tasks that rely on the success of the previous task in order to work, then using with can improvide how your structure that sequence.

In my example I used to have a parse() function that used to look like this:

def parse(filepath_to_csv) do
	filepath_to_csv
	|> File.read_csv
	|> preview
	|> create_changeset_list
	|> insert_into_database
end

Control Structure

Every statement of with is executed in order. Execution will continue as long as right function returns a match to to left tuple.

So if preview(filepath_to_csv) returns {:ok, list_of_maps} then list_of_maps is passed down to the next function create_list_of_changeset(list_of_maps). And if that returns a match of {:ok, list_of_changesets} then that gets passed into insert_update_into_db(list_of_changeset). And if that returns {ok, result} then we are at the end and the functions within the do statement get executed.

If any errors occur the else will pattern match on the error.

def parse(filepath_to_csv)  do
      with {:ok, list_of_maps}       <- preview(filepath_to_csv),
           {:ok, list_of_changesets} <- create_list_of_changeset(list_of_maps),
           {:ok, result}             <- insert_update_into_db(list_of_changesets)
      do
           {:completed, result}
      else
        {:error, failed_operation, failed_value, changes_so_far} -> IO.inspect "#{failed_operation}, #{failed_value}, #{changes_so_far}"
        error -> {:error, error}
      end
end

Summary

One of the outcomes of using with and else is your functions within the body become isolated. This gives you the benefit of testing out the individual functions in isolation and then making the code easier to reason and read in the future.