EarlyReturn

Early return support for Elixir functions.

Installation

Add early_return to your list of dependencies in mix.exs:

def deps do
  [
    {:early_return, "~> 1.0.0"}
  ]
end

Usage

Add use EarlyReturn to any module to enable the return macro:

defmodule MyModule do
  use EarlyReturn

  def divide(a, b) do
    if b == 0, do: return {:error, :division_by_zero}

    {:ok, a / b}
  end
end

return with no arguments returns nil:

def maybe_process(data) do
  if invalid?(data), do: return()

  do_work(data)
end

Both def and defp are supported. User-defined catch and rescue blocks are preserved:

def fetch(url) do
  if cached = Cache.get(url), do: return cached

  HTTP.get!(url)
rescue
  e in HTTP.Error -> {:error, e.reason}
end

How it works

use EarlyReturn replaces Kernel.def/2 and Kernel.defp/2 with versions that wrap function bodies in a catch block. The return macro expands to a throw, which is caught by the wrapping catch and returned as the function’s result.

Caveats

Non-local return from anonymous functions

return inside a fn will return from the enclosing def, not from the fn itself. This is similar to Ruby’s return inside blocks:

def first_even(list) do
  Enum.each(list, fn x ->
    if rem(x, 2) == 0, do: return x  # returns from first_even, not the fn
  end)

  nil
end

No process boundary crossing

throw does not cross process boundaries. Using return inside spawn, Task.async, or similar will crash the child process with {:nocatch, {:early_return, value}}.

Interaction with user-defined try/catch

If you write your own try/catch inside the function body with a catch-all clause, it will intercept the return throw before it reaches the function-level handler:

def example do
  try do
    return :early  # caught by the try below, not by def
  catch
    x -> x  # swallows the return
  end
end

Formatter

To allow return value without parentheses, add :early_return to your formatter’s :import_deps:

# .formatter.exs
[
  import_deps: [:early_return],
  inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]