Skip to content
Anonymous Functions and the Capture Operator
step 1/7

Reading — step 1 of 7

Learn

~3 min readAtoms, Anonymous Functions, Comprehensions

Elixir is a functional language — functions are values. Anonymous functions, the capture operator &, and partial application are daily-use tools.

fn — anonymous functions

add = fn a, b -> a + b end
add.(3, 4)                  # 7 — note the DOT

square = fn n -> n * n end
square.(5)                   # 25

Anonymous functions REQUIRE the dot to call: fun.(args). Named functions DON'T: Module.fun(args). Easy to forget.

Multi-clause anonymous functions

classify = fn
    n when n < 0 -> :negative
    0 -> :zero
    n when n > 0 -> :positive
end

classify.(-5)        # :negative
classify.(0)         # :zero
classify.(7)          # :positive

Like a case expression but as a value. Useful for callbacks with branching logic.

& — capture operator (function references)

Get a reference to a NAMED function:

upcase = &String.upcase/1     # captures String.upcase, arity 1
upcase.("hello")              # "HELLO"

# Use as a value:
Enum.map(["a", "b", "c"], &String.upcase/1)   # ["A", "B", "C"]

The syntax: &Module.function/arity. Arity matters because Elixir distinguishes String.split/2 (2 args) from String.split/3 (3 args).

& — shorthand for anonymous functions

The & operator also creates anonymous functions inline:

# These are equivalent:
fn x -> x * 2 end
&(&1 * 2)

# Multiple args:
fn a, b -> a + b end
&(&1 + &2)

# Used heavily in pipelines:
Enum.map(nums, &(&1 * 2))
Enum.filter(nums, &(&1 > 5))

&1, &2, ... refer to the first, second, ... argument. Concise for simple expressions; harder to read for complex ones.

Rule of thumb: & for one-line callbacks. fn ... -> ... end for anything with multiple steps.

Higher-order functions

# Pass functions as arguments:
Enum.map([1, 2, 3], fn n -> n * 2 end)
Enum.map([1, 2, 3], &(&1 * 2))
Enum.map([1, 2, 3], &double/1)        # if `double` is named

# Return functions:
defmodule Multiplier do
    def by(n), do: fn x -> x * n end
end

times_3 = Multiplier.by(3)
times_3.(7)                  # 21 — closure captured n=3

Closures capture their environment. Each call to Multiplier.by/1 creates a fresh closure.

Partial application via the capture operator

add = fn a, b -> a + b end
add_five = &add.(5, &1)       # bind first arg, leave second free
add_five.(10)                  # 15

# More common with named functions:
increment = &Kernel.+(&1, 1)
increment.(5)                  # 6

Not strict partial application (you can't "curry" arbitrarily) but covers most needs.

Pipelines with capture

[1, 2, 3, 4, 5]
|> Enum.filter(&(&1 > 2))
|> Enum.map(&(&1 * &1))
|> Enum.sum()
# 50 (= 9 + 16 + 25)

Canonical Elixir style: pipeline of small & lambdas.

Common mistakes

  • Forgetting the dot to calladd(1, 2) for an anonymous function fails. Must be add.(1, 2).
  • Using & shorthand for complex bodies&(&1 |> something |> &2) is unreadable. Use fn for non-trivial logic.
  • Mistaking &1 for $1 — different syntax. &1 is bash; & is Elixir's positional placeholder.
  • Forgetting arity on captures&String.upcase doesn't compile; you need &String.upcase/1.
  • Confusing & capture vs & placeholder — context-dependent. &Module.fun/arity is capture; &(...) with &N inside is placeholder syntax.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…