Then

Elixir CIHex.pm VersionHex.pm DownloadsDocumentation

Because sometimes you want to do something after a function, but don't want to clutter its code.

Put @then :callback or @then {Module, :callback} above a function and it will automatically call the callback after execution. Function result stays unchanged, callback is called for side effects.

Installation

def deps do
  [{:then, "~> 1.2.0"}]
end

Basic Usage

Simple Local Callbacks

defmodule MyModule do
  use Then

  @then :log
  def add(a, b) do
    a + b
  end

  def log(result) do
    IO.puts("Got #{result}")
  end
end

MyModule.add(2, 3)
# Got 5
# => 5

External Module Callbacks

You can also call functions from other modules:

defmodule Calculator do
  use Then

  @then {IO, :puts}
  def multiply(a, b) do
    a * b
  end

  @then {MyAudit, :track_operation}
  def divide(a, b) when b != 0 do
    a / b
  end
end

defmodule MyAudit do
  def track_operation(result) do
    IO.puts("Operation completed with result: #{result}")
  end
end

Aliased modules work too:

defmodule Calculator do
  alias IO, as: MyIO
  use Then

  @then {MyIO, :puts}
  def calculate(x) do
    x * 2
  end
end

Real-world Example

defmodule UserService do
  use Then

  @then :audit_creation
  def new_user(params) do
    case params do
      %{email: email, name: name} -> {:ok, %User{name: name, email: email}}
      _ -> {:error, "Required fields are missing"}
    end
  end

  # side effects separately
  def audit_creation({:ok, user}), do: IO.puts("✅ User #{user.email} created")
  def audit_creation({:error, reason}), do: IO.puts("❌ User creation failed: #{reason}")
end

Compatibility

Works perfectly with other function attributes:

defmodule MyService do
  use Then

  @doc "Gets age from params"
  @spec get_age(map()) :: integer()
  @then :log_term
  def get_age(params) do
    params[:age] || 0
  end

  defp log_term(term), do: IO.puts("[log-term] #{term}")
end

@spec, @doc, @deprecated and other attributes work as expected. Callback functions can be private (defp).

Limitations

Callback Formats

@then accepts two formats:

Why Use This?

It's simple and clear. Move log and other side-effects out of your b