Defer in Elixir

In Elixir, we don’t have a direct equivalent to the defer keyword. However, we can achieve similar functionality using the try, rescue, and after constructs. Here’s how we can implement the same behavior:

defmodule DeferExample do
  def main do
    {:ok, file} = create_file("/tmp/defer.txt")
    try do
      write_file(file)
    after
      close_file(file)
    end
  end

  defp create_file(path) do
    IO.puts("creating")
    case File.open(path, [:write]) do
      {:ok, file} -> {:ok, file}
      {:error, reason} -> raise "Failed to create file: #{reason}"
    end
  end

  defp write_file(file) do
    IO.puts("writing")
    IO.write(file, "data\n")
  end

  defp close_file(file) do
    IO.puts("closing")
    case File.close(file) do
      :ok -> :ok
      {:error, reason} ->
        IO.puts(:stderr, "error: #{reason}")
        System.halt(1)
    end
  end
end

In this Elixir version, we use a try block to ensure that the file is closed after writing, regardless of whether an exception occurs or not. The after clause in the try block serves a similar purpose to defer in the original example.

Let’s break down the main parts:

  1. The main function opens a file, writes to it, and ensures it’s closed.

  2. create_file opens a file for writing and returns the file handle. If there’s an error, it raises an exception.

  3. write_file writes data to the file.

  4. close_file closes the file and checks for errors. If there’s an error during closing, it prints the error and exits the program.

To run this program:

$ elixir defer_example.exs
creating
writing
closing

This Elixir implementation demonstrates how to ensure that certain operations (like closing a file) are performed, even if exceptions occur during the execution of the main logic. While it’s not exactly the same as Go’s defer, it achieves a similar result in terms of resource management and cleanup.