Adjunctions: What Comes Back
What a round trip preserves
Aa
The text of Cervantes and that of Menard are verbally identical, but the second is almost infinitely richer.
This chapter introduces A9 (Adjunction): a relation between translations stronger than the observation that one round trip loses information. In a specified preorder, an adjoint gives a least or greatest approximation relative to the other map. The general categorical relation is a natural correspondence between morphisms. API versioning and abstract interpretation supply examples under their stated structures; a color taxonomy shows why choosing a representative does not automatically supply an adjunction. Unit and counit describe round-trip maps, not a numerical or economic cost. Vol I, Chapter 5 (The Empire of Tables) examines how a documented transformation can support a useful comparison without reproducing an original answer. The formal model of translation here is self-contained.
The Imperfect Translation
Backward compatibility is a promise. Sometimes the promise is kept imperfectly—and the imperfection is asymmetric. Translation in one direction may preserve everything; translation back may lose something that cannot be recovered.
Consider the common case. A client built for v1 must communicate with a v2 server. The compatibility layer looks straightforward: promote incoming v1 requests to v2 format, demote outgoing v2 responses back to v1. The adapters are short, the tests pass, the release notes say "backward compatible."
promote : v1.Response → v2.Response
demote : v2.Response → v1.Response
Version 1 returns {user_id, name}. Version 2 returns {user_id, name, created_at, metadata}. The promote function adds default values for the new fields. The demote function drops them.
Now run the round-trip test from Chapter 7.
Start with a v1 record. Promote it to v2. Demote it back to v1. Compare the result to the original.
demote(promote(r1)) = r1 ✓
The v1 record survives. Promoting adds defaults; demoting strips them; the original fields return unchanged. So far, so good.
Now run the test in the other direction. Start with a v2 record. Demote it to v1. Promote it back to v2. Compare.
promote(demote(r2)) ≠ r2 ✗
The v2 record does not survive. Demoting loses created_at and metadata. Promoting adds defaults, not the original values. Information that left the server does not return.
The asymmetry is structural. One direction is lossless; the other is lossy. The displayed maps are not inverses. A pair like this can implement a useful compatibility policy. Whether it also satisfies a universal property depends on the ordering of records and the meaning assigned to the defaults.
The question is not "can we make this symmetric?" We cannot, not without inventing information. The question is: what makes this particular asymmetric pair principled? Why is this the "right" way to translate between v1 and v2 when perfect equivalence is unavailable?
An adjunction can answer that question once the maps and their order are specified. The loss of a field alone cannot answer it. We need to know what counts as more information, and which completion is allowed to stand below the others.
Optimal Under Constraints
"Best" is meaningless without a criterion. Best for whom? Best under what constraints? In engineering, "best" often means "we tried several things and this one works." A universal property can make a particular notion of “best” precise by specifying which maps must factor through the proposed solution and how.
Consider the problem of rounding real numbers to integers. There are many ways to do it: truncate toward zero, round to nearest, round up, round down. Each has uses. But ceiling and floor are distinguished by a universal property.
The ceiling of x, written ⌈x⌉, is the smallest integer greater than or equal to x. Not just any integer above x, but the smallest one. Any other integer that bounds x from above must be at least as large as the ceiling. The ceiling is optimal.
The floor of x, written ⌊x⌋, is the largest integer less than or equal to x. Any other integer that bounds x from below must be at most as large as the floor. The floor is optimal from the other direction.
These optimality conditions are not independent. Ceiling and floor are connected by a relationship:
⌈x⌉ ≤ n if and only if x ≤ n (for integers n)
n ≤ ⌊x⌋ if and only if n ≤ x (for integers n)
The ceiling gives the best integer approximation from above; the floor gives the best from below. Each forms a Galois connection with the inclusion of integers into reals. Ceiling and floor are not here a single adjoint pair to one another.
The pattern recurs in translations between different levels of description. The formal name is adjunction, and it captures something precise: when you translate between two levels of structure, a given functor may have an adjoint characterized by a universal condition; when it exists, the adjoint is determined up to the appropriate isomorphism. In the special case of preorders, this structure was first systematically studied by Ore under the name "Galois connexion."1
Daniel Kan introduced adjoint functors in 1958.2 The insight was that many seemingly unrelated constructions share the same shape: free groups, tensor products, limits, colimits, localizations. Each is "optimal" in a specific sense, and the optimality conditions fit a single template. Adjunctions are that template.
The Galois Connection
Preorders let us examine the relation before introducing the full categorical machinery.
A preorder is a set with a reflexive, transitive relation. "Less than or equal to" on numbers. "Is a subtype of" on types. "Refines" on schemas. "Contains more information than" on records.
When both categories are preorders, an adjunction becomes something simpler: a Galois connection. The definition:
Let and be preorders. A Galois connection between them consists of monotone functions:
- (left adjoint)
- (right adjoint)
satisfying, for all and :
The equivalence says: comparing F(a) to b in Q is the same as comparing a to G(b) in P. You can do the comparison in either world; the answer is the same.
This is the "best approximation" property made literal. If F(a) ≤ b, then F(a) is below b in the ordering. If a ≤ G(b), then a is below the image of b pulled back through G. The Galois connection says these conditions are equivalent. In precise terms: F(a) is the least b in Q such that a ≤ G(b), and G(b) is the greatest a in P such that F(a) ≤ b.
Ceiling and floor form two Galois connections with the inclusion of integers into the real numbers. The inclusion of integers into reals sits between them:
- Ceiling ⌈·⌉ is left adjoint to inclusion
- Inclusion ι is left adjoint to floor ⌊·⌋
The conditions ⌈x⌉ ≤ n ⟺ x ≤ n and n ≤ ⌊x⌋ ⟺ n ≤ x are exactly the Galois connection equations.
For the API example, fix the projection that drops the new fields. If each new field has a genuine least-information value, promotion can supply the least record projecting to a given old record. That is the proposed adjunction to test.
For this API example, the order on records expresses the information preserved by the displayed projection. The detailed product order is given below. The promote function sends v1 records to v2 records by adding defaults. The demote function sends v2 records to v1 records by dropping fields.
A critical constraint: the adjunction holds only when "default" means the bottom element in the information ordering—unknown, unspecified, null. If promote inserts a specific value (e.g., created_at = 0), the Galois condition fails. Suppose r2 has created_at = 2024. Then promote(r1) with created_at = 0 is not below r2 in information content; the specific default contradicts the actual data. The adjunction requires that promote adds minimal information, not specific information. A specific default may still be a useful compatibility decision. It must then be justified by that contract, rather than by this bottom-completion adjunction.
The Galois connection condition says: promote(r1) ≤ r2 if and only if r1 ≤ demote(r2). In plain terms, a promoted v1 record is below a v2 record in information content exactly when the original v1 record is below the demoted v2 record. These two order comparisons correspond. Other queries and properties require their own preservation argument.
With the projection and information order fixed, its left adjoint supplies the least completion. In a poset this adjoint is unique; in a preorder its values are equivalent in the order, and in categories uniqueness is up to the canonical natural isomorphism. Changing the projection or the order changes the problem. The adjunction law does not choose both maps from the names of the two schemas.
The Full Adjunction
When categories have non-trivial morphisms beyond mere ordering, the Galois connection generalizes to the full adjunction.3
Let and be categories. An adjunction consists of:
- A functor (left adjoint)
- A functor (right adjoint)
- A natural bijection: for all and ,
The bijection is natural in both A and B.
The Hom-bijection says: maps from F(A) to B in the "rich" category correspond exactly to maps from A to G(B) in the "simple" category. The mapping problem does not disappear; it relocates. A morphism in one category corresponds to a morphism in the other, and the correspondence is natural and bijective at the level of morphisms.
Why does this matter? In many systems adjunctions, F behaves like a "promote" or "free" construction and G behaves like "project" or "forget." The adjunction is the guarantee that these are not arbitrary choices: they are optimal relative to the structure you declared. The Hom-bijection says: to give a map out of F(A) is exactly to give a map out of A after forgetting along G. That equivalence is the universal property that makes the translation principled.
The adjunction comes with two natural transformations that compare the round trips with the original objects:
- Unit η : Id_C → G ∘ F
- Counit ε : F ∘ G → Id_D
The unit η_A : A → G(F(A)) sends A into the image of G applied to F(A). In systems terms: promote then demote. If η is an isomorphism, the round-trip is lossless from the "simple" side.
The counit ε_B : F(G(B)) → B sends the image of F applied to G(B) back to B. In systems terms: demote then promote. If ε is an isomorphism, the round-trip is lossless from the "rich" side.
In the split API example, η is an isomorphism while ε need not be. The simple side survives round-tripping; the rich side loses information. This asymmetry is not a defect. It reflects the structural difference between the categories.
The unit and counit satisfy triangle identities:
The triangle identities say that the displayed composites are the identity at F and G. In the split API example, they fit the section-and-retraction account of its maps. A general adjunction need not have an invertible unit or counit; these laws provide neither an error metric nor a runtime bound.
In the posetal (Galois) case, this coherence becomes literal stabilization: the closure G ∘ F and interior F ∘ G operators are idempotent. Promote, demote, promote again yields the same result as promote once. In systems terms: once you have collapsed information to the v1 shadow, repeating the collapse does not create a new kind of shadow; you get the same approximation.
What the Round Trip Preserves
In the API example, the round trips expose a specific loss: the old fields return unchanged, while the new fields are replaced by their least-information values. A developer can inspect that loss before authorizing a use that depends on the missing values.
The unit and counit locate the comparison. They do not price it. An isomorphism can be expensive to compute, and a lossy projection can be cheap. Nor does a universal property decide whether the chosen order captures what the user needs. It determines the adjoint relative to the map and structure already supplied.
That is substantial work. Once the projection is fixed, a left adjoint is not merely one convenient completion among others. It is characterized by all the comparisons in the stated order. A proposed default that fails those comparisons cannot borrow the adjunction's authority.
The exchange instruments in Vol I's prologue and Chapter 2 bounded demands across currencies and institutions. Their accountability depended on terms, law and recourse, not on an established adjunction. Here an adjunction supplies a distinct mathematical model of translation whose structure and round-trip behavior can be stated precisely.
A promote operation that sets created_at = now() has a time-dependent contract and fails this bottom-completion test. Hashing metadata loses its recoverable contents but still defines a composable function. Both operations can be specified and reasoned about. What they cannot claim without further proof is the particular universal property just established.
Running Examples
API Versioning (Posetal Framing)
We order schemas by refinement: S₁ ⪯ S₂ if there exists a projection p : S₂ → S₁ and a section s : S₁ → S₂ such that p ∘ s = id. This is exactly the v1/v2 situation: every v1 record embeds into v2, and every v2 record projects back to v1. This is the split case. A migration with no supplied section presents a different reconstruction problem; its cost must be assessed from the work needed to solve it.
For this example, order old records discretely: they compare only when equal. Order new records by equal old fields and the product information order on the added fields, each with an explicit bottom value. A new record with bottom in each added field lies below every new record with the same old fields. Ordinary SQL NULL does not supply this order by itself.
For v1 = {user_id, name} and v2 = {user_id, name, created_at, metadata}:
- Promote F : v1 → v2 adds the declared bottom values for new fields
- Demote G : v2 → v1 drops new fields
The Galois condition: F(r1) ≤ r2 ⟺ r1 ≤ G(r2). Promoting r1 is below r2 in information order exactly when r1 is below the demotion of r2. This is verifiable: compare the field values after canonicalization.
The unit η: for a v1 record r1, we have η(r1) = G(F(r1)) = r1. Promote adds defaults; demote strips them; the original returns unchanged. The unit is identity.
The counit ε: for a v2 record r2, we have F(G(r2)) ≠ r2 in general. Demote loses created_at and metadata; promote adds defaults, not originals. The counit is the order comparison from that least completion to r2; no numerical loss is defined.
The triangle identity in action: promote r1, demote to get r1, promote again. You get F(r1), not some further-distorted version. The distortion happens once.
Color Taxonomy (Fine and Coarse)
Catalog A uses fine-grained colors: navy, royal_blue, sky_blue, teal, cerulean.
Catalog B uses coarse categories: blue, green, red.
Order colors by specificity: c1 ≤ c2 if c1 is a special case of c2. Navy ≤ blue. Royal_blue ≤ blue. But navy and royal_blue are incomparable (neither is more specific than the other).
Define F : Coarse → Fine by picking a canonical representative: blue ↦ navy. Define G : Fine → Coarse by collapsing: navy ↦ blue, royal_blue ↦ blue, sky_blue ↦ blue.
The proposed Galois condition fails. Take c_coarse = blue and c_fine = royal_blue. Then blue ≤ G(royal_blue) is true, but F(blue) = navy ≤ royal_blue is false: the two shades are incomparable.
The coarse round trip nevertheless succeeds: G(F(blue)) = blue. One successful round trip was not enough. In the other direction, F(G(royal_blue)) = navy, and there is not even the required order comparison from navy to royal blue.
Collapsing fine shades can still answer a documented coarse query. Choosing navy to reconstruct a lost shade cannot establish that the item was navy. This failed adjunction exposes the difference between a useful classification and a justified reconstruction; it does not make either catalog unusable.
Static Analysis (Abstraction and Concretization)
Abstract interpretation uses a Galois connection to relate concrete program states to abstract domains.4
- Concrete domain: the set of all possible program states
- Abstract domain: a chosen lattice of abstractions; a finite sign lattice can include negative, zero, positive, their allowed joins, and a bottom element
The abstraction function α maps a set of concrete states to the smallest abstract element that covers them all. The concretization function γ maps an abstract element to all concrete states it represents.
The Galois connection: α(C) ⊑ a ⟺ C ⊆ γ(a). A concrete set C is abstracted to something below a if and only if all states in C are covered by γ(a).
This relation supplies a sound abstraction of the given set C: its concretization covers C. An analyzer must also show that its abstract program operations soundly approximate the concrete ones and that its computed invariant covers the reachable states. The Galois connection alone does not discharge those further obligations.
The unit η: C ⊆ γ(α(C)). Concrete states are covered by their abstraction. The approximation is an over-approximation.
The counit ε: α(γ(a)) ⊑ a. In this ordering, the result is no less precise than a; it may remove abstract slack. In a Galois insertion it returns exactly the same abstract element.
The abstraction can make analysis feasible while retaining the information needed for a particular proof. Sound transfer functions and justified fixed-point approximations carry the relation through the program. Their value is that a simpler computation can establish a property of executions it has not enumerated.
What Adjunctions Are Not
The color maps failed a particular universal property. Their useful coarse classification survived that failure. An adapter must be assessed by the behavior it claims to preserve, whether that account uses an adjunction or another proof.
Not all translations have an adjoint. Given an arbitrary function F : C → D, there may be no function G : D → C such that F ⊣ G. Adjointness is a strong condition. A promotion without a principled demotion, or a demotion without a principled promotion, is not part of an adjunction.
Adjunctions do not eliminate loss. They characterize maps relative to the declared structure. The API example has a lossy round trip; some adjunctions are equivalences. A claim of minimum loss requires the specified order or another justified loss measure.
Adjunctions are relative to categorical structure. A different choice of what counts as "structure-preserving" yields different adjunctions. The v1/v2 adjunction depends on treating schemas as preordered by information content. If you change the ordering, you change what "optimal" means.
Adjunctions are "best" within constraints, not absolutely. There is no universal "best translation." A universal property determines an adjoint to a given functor; a pair of categories can support different adjunctions. The universality is local to the structure you have committed to.
The value of recognizing an adjunction is the guarantees it provides. When a translation is adjoint:
- The unit and counit make the round-trip comparisons explicit
- The round-trip behavior is predictable (triangle identities)
- The canonicity is enforced by the universal property
An adapter without this adjunction needs another account of its behavior. A direct correctness proof or a different mathematical structure may supply one. The failure is claiming a guarantee that the particular maps do not satisfy.
Consequence
The color example leaves us with a usable translation and an unavailable reconstruction. A catalog can answer the coarse question without knowing the lost shade. The API example preserves a different relation: its unit and counit specify what each round trip supplies, and the triangle identities prevent repeated passage from adding a further distortion of that kind. These are definite achievements. Neither tells us the price of running an adapter, maintaining it or investigating a disputed record.
The three chapters have supplied different ways to examine a change of representation. An invariant names a property preserved under specified transformations. Inverse maps establish isomorphism of the structures in question. An adjunction supplies a natural correspondence and its round-trip laws. They can be used together without forming a scale from apparent properties to real ones, or from imperfect translations to a priced optimum. A cost comparison requires a cost model.
A recipient can now ask more than whether two outputs look alike. It can ask which map relates them and what survives that passage. But the map still has a domain, its evidence has grounds, and the next operation may need a property the map was never shown to preserve. Chapter 9 makes those conditions part of the object that accompanies an identification. A useful translation should travel with its justification, not with every permission its recipient might wish to infer from it.