# EarlyReturn
Early return support for Elixir functions.
## Installation
Add `early_return` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:early_return, "~> 1.0.0"}
]
end
```
## Usage
Add `use EarlyReturn` to any module to enable the `return` macro:
```elixir
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`:
```elixir
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:
```elixir
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:
```elixir
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:
```elixir
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`:
```elixir
# .formatter.exs
[
import_deps: [:early_return],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
```