Deference

Hex.pm

A function deferring library inspired by zig!

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:

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:

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.

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