Using else in a def macro

I was looking at some code, and I found something like this:

defmodule Test do
  def test(a) do
    IO.inspect a
  else
    r -> IO.inspect "from else #{r}"
  end 
end

The output is:

10
from else 10

What would be the use-case for such code and how it works?

It is accidental design quirk. The thing is that whole “do-block” in def* functions is de facto try-block, so these two:

def test(a) do
  try do
    IO.inspect(a)
  else
    r -> IO.inspect("from else #{r}")
  end
end

Is de facto the same as:

def test(a) do
  IO.inspect(a)
else
  r -> IO.inspect("from else #{r}")
end

I assume that Elixir uses such approach to convert the errors from Erlang errors to Elixir exceptions on function boundary in case if it is called from other BEAM language, but I am not 100% sure. In short, it is design decision that is there and we need to live with it at least till Elixir 2.

This approach is often used for early returns from functions using throw/1 and catch block in try:

def foo(a) do
  # some computation
  if discombobulated?(a), do: throw(:early_exit)
  # more computations
  {:ok, result}
catch
  :early_exit -> {:error, {:discombobulated, a}}
end
6 Likes

Even the compilation warning is strange:

warning: “else” shouldn’t be used as the only clause in “def”, use “case” instead