# Deference
[](https://hex.pm/packages/deference)
<!-- [](https://hexdocs.pm/deference) -->
A function deferring library inspired by [zig](https://zig.guide/language-basics/defer/)!
The main purpose is chaining multiple API or operations which could fail for any number of reasons, while
also requiring cleanup operations to be performed. The most important bits are `with_defer/1`, `defer/1`, and `throw_deferred/1`.
Interface is *not* stable, but here's how to do it:
```ex
import Deference
def example() do
with_defer do
{:ok, user_id} =
API.User.create("really_cool_username", "really_cool_password")
defer do
API.User.delete("really_cool_username")
end
post_id =
API.Post.create("really_cool_username", "hello")
|> case do
{:ok, post_id} -> post_id
{:error, _reason} ->
# can't post, no reason to keep the user around
throw_deferred({:error, :failed_to_post})
end
defer do
API.Post.delete(post_id)
end
API.Post.edit(post_id, "hello\nedit: wow i didn't expect this to blow up")
|> case do
{:ok, post_id} -> :ok
{:error, _reason} ->
# failed to edit post, bail!
throw_deferred({:error, :failed_to_edit_post})
end
end
end
```
Deferred operations are collected and called in the reverse order they are called in.
In the case above, if the post failed to edit, then first it would call `API.Post.delete(post_id)`, then `API.User.delete("really_cool_username")`.
In case it's needed, `with_defer_fwd/1` allows you to call deferred functions in the order specified.
If `throw_deferred/1` is not called, then the block returns as normal, e.g:
```ex
with_defer do
defer do
:logger.error("this shouldn't happen!")
end
if 1 == 2 do
throw_deferred()
else
:ok
end
end
```
This will always resolve as `:ok` with no side effects.
`throw_deferred/1` also allows you an early return path. Whatever term is provided will be the return for the whole block, defaulting to `:error`.
```ex
with_defer do
# some stuff
throw_deferred({:error, :hello})
# some other stuff
:ok
end
```
Will always evaluate as `{:error, :hello}` stopping execution at the throw