What Are Higher-Order Functions?
A higher-order function is a function that takes one or more functions as arguments, or returns a function as its result (or both). This is possible because in functional languages, functions are first-class values — they can be passed around just like numbers or strings.
Three higher-order functions appear everywhere in functional code: map, filter, and reduce. Learning them well eliminates the need for most loops and dramatically clarifies intent.
map: Transform Every Element
map applies a function to every element of a collection and returns a new collection of the results. The original collection is not modified.
In Clojure:
(map inc [1 2 3 4 5])
;; => (2 3 4 5 6)
(map #(* % %) [1 2 3 4 5])
;; => (1 4 9 16 25)
In Haskell:
map (+1) [1,2,3,4,5]
-- [2,3,4,5,6]
map (^2) [1,2,3,4,5]
-- [1,4,9,16,25]
Think of map as answering: "What does each element become?"
filter: Keep What You Want
filter takes a predicate function (one that returns true or false) and returns only the elements for which the predicate returns true.
In Clojure:
(filter even? [1 2 3 4 5 6])
;; => (2 4 6)
(filter #(> % 3) [1 2 3 4 5])
;; => (4 5)
In Haskell:
filter even [1,2,3,4,5,6]
-- [2,4,6]
filter (> 3) [1,2,3,4,5]
-- [4,5]
Think of filter as answering: "Which elements satisfy this condition?"
reduce: Collapse a Collection to a Value
reduce (also called fold in Haskell) takes a combining function, an initial value (accumulator), and a collection — and repeatedly applies the function to accumulate a single result.
In Clojure:
(reduce + 0 [1 2 3 4 5])
;; => 15
(reduce (fn [acc x] (conj acc (* x x))) [] [1 2 3])
;; => [1 4 9]
In Haskell:
foldl (+) 0 [1,2,3,4,5]
-- 15
foldl (\acc x -> acc ++ [x^2]) [] [1,2,3]
-- [1,4,9]
Think of reduce as answering: "How do all these elements combine into one result?"
Chaining Them Together
The real power emerges when you chain these functions. In Clojure, the threading macro ->> makes pipelines readable:
(->> (range 1 11) ; (1 2 3 4 5 6 7 8 9 10)
(filter odd?) ; (1 3 5 7 9)
(map #(* % %)) ; (1 9 25 49 81)
(reduce +)) ; => 165
This reads naturally from top to bottom: generate a range, keep the odd numbers, square them, then sum the results. Compare this to an equivalent imperative loop — the functional version expresses intent far more clearly.
When to Use Each
| Function | Use When You Want To… |
|---|---|
map | Transform every element; output same size as input |
filter | Select a subset; output is same type, smaller or equal size |
reduce | Aggregate, summarize, or restructure a collection into one value |
Once map, filter, and reduce become second nature, you'll find yourself writing less boilerplate, making fewer bugs, and expressing complex data transformations in just a few elegant lines.