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.