Kernel.SpecialForms.with

You're seeing just the macro with, go back to Kernel.SpecialForms module for more information.

Used to combine matching clauses.

Let's start with an example:

iex> opts = %{width: 10, height: 15}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> end
{:ok, 150}

If all clauses match, the do block is executed, returning its result. Otherwise the chain is aborted and the non-matched value is returned:

iex> opts = %{width: 10}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> end
:error

Guards can be used in patterns as well:

iex> users = %{"melany" => "guest", "bob" => :admin}
iex> with {:ok, role} when not is_binary(role) <- Map.fetch(users, "bob") do
...>   {:ok, to_string(role)}
...> end
{:ok, "admin"}

As in for/1, variables bound inside with/1 won't leak. Expressions without <- may also be used in clauses. For instance, you can perform regular matches with the = operator:

iex> width = nil
iex> opts = %{width: 10, height: 15}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      double_width = width * 2,
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, double_width * height}
...> end
{:ok, 300}
iex> width
nil

The behaviour of any expression in a clause is the same as outside. For example, = will raise a MatchError instead of returning the non-matched value:

with :foo = :bar, do: :ok
** (MatchError) no match of right hand side value: :bar

As with any other function or macro call in Elixir, explicit parens can also be used around the arguments before the do/end block:

iex> opts = %{width: 10, height: 15}
iex> with(
...>   {:ok, width} <- Map.fetch(opts, :width),
...>   {:ok, height} <- Map.fetch(opts, :height)
...> ) do
...>   {:ok, width * height}
...> end
{:ok, 150}

The choice between parens and no parens is a matter of preference.

An else option can be given to modify what is being returned from with in the case of a failed match:

iex> opts = %{width: 10}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> else
...>   :error ->
...>     {:error, :wrong_data}
...>
...>   _other_error ->
...>     :unexpected_error
...> end
{:error, :wrong_data}

The else block works like a case clause: it can have multiple clauses, and the first match will be used. Variables bound inside with (such as width in this example) are not available in the else block.

If an else block is used and there are no matching clauses, a WithClauseError exception is raised.