The idea
Two functions, wired end to end. The output of one becomes the input of the next, and the pair behaves as a single new function. What makes this more than shorthand is that the result is itself a function — a value you can name, store, pass, and compose again. Composition is closed, and that closure property is why it scales where nesting does not.
Point-free — the argument you never name
"Point" is the mathematician's word for an argument. Point-free means argument-free: the pipeline is assembled from named stages rather than described as a thing that happens to a value. Both versions below compute the same answer; the difference is structure. In the point-free one, normalize exists as a reusable, testable thing. In the other it is buried inside a nest of calls.
The mathematics
Given g : A → B and f : B → C, the composite is (f ∘ g)(x) = f(g(x)). Three properties carry all the weight — and each one is checkable here rather than merely assertable.
Associativity plus identity is precisely the definition of a monoid. Add a record of which arrows may legally follow which and it becomes a category. That is not decoration applied afterwards: when Eilenberg and Mac Lane founded category theory in 1945, they took composition as the primitive. A category is objects, arrows, an associative composition, and an identity arrow per object. Functions under composition became one example among many rather than the definition — which is why nearly every structural idea since is stated in terms of composition rather than in terms of functions.
History
Substitutions, before functions — early 1800s
Composition entered mathematics through group theory, before function had its modern meaning. Galois and Cauchy studied substitutions — rearrangements of the roots of a polynomial — and the operation that mattered was performing one after another. Read the group axioms literally and they are the axioms of composition: associativity, an identity, an inverse for every element. The subject was composition before anyone called it that.
Schönfinkel, 1924 — composition as an object
Moses Schönfinkel showed that all of logic's variable binding could be eliminated in favour of a handful of combinators — functions built from nothing but application. One of them was composition itself: B f g x = f (g x). Haskell Curry rediscovered and systematised the work from the late 1920s, founding combinatory logic; Raymond Smullyan later gave the combinators bird names, where B is the bluebird. The same paper is the origin of currying — the reason it is not called schönfinkeling is an accident of who popularised it.
Backus, 1977 — composition as a discipline
John Backus used his Turing Award lecture, Can Programming Be Liberated from the von Neumann Style?, to argue against assignment-driven programming and for a language of combining forms: operators that build programs out of programs. Composition was the headline combining form, and the lecture is the direct ancestor of point-free style as a programming practice rather than a mathematical convenience.
Unix pipes, 1973 — composition for everyone
Doug McIlroy's pipe put composition in the hands of people who had never heard of a combinator. cat f | grep x | sort | uniq -c is a composition of four functions over streams, and is very likely the most-used implementation of the idea in history. Note the reading direction: left to right. Every pipeline notation since has followed the shell rather than the textbook — which is the root of the two-directions problem every functional language has had to solve.
APL, J, and Haskell
Iverson's APL used ∘ for composition, and J developed tacit programming to a high art with hooks and forks — entire programs written with no variables at all. J's +/ % # is the mean, with no argument named anywhere; Axioma's converge below is the same idea, named. Haskell's . operator, right to left, combined with currying made point-free style ordinary rather than exotic — and gave us the self-deprecating pun that point-free taken too far becomes pointless.
In the lambda calculus
The lambda calculus has three constructs: variables, abstraction, application. There is no composition operator — so composition must be built, and that is the interesting part. Schönfinkel's B combinator is λf. λg. λx. f (g x): given f, given g, return a function that takes x, feeds it to g, then feeds that result to f.
Because composition is defined rather than assumed, associativity becomes a theorem. Both groupings β-reduce to the same normal form — B (B h g) f x and B h (B g f) x both become h (g (f x)). That is what associativity means, discharged mechanically rather than posited.
Axioma's four spellings
Axioma ships both directions, deliberately distinct. All four route through one implementation, so they can differ in reading order and never in behaviour.
Why two directions, and why neither could be dropped
This looks like redundancy and is not. ∘ cannot be left-to-right: on functions the glyph has one universal textbook meaning — f ∘ g applies g first — and a language whose premise is that textbook notation runs verbatim cannot ship it reversed. compose cannot be right-to-left: it also composes relations, where compose(R, S) is {(x,z) | (x,y) ∈ R ∧ (y,z) ∈ S}, left to right. A function is its graph, so composing two function graphs and composing the two functions must agree. Tabulating settles it:
Both readings are forced. They are mirrors, not aliases — which is why neither is documented as shorthand for the other. If you only ever want one, use >>: it reads in the order things happen.
The empty composition
Composition has an identity element, so the zero- and one-stage cases return it rather than refusing. That is what makes a stage list foldable at any length — including a configuration that turned out to be empty.
One gotcha worth internalising: calls bind tighter than composition. f >> g(3) reads as f >> (g(3)) and errors. Parenthesise the composition before calling it — exactly as you would on paper.
The combinator kit
Axioma ships more of Schönfinkel's toolbox than most languages. Each returns a function, so each composes with the others.
Beyond the chain
Composition builds a chain: one value in, one out, one stage at a time. Two shapes it cannot build come up constantly, and without them point-free style collapses back to naming the argument purely in order to mention it twice.
converge — fork, then rejoin
A mean needs the sum and the count of the same list, divided. That is a branch, not a chain. This is J's fork, with the pieces named.
on — preprocess both arguments
The shape composition provably cannot express: cmp ∘ key would feed one result to a function expecting two. on(cmp, key)(a, b) is cmp(key(a), key(b)) — the common shape of every sort-by, group-by and dedupe-by.
Relations — and the refusal that protects them
Here Axioma diverges from every functional language above. compose does double duty: on sets of pairs it computes relational composition, matching the last component of one against the first of the next. A function is a special case of a relation, so covering both is principled rather than an overload of convenience.
Relational R ∘ S genuinely has two incompatible conventions in the published literature. A language that silently picked one would be wrong to half its readers with no way for them to notice. So the ambiguous spelling is rejected and the unambiguous one is named — and the error explains the convention it commits to. Mixing the two families in one call is a category error, not a coercion: composing a relation with a function has no meaning until someone says which graph is intended.
Pipes are not compositions
Axioma's |> threads a value; >> builds a function. Same reading direction, different product — and the distinction is the whole of what point-free buys you.
The glyph is a deliberate visual echo of the shell pipe, but the mechanism is unrelated. Unix | runs its stages concurrently as separate processes over an unbounded byte stream, with kernel back-pressure and SIGPIPE to kill a producer early. |> is a parser rewrite: strictly sequential, one typed value, fully materialised. In a shell cat huge.log | head -5 reads five lines and stops; huge |> head(5) must produce huge in full first. Same shape on the page, entirely different runtime.
The full written treatment — including the reference tables, the language-by-language comparison, and the known limits — lives in resources/docs/claude/FUNCTION_COMPOSITION.md. The textbook covers the same ground gently in Chapter 10, and every claim on this page is pinned by tests/axioma/showcase/composition.ax.