F2 · Functional Programming
Higher-order functions
In a functional language, functions are values like any other. A higher-order function is one that takes a function as an argument or returns one — so you can pass behaviour around the way you pass a number or a list.
F1.07 met this idea in the abstract: the sum and product operators each took a function and
applied it across a range. Here it is the everyday tool. Enum.map takes a function
and runs it over a list; Enum.filter takes a test; Enum.reduce
takes a combiner. And a function can build and return another function. Writing the iteration once and
passing in the part that varies is how functional code stays small.
Passing a function
The clearest higher-order function takes another function as an argument.
Enum.map is the canonical one: it walks a list and applies whatever function you
hand it to each element. The list operation is fixed; the behaviour is a parameter. Swap the function and
the same call does something new.
Choose the function passed to map.
- higher-order function
- a function that takes a function as an argument or returns one.
- first-class
- functions are values — they can be passed, stored, and returned.
- capture operator
&makes a function value, e.g.&(&1 * 2)or&String.upcase/1.- function factory
- a function whose result is another function.
Enum.map · the behaviour is an argument
F1.07 · operators
Sum and product each took a function and applied it across a range.
Elixir
Enum.map(list, fun) applies fun to every element — the function is the argument.
One operation, many behaviours. The iteration is written once; the part that changes is a function you pass in.
One shape, many roles
Different higher-order functions expect functions of different shapes. map wants a
transformer, one value to another; filter wants a test that returns true or false;
reduce wants a combiner that folds an element into an accumulator;
sort_by wants a key to compare on. The signature tells you the role.
Inspect what each one asks for.
The function each one expects
Idea
The combinator is generic; the function you pass gives it a specific job.
Elixir
transformer for map, predicate for filter, combiner for reduce.
Reading the signature of the function an operator wants tells you what the operator does — a transformer maps, a predicate filters, a combiner reduces.
Returning a function
A higher-order function can also return a function. A factory takes some value and hands back a
new function that uses it. adder takes n and returns a
function that adds n to its argument — the returned function carries
n with it. Calling the factory with different values builds different functions.
Set the captured value, then apply the function it builds.
A function factory · build, then apply
Idea
A function that returns a function lets you fix one input now and the rest later.
Elixir
fn n -> fn x -> x + n end end — the inner function keeps n.
Returning a function captures a value for later use. That captured environment is the closure — the subject of F2.06.
What this lands
Higher-order functions follow from treating functions as values. Pass one into a combinator and the
combinator is parameterised by behaviour: map transforms, filter
tests, reduce combines — the same idea F1.07 drew as operators over a function.
Return one and you have a factory that fixes an input now and the rest later, carrying that value in a
closure. Both halves let you write the fixed structure once and vary the behaviour. F2.04 turns to the
oldest way to express that structure over a collection: recursion, done so it runs in constant stack space.
Next: F2.04 — Recursion patterns & tail calls — accumulators and tail-call optimisation. The full chapter path is on the F2 overview.