Closures in Elixir

Elixir supports anonymous functions, which can form closures. Anonymous functions are useful when you want to define a function inline without having to name it.

defmodule Closures do
  # This function `int_seq` returns another function, which
  # we define anonymously in the body of `int_seq`. The
  # returned function closes over the variable `i` to
  # form a closure.
  def int_seq do
    {:ok, agent} = Agent.start_link(fn -> 0 end)
    fn ->
      Agent.get_and_update(agent, fn i ->
        {i + 1, i + 1}
      end)
    end
  end

  def main do
    # We call `int_seq`, assigning the result (a function)
    # to `next_int`. This function value captures its
    # own state, which will be updated each time
    # we call `next_int`.
    next_int = int_seq()

    # See the effect of the closure by calling `next_int`
    # a few times.
    IO.puts(next_int.())
    IO.puts(next_int.())
    IO.puts(next_int.())

    # To confirm that the state is unique to that
    # particular function, create and test a new one.
    new_ints = int_seq()
    IO.puts(new_ints.())
  end
end

Closures.main()

To run this Elixir script, save it to a file (e.g., closures.exs) and execute it using:

$ elixir closures.exs
1
2
3
1

In Elixir, we use an Agent to maintain state in our closure, as Elixir is a functional language and doesn’t have mutable variables. The Agent allows us to store and update state in a concurrent-safe manner.

The int_seq function returns an anonymous function that, when called, increments and returns a counter. Each call to int_seq creates a new Agent with its own state, allowing multiple independent counters.

The last feature of functions we’ll look at for now is recursion, which is a natural fit for Elixir’s functional paradigm.