Higher-order functions: when functions take functions

LLM-authored, human-reviewed

Foundations

A function that doubles its input is a function. A function that applies a doubling function is different: it is built from another function. That is what the higher_order exercises in the functions exercises practice. They are the last stop before lambda calculus, where functions are the only primitive and everything comes from wiring functions together.

Functions are values first

Higher-order functions work because a function is a value, not a special kind of statement. You can name one, pass it to another function, or return one as a result. Numbers work the same way. This is what “first-class function” means, and every programming language on this site makes that assumption:

const triple = x => 3 * x;      // a function, as a value
const h = x => triple(x) + x;   // a function built from that value
h(4);   // triple(4) + 4 = 12 + 4 = 16

The helper triple appears inside h just as a constant like 2 would. You name it, then use it. The higher-order exercises ask you to say what the code does: “h is built from a helper that triples its input; h applies the helper, then adds the original input.” Turn that sentence into 3x+x3x + x, then evaluate it.

The three building patterns

The exercises use three ways to build a function from helper functions. Name them. Each is a template you can recognize.

Apply once. Start with a helper and apply it to the input. You may reuse the input afterward. “h applies the tripling helper to the input, then adds the original input to the result” is 3x+x3x + x. The input appears twice: once inside the helper and once outside it.

Self-composition. Apply the helper, then apply it again to the result. “d applies a tripling helper, then applies the tripling helper again to that result” is 3(3x)3(3x). This is composition (its own article) with the same function in both slots: a machine that triples a triple.

The identity. The helper that changes nothing, id(x) = x, is the neutral element of this whole business. “n applies the identity function, then multiplies that result by itself” is x2x^2, because applying the identity does nothing before the squaring. The identity is not a trick question. It shows that “apply a function, then use the result” is the same machine even when the function does nothing.

Use the same translation each time: read the description, find the helper, write the expression it produces, and evaluate. “Higher order” is not harder than evaluate_fn. It is evaluate_fn with a helper function named in the problem instead of a formula given as a rule.

The code bridge, and where it points

Elixir makes self-composition look almost like the prose:

triple = fn x -> 3 * x end
d = fn x -> triple.(triple.(x)) end
d.(2)   # 3 * (3 * 2) = 18

The dot in triple.(x) is the call. triple.(triple.(x)) is self-composition: apply triple, then apply it again to the result. The identity in Elixir is fn x -> x end. Composing it into anything leaves that thing unchanged.

This topic comes right before lambda calculus because lambda calculus takes “functions are values” to its endpoint. There are no numbers, no booleans, and no built-in operations—only functions and one rule for applying one to another. A higher-order function like d, “triple applied twice,” is already a small lambda-calculus program in disguise. It is built entirely by composing a helper with itself. Lambda calculus adds the λx. notation for “the function of x.”

Why this matters here

The higher_order exercises are the on-ramp to introducing lambda calculus. They get you comfortable building functions from functions with nothing but application, before the notation changes and the built-ins disappear. Once “build a function from a helper, then evaluate it” is automatic, the first lambda exercises—(λx. x) y reduces to y, (λx. λy. x) a b reduces to a—are the same move with different spelling.

Where to go next

Related exercises

← Back to articles