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"}
]
endUsage
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
endreturn 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}
endHow 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
endNo 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
endFormatter
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}"]
]