anonymous_functions

Learn how to define and use anonymous functions (closures) in Elixir. Understand their syntax, scope, and how they capture variables with examples.

Elixir Anonymous Functions

Understanding Elixir Anonymous Functions

Anonymous functions, also known as closures in Elixir, are functions without a name that can be defined and passed around as values. They are fundamental to functional programming and are often used for concise, inline operations. These functions capture variables from their surrounding scope at the time of their definition, ensuring that the captured values remain consistent even if the original variables are reassigned.

Defining Anonymous Functions

Anonymous functions in Elixir take the general form of fn ... end. They can accept arguments and contain multiple clauses, similar to case statements. Each clause can have pattern matching and guards.

fn (1, 2, 3) -> IO.inspect 1*2*3
   (a, b, c) -> IO.inspect [a,b,c]
end

Invoking Anonymous Functions

Anonymous functions are invoked using the dot (.) operator followed by the arguments in parentheses. This syntax distinguishes them from named function calls.

> add_2 = fn num -> num + 2 end
#      ⇣called with a dot
> add_2.(127) == 129
true
> three = 3
> add_3 = fn num -> num + three end
> add_3.(262) == 265
true
#   ⇣is our function compromised?
> three = 7
#                ⇣no, `three` is still 3 in the function
> add_3.(262) == 265
true

Scope and Variable Capture

A key characteristic of Elixir's anonymous functions is their scope management. Variables defined outside an anonymous function are captured by value at the time the function is defined. This means that subsequent changes to the external variable do not affect the function's behavior.

Furthermore, anonymous functions do not leak their internal scope. Variables defined within an anonymous function are local to that function and do not affect the surrounding environment.

> support_method = fn ("suspenders")   -> IO.puts "Hey big spender."
                      ("belt")         -> IO.puts "Who is the real hero?"
                      a when is_list a -> Enum.each(a, support_method)
                      _                -> IO.puts "What is this wizzardry?"
                   end
> support_method.(["belt", "suspenders"])
"Who is the real hero?"
"Hey big spender."
> peanut_butter = "smooth"
#                                      ⇣immediately call
> fn () -> peanut_butter = "chunky" end.()
> peanut_butter == "smooth"
true # scope was not leaked

Arity Limitations

Unlike named functions, anonymous functions in Elixir cannot have multiple arities (the number of arguments they accept). Attempting to define an anonymous function with different arities will result in a compile-time error.

> fn (a)    -> "One"
     (a, b) -> "Two"
  end
# That's a compile error

Further Reading