I use with
for all pipelines where the functions can fail (i.e. return :ok
or :error
tuples) and we want to break out of the pipeline to deal with it and |>
for simpler pipelines which either can never fail or throw when they do. This reduces the use of nested and confusing case
or if
statements.
Some contrived examples:
def build(params) do
%User{}
|> cast(params, [:name, :age])
|> apply_changes()
end
def convert_to_csv(source_filename) do
with {:l1, {:ok, :xlsx}} <- {:l1, detect_file_type(source_filename)},
{:l2, {:ok, csv_filename}} <- {:l2, convert_excel_to_csv(source_filename)} do
{:ok, csv_filename}
else
{:l1, {:error, error}} -> {:error, error} # (such as file not found)
{:l1, {:ok, _other}} -> {:error, :not_an_excel_file}
{:l2, error} -> error
end
Note wrapping the tuples in the with
statement with their line numbers which I found necessary to disambiguate the else clauses. I’m not sure there’s a better way of doing that without a third-party library.