We have a naming convention in Elixir where functions with ! suffixed at the end of its name (e.g. run!) will raise an exception if the function encounters an error.

Enum.fetch! is one example. It has a sibling Enum.fetch function which does not raise exception. Both functions finds the element at the given index. The difference is Enum.fetch! raises OutOfBoundsError if the given position is outside the range of the collection.

Here’s a nice macro you can use to generate bang! versions of your existing non-raising functions.

  @doc """
  Creates bang version of given function
  """
  defmacro bang(result) do
    quote do
      case unquote(result) do
        :ok -> nil
        {:ok, value} -> value
        {:error, error} -> raise error
      end
    end
  end

Let’s see a full example.

defmodule MyModule do

  @doc """
  Creates bang version of given function
  """
  defmacro bang(result) do
    quote do
      case unquote(result) do
        :ok -> nil
        {:ok, value} -> value
        {:error, error} -> raise error
      end
    end
  end

  @spec run(atom) :: {:ok, String.t} | {:error, String.t}
  def run(command) do
    case command do
      :success -> {:ok, "Result"}
      :fail -> {:error, "Some error"}
    end
  end

  @spec run!(atom) :: nil
  def run!(command) do
    bang(run(command))
  end

end

In the above code, we have two functions run and run!. Notice that we using our bang macro to generate the bang version of run.

iex(1)> c "my_module.ex"
[MyModule]
iex(2)> MyModule.run :success
{:ok, "Result"}
iex(3)> MyModule.run :fail
{:error, "Some error"}
iex(4)> MyModule.run! :success
"Result"
iex(5)> MyModule.run! :fail
** (RuntimeError) Some error
    my_module.ex:26: MyModule.run!/1