Functions as First-Class Citizens
In Erlang, functions are ordinary values: you can bind a function to a variable, pass it as an argument to another function, store it in a list or tuple, and return it as a result — this is what it means for functions to be first-class. A higher-order function is simply a function that accepts another function as an argument, returns a function, or both, and this capability is what lets Erlang express generic operations like 'do this to every element' or 'combine every element' without writing a bespoke loop for each specific case.
Cricket analogy: Treating a bowling strategy as something you can hand to any captain — not baked into one specific match plan — is like Erlang treating functions as values; a higher-order function like 'apply this strategy to every over' works for any strategy you pass it, instead of writing a new match plan for each bowler.
Anonymous Functions (Funs)
An anonymous function, or fun, is written fun(X) -> X * X end and can be assigned to a variable like Square = fun(X) -> X * X end, then invoked as Square(4). Funs are closures: a fun created inside another function automatically captures the variables in scope at the point it's defined, so fun(X) -> X + Base end. remembers whatever value Base had when the fun was created, even after the enclosing function has returned and that variable would otherwise be gone.
Cricket analogy: Square = fun(X) -> X * X end is like a coach writing down a specific drill on a card and handing it to any player to run; and a fun that captures Base, the way fun(X) -> X + Base end. remembers a specific target score set before play started, keeps using that target even after the team meeting where it was set has ended.
-module(hof_examples).
-export([apply_twice/2, doubled/1, evens/1, total/1, reversed/1]).
apply_twice(F, X) -> F(F(X)).
doubled(List) ->
lists:map(fun(X) -> X * 2 end, List).
evens(List) ->
lists:filter(fun(X) -> X rem 2 =:= 0 end, List).
total(List) ->
lists:foldl(fun(X, Sum) -> X + Sum end, 0, List).
reversed(List) ->
(fun lists:reverse/1)(List).lists:map, lists:filter, lists:foldl
The lists module ships three workhorse higher-order functions: lists:map(Fun, List) applies Fun to every element and returns a new list of the results, such as lists:map(fun(X) -> X * 2 end, [1,2,3]) yielding [2,4,6]; lists:filter(Pred, List) keeps only the elements for which Pred returns true, such as lists:filter(fun(X) -> X rem 2 =:= 0 end, [1,2,3,4]) yielding [2,4]; and lists:foldl(Fun, Acc0, List) reduces the whole list down to a single accumulated value by threading Fun and a running accumulator through every element, the way lists:foldl(fun(X, Sum) -> X + Sum end, 0, [1,2,3]) yields 6.
Cricket analogy: lists:map is like converting every batsman's raw score to strike rate across the whole scorecard in one pass; lists:filter is like keeping only the batsmen who scored a half-century; and lists:foldl is like totting up the entire team's total runs by adding one player's score to a running total at a time.
The fun Module:Function/Arity syntax, such as fun lists:reverse/1, lets you pass an already-defined named function anywhere a fun is expected, without the overhead or extra clutter of wrapping it as fun(X) -> lists:reverse(X) end — both work identically at the call site.
Writing Your Own Higher-Order Functions
You can define your own higher-order functions the same way the standard library does, accepting a fun as a parameter and applying it inside, as in apply_twice(F, X) -> F(F(X))., which calls apply_twice(fun(X) -> X + 3 end, 10) to get 16. Erlang also lets you reference an already-named function as a fun value using the fun Module:Function/Arity syntax, so fun lists:reverse/1 can be passed anywhere a fun is expected without wrapping it in an extra anonymous fun.
Cricket analogy: apply_twice(F, X) -> F(F(X)) is like running a specific fielding drill on a player twice in a row to reinforce it; and referencing fun lists:reverse/1 directly, instead of wrapping it, is like a coach directly assigning a named specialist coach to a drill roster rather than describing the drill from scratch each time.
A fun created inside a loop-like recursive helper captures whatever variables were in scope at that point, which is convenient for closures but can also accidentally hold references to large data structures longer than expected if the fun itself is stored and kept alive well past when that data is needed.
- Functions are first-class values in Erlang: they can be assigned to variables, passed as arguments, and returned from other functions.
- A higher-order function accepts a function as an argument, returns one, or both.
- Anonymous functions (funs) are written fun(Args) -> Body end and can be invoked like Fun(Args).
- Funs are closures: they automatically capture the variables in scope at their point of definition.
- lists:map/2 transforms every element; lists:filter/2 keeps elements matching a predicate; lists:foldl/3 reduces a list to one value.
- fun Module:Function/Arity references an existing named function as a fun value without wrapping it.
- Custom higher-order functions can accept and apply funs exactly the way the standard library's do.
Practice what you learned
1. What does it mean for functions to be 'first-class' in Erlang?
2. What is a closure in the context of Erlang funs?
3. What does lists:filter(fun(X) -> X rem 2 =:= 0 end, [1,2,3,4]) return?
4. What does lists:foldl(fun(X, Sum) -> X + Sum end, 0, [1,2,3]) evaluate to?
5. What does fun lists:reverse/1 let you do?
Was this page helpful?
You May Also Like
Functions and Modules
A practical guide to organizing Erlang code into modules and functions, covering declarations, multiple clauses, arity, and exporting a public API.
Recursion in Erlang
Why recursion replaces loops in Erlang, and how to write correct, tail-recursive functions that run in constant stack space.
Guards and Clauses
How Erlang guards extend pattern matching with safe boolean conditions to express multi-way branching without if/else.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics