 
       iex> opts = %{width: 10, height: 15}
       iex> with {:ok, width} <- Map.fetch(opts, :width),
-      ...>      {:ok, height} <- Map.fetch(opts, :height),
-      ...>      do: {:ok, width * height}
+      ...>      {: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.


      iex> opts = %{width: 10}
      iex> with {:ok, width} <- Map.fetch(opts, :width),
      ...>      {:ok, height} <- Map.fetch(opts, :height),
      ...>      do: {:ok, width * height}
      :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)}
      {:ok, "admin"}

  As in `for/1`, variables bound inside `with/1` won't leak;


      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:
